#udon-networking

1 messages · Page 26 of 1

frank fjord
#

or if the owner disconnects and the server "automatically" reassigns ownership in whatever way it thinks to do so

minor gale
#

i wouldn't personally design around the suspended state, but i don't make game worlds

twin portal
#

I would guess that, if a player becomes suspended, you could safely transfer ownership to the Instance Master. This is under my assumption that the Master would not be a suspended player, and Master status is transferred if they become suspended

frank fjord
#

not a dig at you, just my own comfort level with the 'certainty' of these systems

twin portal
#

no I understand
my networking strategy is to drive the car until it explodes

minor gale
#

master is not actually transferred from a suspended player to my knowledge, i believe they did attempt it, but master swaps are actually less preferable for a lot of world logic

north thistle
#

Don't know how revelent that documentation is is anymore considering its age

frank fjord
#

Well it's more up-to-date than the original Udon# docs...

#

I mean, you still have a point, but ALSO that

north thistle
#

recent Udon dcoumentation

frank fjord
#

'cuz there are like... what, 3? 4? maybe more? different domains that have creator docs now

#

and I'm losing track lol

north thistle
#

Well right now that would be the Network Events page, lol

And yes, I do realize how not useful that is

frank fjord
#

just so I know for sure what we're talking about

twin portal
#

they tend to put the most updated docs on the Beta pages

north thistle
frank fjord
#

Okay yeah so the Creator docs

#

though I realize different pages have different... ages

north thistle
#

and the beta docs can have out-of-date info iirc

hoary frost
#

yep

minor gale
#

i think to add to this, players can probably work out that someone isn't responsive if their turn comes around and the game halts. i think that's more of a player moderation situation if someone going suspended is impacting the game; i guess ideally your logic wouldn't break if they happened to go in and out of that state for a bit. it probably depends on what you're actually doing

adding like an indicator above someone that they're unresponsive/afk/whatever would be the furthest i'd go personally without a very specific case for suspended state

like accounting for it would involve some other player(s) deciding that they're suspended and accounting for it in logic; determining who makes that judgement when it could apply to any player is iffy. the local view on suspended state is also behind by latency and packet loss that the local player could be experiencing. it'd be a different story if we could run serverside logic

north thistle
#

For my own game world, I imagine I'd trigger my "on player leave" code whenever a player became suspended and have my owner master system in case the master becomes suspended

twilit pewter
#

What am I doing wrong? I can't see any Info or Warning console entries.

#

Toggling any at the top row on or off doesn't actually change anything.

#

Only the Error toggle is actually changing what is displayed.

twin portal
#

check in your VRChat settings, there's an option for what kind of logs you want, including one for Error Only

twilit pewter
snow vine
#

I have a problem, I have a udonsharp script with synced variables on a kinematic pickup with Object Sync and as soon as the pickup is sleeping the variables just stop syncing completely.
If I grab the pickup they start syncing again until it is dropped and enter sleep

frozen igloo
# snow vine I have a problem, I have a udonsharp script with synced variables on a kinematic...

If you put an udonbehaviour with synced variables on the same object as an objectsync, it will be forced to follow the objectsync behavior, which is to go to sleep when it stops moving. If you want variables that can be updated independent of the object moving, you should put them on a separate gameobject, like a direct child. Then they can have their own independent sync behavior

snow vine
#

maybe this behaviour should be documented on the Networked Variables doc page

mild robin
#

I'm not sure what I did do wrong with the setup. I made a toggle to turn on and off the classroom for my world. I made a setup for the late joiners, but it seems that it's an issue cuz when the classroom is toggled on and a later joiner joins, it would be turned off by itself, and I would have to turn it back on.

twin portal
#

you need to use a synced variable in order for it to sync for late joiners

#

instead of checking for the active state of the game object (which won't be synced), have the sender set a boolean variable
then use that bool to track if things are on or off

mild robin
#

Ah, how would I set that up?

twin portal
#

create a new variable of type Boolean; check the box that says synced

#

then I think it's hold alt? while dragging the variable onto the graph, you want to create a Change node

mild robin
#

Hmm, ok, let me try

#

Got it

#

now where do I connect it to?

twin portal
#

that's not the change node

#

as you're dragging it, it says the key to press, I forget

mild robin
#

oh wait, i didn't notice the second message

#

top is with CTRL
Bottom is ALT

twin portal
#

the top is what you want

mild robin
#

ok

twin portal
#

instead of your "True" and "False" events, use the bool newValue there, plug that into a GameObject.SetActive node along with your game object variable

mild robin
#

like this?

#

I can't remove that custom event cuz it's what makes the toggle

twin portal
#

remove the extra stuff

#

you won't need it

mild robin
#

Hmm ok

#

now what?

twin portal
#

now instead of "Toggle" sending a custom event, just have it set the value of LateJoiner
pretty much LateJoiner's current value (normal variable node) -> UnaryNegation -> Set LateJoiner

#

when you Set LateJoiner, make sure "Send Change" is checked

#

then after Set LateJoiner, RequestSerialization

mild robin
#

like this?

twin portal
#

naur

#

keep your IsMaster check, forgot to mention

#

so put Get IsMaster back into the branch

#

this node you made earlier, that's LateJoiner's current value

#

plug that into UnaryNegation instead

mild robin
#

oh, got it.

#

wait, this?

mild robin
#

Ah, it looks like it's fixed. Thank you

twin portal
#

that's it

mild robin
#

Yep, tested it out

#

Thank you for your help. Appreciate it, man.

twin portal
#

oh almost forgot, Master is not the same as Owner. and you have to be the Owner in order to set synced variables and run RequestSerialization

#

so to prevent it from breaking, after the Branch I'd suggest doing SetOwner, which you can just plug in Get IsMaster I guess

mild robin
#

Although it would be nice to have like a list of people that can have access to the toggle. Like the VIP room.

twin portal
#

no the Owner isn't just you, the Owner is the player that "owns" the networking of the script

#

the Master is the player that is the default Owner for all scripts

#

but you could have a situation where a player is the Owner of script, but they aren't the Master

#

or vice versa, where a problem would occur

#

if a player is the Master, but has ended up not being the owner of the script, then the button will no longer work

#

you just have to set them as the Owner right before you set the variables in order to prevent this issue

mild robin
#

Ah ok.

mild robin
#

Like this? @twin portal

#

hmm, the toggle isn't working now lol

twin portal
mild robin
#

ah ok

#

got it, thank you again

sturdy totem
#

How secure is string/image loading against MitM attacks with https urls and an altered root certificate store?

fringe agate
#

How would you add persistence through UDON Graph for toggles? Like, something simple as this?

fringe agate
#

Or is persistence purely though Udon# (Which I have 0 knowledge on, I don't code)

tulip sphinx
fringe agate
#

Didn't know there was a channel for it, it was hidden, thanks~!

twin portal
frank fjord
#

Would someone remind me what the behavior of udonsynced arrays is? Struggling to find specific documentation about them in the creator docs. In particular, what happens if you resize the array during runtime, both up and down in size?

tulip sphinx
#

all the array gets sent out during deserialization, why would it matter if its got resized

frank fjord
#

I dunno! But I've been gotten by things like this before, and I have this faint memory from working on a project last year that arrays are a little funky with, say, their serialization size etc

frozen igloo
# frank fjord Would someone remind me what the behavior of udonsynced arrays is? Struggling to...

Arrays cannot be resized, but you can replace the array with a different one, with a different size. When a synced array is changed to a different size, then remote users will deserialize that into a new array with the new size. If the array size does not change, it just deserializes into the existing array already set up. This is what makes OnVariableChanged weird with arrays, because if the owner replaces it with a new array that is the same size, they'll get OnVariableChanged but remote users won't, because they're not aware of anything changing

frank fjord
#

Sorry, yes, poor semantics on my part

#

I meant reinitialized/replaced with an array of another size

frozen igloo
#

shouldn't need to worry about it unless you're listening to onvariablechanged on a synced array or taking the synced array and then storing it elsewhere locally. Which is why I'd generally recommend avoiding both of those

frank fjord
#

Gotcha. Does the serialization size of an array decrease if it's overwritten with a new array of a smaller size? Or does the serialization size get 'locked' at that array's highest used size?

frozen igloo
#

It decreases in size, yes. Each serialization is constructed independently, it does not care what was serialized before

frank fjord
#

Alright, cool. Thanks for the clarity ✨

north thistle
#

Also, and I'm assuming this is still the case, arrays have an additional sync cost on top of the sum of the individual elements of the array.

frozen igloo
#

part of it is the cost of serializing length and variable type, part of it is the byte alignment which is done for speed

north thistle
frozen igloo
#

ehh, maybe. You'd have to do your own research on that

sturdy totem
#

Can I expect a VRCPlayerApi to work over the network? (like as a parameter in an event or in a synced variable)
Or do I need to convert it to the playerId and back?

twin portal
#

a VRCPlayerApi is not a syncable type
so yes, you'll want to sync the playerID instead

sturdy totem
#

How do you sync a 2d or 1d array of integers via variables?

twin portal
#

mostly the same way you'd do it with a normal variable

#

any syncable type can also be an array of that type and be synced

sturdy totem
#

Both:

  • VRC.SDK3.Data.DataList[]
  • VRC.SDK3.Data.DataList

are not syncable

#

And with arrays I'd have to recreate them each time, which would waste network bandwidth

twin portal
#

Datalists on their own are not, but they can kind of be synced, just takes some tricks

#

the docs have an example for it

#

you serialize it to a JSON string, sync that string, and then deserialize it back into the DataList once the data is received

sturdy totem
#

I guess I am better of wrangling arrays then.

twin portal
#

if it's a DataList of only integers then you should be fine just doing that

sturdy totem
twin portal
#

believe you can only do single-dimension arrays for syncing

sturdy totem
#

I hate it here. Time to employ hacky workarounds again.

north thistle
#

OnPreSerialization/OnDeSerialization does allow you to write your own serializer, so if you're trying to to something more specialized than what is provided by default you can try that

#

building a serializer for an array of an array of a specific known type doesn't seem too complicated

#

But if you need something more dynamic... JSON exists for a reason

honest yarrow
#

Sorry probably a very rookie question, but i cant seem to find anything online abt it. Is there a way to call functions in other classes with SendCustomNetworkEvent?

Im currently trying to do it like this
SendCustomNetworkEvent(NetworkEventTarget.All, nameof(inputSubmitHandler.ApplyPrompts)); where inputSubmitHandler is a type of its own class and a gameobject with said script attached to it

frank fjord
#

I don't believe so. I've always had Behavior A called a middleman function on Behavior B that then sends the networked event call to another function on Behavior B

#

Someone correct me if I'm dumb :3

minor gale
#

it'd be inputSubmitHandler.SendCustomNetworkEvent

honest yarrow
#

I see I see thanks!

frank fjord
#

When you serialize variables over the network, does OnDeserialization run before, or after, udonSynced variables run their field change callbacks? And is this order guaranteed?

frozen igloo
frank fjord
#

Great, thanks ❤️

frank fjord
#

So I think someone the other day was talking about having an unusual number of network-ID'd objects. I'm just realizing as I look at my VRC scene descriptor that I too have a wildly bloated number of network IDs. Manually checking some of these, this list includes UdonBehaviors that are explicitly set to Sync Mode None (both in the dropdown, and in code).

... Is this to be expected? Do ALL udonbehaviors go into the network ID list? Or is something fishy going on

timber ferry
#

from what happened with them, it seems all behaviours regardless of sync mode show up with a network ID in the network ID utility, but the actual amount of networked objects in-game decreased significantly when they set non-synced udonbehaviours to sync mode none

cyan hollow
#

I gave up using VRCObjectPool because VRChat acts up when teleporting an object that was only just enabled.
However, even when making my own pool system that doesn't enable/disable the object, it still acts up. It doesn't properly teleport if the player isn't the owner initially. Even having it wait a frame doesn't fix it.
What am I doing wrong?

/*
 * Copyright (c) CompuGenius Programs. All Rights Reserved.
 * https://www.cgprograms.com
 */

using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;
using VRC.SDKBase;

namespace CGPrograms
{
    [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
    public class DrinkSpawnManager : UdonSharpBehaviour
    {
        #region Variables

        private GameObject[] pooledObjects;

        [UdonSynced] private int poolIndex;
        private int poolSize;

        #endregion

        #region Unity Methods

        private void Start()
        {
            pooledObjects = new GameObject[transform.childCount];
            for (var i = 0; i < transform.childCount; i++)
            {
                pooledObjects[i] = transform.GetChild(i).gameObject;
            }

            poolSize = pooledObjects.Length;
        }

        #endregion

        #region Custom Methods

        public void ChangeDrinkName(string drinkName)
        {
            for (var i = 0; i < poolSize; i++)
            {
                var drinkManager = pooledObjects[i].GetComponent<DrinkManager>();
                drinkManager.ChangeName(drinkName);
            }
        }

        public void SpawnDrink(Transform spawnPoint)
        {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);

            var attempts = 0;
            while (attempts < poolSize)
            {
                var obj = pooledObjects[poolIndex];
                poolIndex = (poolIndex + 1) % poolSize;
                attempts++;

                var pickup = obj.transform.GetChild(0).GetComponent<VRCPickup>();
                if (pickup.IsHeld || obj.GetComponent<DrinkManager>().Purchased) continue;

                Networking.SetOwner(Networking.LocalPlayer, obj);
                ForceRelocate(obj.transform.GetChild(0).gameObject, spawnPoint);

                RequestSerialization();
                return;
            }
        }


        public void ReturnDrink(GameObject drink)
        {
            Networking.SetOwner(Networking.LocalPlayer, drink);
            ForceRelocate(drink.transform.GetChild(0).gameObject);
        }

        private void ForceRelocate(GameObject target, Transform teleportPoint = null)
        {
            Networking.SetOwner(Networking.LocalPlayer, target);

            var sync = target.GetComponent<VRCObjectSync>();
            sync.FlagDiscontinuity();

            if (teleportPoint)
            {
                target.transform.position = teleportPoint.position;
                target.transform.rotation = teleportPoint.rotation;
            }
            else
            {
                sync.Respawn();
            }
        }

        #endregion
    }
}
cyan hollow
#

This is my next attempt:

private void ForceRelocate(GameObject target, Transform teleportPoint = null)
{
    Networking.SetOwner(Networking.LocalPlayer, target);

    deferredObject = target;
    deferredSpawnPoint = teleportPoint;

    if (Networking.IsOwner(deferredObject))
    {
        DeferredTeleport();
    }
    else
    {
        SendCustomEventDelayedFrames(nameof(DeferredTeleport), 1);
    }
}

public void DeferredTeleport()
{
    if (!Networking.IsOwner(deferredObject))
    {
        Networking.SetOwner(Networking.LocalPlayer, deferredObject);
        SendCustomEventDelayedFrames(nameof(DeferredTeleport), 1);
        return;
    }

    var sync = deferredObject.GetComponent<VRCObjectSync>();
    sync.FlagDiscontinuity();

    if (deferredSpawnPoint)
    {
        deferredObject.transform.position = deferredSpawnPoint.position;
        deferredObject.transform.rotation = deferredSpawnPoint.rotation;
    }
    else
    {
        sync.Respawn();
    }
}
minor gale
#

if you're comfortable making your own sync in manual it'd avoid what is probably object sync timing issues, a frame delay is kinda arbitrary

i'm not sure you really need any synced vars on the pool itself, like if you iterate over the drinks to see if they've been purchased or are held, you could just do that without syncing the latest index

#

or even handle the purchased state on the pool as a synced bool array instead of peeking the objects

#

i wasn't able to recreate what you described actually, it works as i'd expect with a teleport call on someone that doesn't own the item at the time (which is similar to flagging discontinuity and setting the pos), though i don't have the exact same setup in terms of hierarchy or components i imagine

Networking.SetOwner(Networking.LocalPlayer, objSyncItem.gameObject);
objSyncItem.TeleportTo(teleportPoint);
cyan hollow
#

I gave up using VRCObjectPool because VRChat acts up when teleporting an object that was only just enabled.
I came up with the following which works to spawn objects, yet when calling ReturnDrink, the object doesn't get send back to the proper location. What's weird is that the LocalPlayer is definitely the owner because they only just spawned the drink to begin with, yet if there is only one person in the instance, it does work.

/*
 * Copyright (c) CompuGenius Programs. All Rights Reserved.
 * https://www.cgprograms.com
 */

using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;
using VRC.SDKBase;

namespace CGPrograms
{
    [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
    public class DrinkSpawnManager : UdonSharpBehaviour
    {
        #region Variables

        private GameObject[] pooledObjects;

        [UdonSynced] private int poolIndex;
        private int poolSize;

        private GameObject deferredObject;
        private Transform deferredSpawnPoint;

        #endregion

        #region Unity Methods

        private void Start()
        {
            pooledObjects = new GameObject[transform.childCount];
            for (var i = 0; i < transform.childCount; i++)
            {
                pooledObjects[i] = transform.GetChild(i).gameObject;
            }

            poolSize = pooledObjects.Length;
        }

        #endregion

        #region Custom Methods

        public void ChangeDrinkName(string drinkName)
        {
            for (var i = 0; i < poolSize; i++)
            {
                var drinkManager = pooledObjects[i].GetComponent<DrinkManager>();
                drinkManager.ChangeName(drinkName);
            }
        }

        public void SpawnDrink(Transform spawnPoint)
        {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);

            var attempts = 0;
            while (attempts < poolSize)
            {
                var obj = pooledObjects[poolIndex];
                poolIndex = (poolIndex + 1) % poolSize;
                attempts++;

                var pickup = obj.transform.GetChild(0).GetComponent<VRCPickup>();
                if (pickup.IsHeld || obj.GetComponent<DrinkManager>().Purchased) continue;

                ForceRelocate(obj.transform.GetChild(0).gameObject, spawnPoint);

                RequestSerialization();
                return;
            }
        }


        public void ReturnDrink(GameObject drink)
        {
            ForceRelocate(drink.transform.GetChild(0).gameObject, transform.parent);
        }

        private void ForceRelocate(GameObject target, Transform teleportPoint)
        {
            Networking.SetOwner(Networking.LocalPlayer, target);

            deferredObject = target;
            deferredSpawnPoint = teleportPoint;

            SendCustomEventDelayedFrames(nameof(DeferredTeleport), 1);
        }

        public void DeferredTeleport()
        {
            if (!Networking.IsOwner(deferredObject))
            {
                Networking.SetOwner(Networking.LocalPlayer, deferredObject);
                SendCustomEventDelayedFrames(nameof(DeferredTeleport), 1);
                return;
            }

            var sync = deferredObject.GetComponent<VRCObjectSync>();
            sync.FlagDiscontinuity();

            deferredObject.transform.position = deferredSpawnPoint.position;
            deferredObject.transform.rotation = deferredSpawnPoint.rotation;
        }

        #endregion
    }
}
pickup.Drop();
drinkSpawnManager.ReturnDrink(gameObject);
north thistle
#

I had similar issues when I was transferring a lot of ownerships all at once. Ended up creating a couple second delay before trying to move stuff.

#

But that involved the world master transferring all the ownerships around and then telling the new owners where the move the objects, so maybe not entierly applicable

finite sierra
#

You kinda always need to have a small delay after a transfer. And if you do alot you need it even more so.

north thistle
#

It really depends

hoary frost
cold laurel
glass lintel
#

Okay, quick question because now that I'm working with this sort of stuff, its something I need to handle

  1. How do I send data to other players? eg. a player needs to send some ints to the room master
  2. I've only taken a quick look at the docs, so, is there a doc, guide, or FAQ area I can go to so i dont need to spam this chat with simple or already-answered questions
#

currently trying to find something thats in the discord but, discord search is bad

#

From what I'm seeing, people are saying:

  • Set ownership of object to yourself
  • Set a variable, let it sync
  • Send networked event

This seems incredibly inefficient, no?

north thistle
north thistle
#

Note that network events have the ability to also send parameters

glass lintel
#

then thats exactly what I'm looking for, networked events

#

docs time

#

wh
this looks so easy to do, why is this not like, the first result?

north thistle
#

it use to be that network events didn't support parameters so to replicate it you had to use a janky implementation of a variable synched object

glass lintel
#

I see

#

thanks search engines

#

Well, noted, at the first sign of not knowing how to do something, I'll refer to the docs, then search/discord, then AI

north thistle
#

Also people often misunderstand how to use the networking and mistakenly think you need to send an event alongside the variable sync

#

(Udon automatically triggers an event on scripts when they receive new variables from a sync so you can use that instead to tell when new variables arrive)

glass lintel
#

i get it now
SendCustomNetworkEvent((IUdonEventReceiver)this,"RequestSpawnBody",forPlayer.playerId, killedBy.playerId, (int)deathReason);

#

oh but i need to define who i send it to dont I

#

time to steal the gameObject owner rq

#

included NetworkEventTarget.Owner :D

#

that was easy

#

okay, im still not entirely right, but im getting there lmao

#

more research time

#

got it working :D

finite sierra
glass lintel
#

I know RR has the "Networking heat" limit which is what I'm used to, its 2kb/s

finite sierra
#

how many you can send at a time. also it may split up the network call if you send to much data at a time.

glass lintel
#

yeah thats nornal, data cap and RPC cap

finite sierra
#

its abit down on here but it tells you how much

glass lintel
#

good news is i dont really have a "CPU limit" to deal with, or a chip limit, so I'm fine converting to a simpler type and back

finite sierra
#

but just to quickly give u that info its Maximum configurable rate: 100 events per second

#

globally.

glass lintel
#

Can network events only send primitives like int/string/float etc, or can they send custom objects/classes?

finite sierra
#

no only primitives. so int,float etc

glass lintel
#

fire

#

i can deal with that

finite sierra
#

also note this If you send an event containing more than 1024 bytes (1 kilobyte) of parameter data, it will be split into multiple events internally. This process is almost transparent to Udon, as the receiving size will re-assemble the event and call it only once on the targeted UdonBehaviour.

glass lintel
#

worst case i have strings

finite sierra
#

so make sure every event is under 1024 bytes. otherwise it will cause two

north thistle
#
[UdonSynced]
private string _json;
private DataList _list;

public override void OnPreSerialization()
{
    if (VRCJson.TrySerializeToJson(_list, JsonExportType.Minify, out DataToken result))
    {
        _json = result.String;
    }
    else
    {
        Debug.LogError(result.ToString());
    }
}

public override void OnDeserialization()
{
    if(VRCJson.TryDeserializeFromJson(_json, out DataToken result))
    {
        _list = result.DataList;
    }
    else
    {
        Debug.LogError(result.ToString());
    }
}
amber sluice
#

Sometimes I wish we could unreliably sync stuff over the network...

tulip sphinx
#

isnt that the whole point of events? or am i missing some irony?

amber sluice
#

Events are still send reliably

glass lintel
#

Quick question, can I send transforms through events, and if not, can I send Vector3's through events?

north thistle
north thistle
glass lintel
north thistle
#

transforms are references and cannot be synced

glass lintel
#

fire

north thistle
#

You could do a Vector3 or give your transforms an id and reference it by that

glass lintel
#

Vector3/Quat are all i need

#

I'm also probably not doing the most effcient method? What I'm trying to do is have the owner spawn an object and place it at the position/rotation I choose

north thistle
#

do not that the second method would only sync the reference, not the local data that transform holds

glass lintel
#

oh the idea of using a transform would just be to send position/rotation in the same type, I dont need an actual transform

#

pos/rot are just fine

north thistle
#

mmhm

safe path
#

Is there any reason why an udon synced variable is not syncing for me. My synchronization is set to manual.

twin portal
#

are you successfully firing RequestSerialization?

safe path
#

One day, one day I’ll understand this

tulip sphinx
#

become owner - change stuff - request serialization - do reaction on stuff.
on deserialization (for everyone else) - do reaction on stuff.

twin portal
#

ha no worries
with Manual sync you have to explicitly tell it "ok sync this data now", and RequestSerialization is how you tell it to do so

safe path
#

Ok back to some semi rage induced coding

#

Thank you, kind people

safe path
#

public string GenerateSeed()
{
int count = Rooms.Count;
Debug.Log(count);
string str;

for (int i = 0; i < Rooms.Count; i = i + 1)
{

    int num = UnityEngine.Random.Range(0, count);
    str = num.ToString();

    seed = (seed + str);
    count = (count - 1);

}

Debug.Log(seed);
RequestSerialization();
return seed;

}

#

Is there any reason the seed variable is not the same for another client or am i doing this right but have a problem somewhere else. Seed is a synced variable

twin portal
#

you need to ensure the Owner is running this function

safe path
#

Ok i think i can do that

twin portal
#

only the Owner of a script can write to synced variables and call RequestSerialization

#

you can have a debug in OnPostSerialization to confirm if the sender was able to send the serialization. Then all other players will run OnDeserialization once they receive the synced data

safe path
#

So what should i do with OnDeserialization

twin portal
#

you should have a function that applies the new synced data; then set up both OnPostSerializaation and OnDeserialization to then call that function

#

you need both because OnDeserialization does not run for the sender

#

so say you get the seed variable synced. OnDeserialization will fire when the client says "ok I just downloaded the synced variables from the sender, and they are now ready to use"
In this case you'd then initialize the randomizer with the seed. lemme find the function for it

safe path
#

I wrote some code for that.

#

ill get it

#

Im sorry in advance
public void RoomSelect()
{

if (GotList == false)
{
    PopulateDestinations();
    //First = Elvtogg.Getchosenbegining();
    pot = Destinations.Count;
    GotList = true;
}



GameObject Room = Anchor;

if (growth < pot)
{
    char sprout = seed[growth];
    string sapling = sprout.ToString();
    int tree = int.Parse(sapling);

    var num = UnityEngine.Random.Range(0, Destinations.Count);

    if (Destinations.TryGetValue(num, TokenType.Reference, out DataToken val))
    {
        if (RunOne == true)
        {
            //Former = First;
            Room = (GameObject)val.Reference;
            Latter = Room;
            DataToken TokenRoom = Room;
            Destinations.Remove(TokenRoom);
            Latter.transform.position = Anchor.transform.position;
        }
        else
        {
            Former = Latter;
            Room = (GameObject)val.Reference;
            Latter = Room;
            DataToken TokenRoom = Room;
            Destinations.Remove(TokenRoom);

            Transform Childtransform = Former.transform.Find("END");
            GameObject Child = Childtransform.gameObject;
            Latter.transform.position = Child.transform.position;
            Latter.transform.rotation = Child.transform.rotation;
        }
    }
    else
    {
        Debug.Log("I scewed up");
    }
}


if (growth < pot)
{
    growth = (growth + 1);
}

RunOne = false;

}

twin portal
#

you can surround your code in triple backticks ` to format it as code

safe path
#

ok i will do that instead of flooding the chat next time

twin portal
#

dw about it we aren't running out of chat real estate space

safe path
#

this function works for one player by being called once to generate the next room from the seed the only problem for me is i cant get the seed to be the same for everyone

twin portal
#

I don't think I'm seeing you set the seed though

#

the seed is an int btw, not a string

safe path
#

yeah i save it as a string and get the first chacter and turn that into an int, its prbably not the best way

#
{
    seed = Elvtogg.ReturnSeed();
} ```
#

i set the seed here

twin portal
#

but isn't seed the synced variable?

safe path
#

im definently using things wrong

#

it it a problem that seed is in a differnet scrpt?

twin portal
#

if you're calling RequestSerialization in another script, you need OnDeserialization in that script too

#

if it's somewhere else, in the majority of cases another script won't be serialized if it was a different script that called it

#

I think it's only when the scripts are all on the exact same GameObject that they get serialized together

safe path
#

Alright i might just but all my stuff in one gameobject

twin portal
#

might be worth practicing with a simple script that all it does is just sync a variable between players

#

then progress that out to setting the seed, then generating a sequence of random numbers, and seeing if they are the same

#

then you could take those concepts and apply it back to your main project

safe path
#

Yeah thats probably a better idea than trying to fix the abomination ive made without knowing how this works

twin portal
#

haha yeah, I'd recommend taking that approach anytime you're working with something unfamiliar

tulip sphinx
#

what i did was to gen one main seed only by master (ie synced var but we never try to take its ownership, so only id=1 would generate it) then locallly generate seeded int randoms[100] based on it, then use each of those for other scripts, like if it was a room generator and 1st room had 4 versions then itd be randoms[0]%4 to pick a version

twin portal
#

get something small working first, then piece it together into what you need

safe path
#

IT WORKS!!! Oh my gosh IT WORKS

twin portal
#

hell yeeeaaaahhh

safe path
#

I was using the wrong variable

#

im laughing and crying at the same time now

#

LEGOS, TheLeastMost peole like you are why this is still worth it

wooden tulip
#

is anyone else having a hard time writing a code to summon players to you

twin portal
#

can see how that could be tricky, since only the local player can teleport themselves

#

you'd need to send an event to everyone to teleport to a position

silver hill
#

Getting some odd error that ive never ran into before, in client sim it works as designed, but in game it gives off an error saying that its sync type is none, which is untrue, Ive checked every Udon behavior to check if they are all manual or continuous.

north thistle
#

hopefully it's not network ids bugging out

silver hill
#

I am testing it now I have a feeling its because I set the projectile original objects to have VRC object sync

#

Nope that didnt work, same issue

north thistle
#

are you trying to use SendCustomNetworkEvent on or from an instantiated gameobject?

#

instantiated object do not support networking

honest yarrow
#

Hello! Im really trying to figure out why the "vote1" to "vote3" variables wont network sync. This seems to be following examples, and i also have another part of code that is similar, but without the switch case, which works. Ive also tried to not have the if statements without success

the vote1-3 variables are ints who are public and UdonSynced, the sync mode is on manual, the total sync is only around 600bytes and it works if no network trafic has to happened

public void HandlePlayerButtons(int vote, int player) //is only executed locally for the one submitting
    {

        if (!Networking.IsOwner(gameObject))
        {
            Networking.SetOwner(Networking.LocalPlayer, gameObject);
        }
        switch (player)
        {
            case 0:
                vote1 = vote;
                break;
            case 1:
                vote2 = vote;
                break;
            case 2:
                vote3 = vote;
                break;

        }
        if (Networking.IsOwner(gameObject))
        {
            RequestSerialization();
        }
        else
        {
            inputSubmitHandler.testt.text = "ownership error"; //this never triggers
        }
    }```

i also know i can vote1-3 as an array and convert it to JSON to sync, but i tried to simplify my code
frozen igloo
honest yarrow
#

No i do not, I do have a OnPostSerialziation which triggers and only lately used for testing

frozen igloo
#

It's probably syncing correctly, you're just not doing anything with it on your end. You need to add OnDeserialization as that's the event that gets called when remote users receive the data that you just sent

#

If you have some UI elements to enable depending on the state of votes, you would do that in ondeserialization

honest yarrow
#

i do some logic that should execute for all other players when taking this information in, i could do it there which would be smart

#

but im wondering, if i run RequestSerialization(), and later have a timer for a few sec just to make sure the network data should sync for all users, shouldnt i just be able to use those values for any function, even if that wasnt triggered by OnDeserialization?

minor gale
#

waiting x amount of time doesn't ensure they actually have the variables, you can direct your logic through a common handler method on the owner and remote if that helps

frozen igloo
# honest yarrow but im wondering, if i run RequestSerialization(), and later have a timer for a ...
  • Code executes independently on all machines, so the owner running a timer doesn't make everybody else run the same timer unless you explicitly sync it
  • OnDeserialization is the event that tells you when data has been received. If you wait a fixed amount of time and just hope that the data has been received by the time the timer is up, that will only work under ideal conditions and any delays in network sync would break it
  • If you're not applying synced variables as a result of OnDeserialization or OnVariableChanged, it's not correct. Whatever it is that you're using is making some false assumption.
honest yarrow
#

Oh yea i knew it wouldnt ensure it, i was more for testing and learning abt the networking. and i also would have had some code as a backup was my original plan. But onDeserialization seems way more convenient and safer, so thanks for that tip! and maybee have a longer timer or smth in the background so forward with default values if the networked data wouldnt arrive at all

wooden tulip
#

@twin portal could you give me an idea on how i could make a summon system ?

#

i can make it work , but under events it will break randomly

twin portal
#

ideally you'd just send an event to everyone, with a parameter included for the position
then have the player teleport to that position

north thistle
honest yarrow
#

Ah i see, thats good to know!

glass lintel
#

Quick question about rate limiting

If I rate-limit an event to only be able to be sent 1 time per second, does that apply locally, or globally?
Basically, can EVERY player send 1 event per second each, or globally, can the event only be sent 1 per second total

#

oh yeah, bonus question, can I sync enums themselves, or do I need send/sync as int?

twin portal
glass lintel
#

fire

glass lintel
#

damn.

#

fix is easy enough, but damn

twin portal
#

Check the Networking doc page for a full list of available types; you can sync arrays of the available types too

glass lintel
#

oh yeah i guess one more question
By properly limiting access to my classes/methods/variables, does that at all change how easy it is for hackers to trigger things

#

because I already plan on giving things as little access as possible

twin portal
#

I think in a general sense yes
If you have a function that you need to be public, but want to ensure it can't be called over the network, name it starting with an underscore

glass lintel
#

oh, can you not call networked events on methods starting with _?

twin portal
#

That's right

glass lintel
#

fire

twin portal
#

They have plans in the future to make this a little less weird to deal with though

#

In the future (with Soba), by default you won't be able to call functions over the network at all, they have to have the attribute [NetworkCallable] in order to be called over the network

glass lintel
#

oh lol, im already doing that

#

any event I want to be networked, im using [NetworkCallable(maxEventsPerSecond:X)]

twin portal
#

Yea this idea is kinda halfway there at the moment with the new network events that came out relatively recently

#

That's why you have to add that if you're using events with parameters

glass lintel
#

If I want to send an CustomNetworkedEvent to another script, do I have to use "MethodName" and not nameof(MethodName)?

finite sierra
glass lintel
#

if that MUST have parameters, thats a bit disapointing

#

i dont want to be changing up my logic left and right depending on if it has parameters or not

finite sierra
#

It came with the parameter update sooo

#

You just have to remove it from the methods that has no parameter

glass lintel
#

alternatively, i make each event like 1 byte bigger by adding a useless bool

finite sierra
#

Eh. That will make you eat the rate limit fast lol

glass lintel
#

well, i guess ill figure out in testing unless someone can tell me in confidence that it NEEDS parameters

glass lintel
finite sierra
#

You can only do 50 events that has parameters per second globally.

#

Plus there is overhead for them too. Dont remember how much but it wouldnt surprise me if its 12 bytes

glass lintel
#

Well, like I said, im not going to be passing through any parameters unless someone tells me I need to, and even then, I doubt I will hit 50 per second

finite sierra
#

Oh sorry its 100 but its dynamically adjusting the limit. If the docs are to go by

strange token
#

So eventListener.SendCustomNetworkEvent(nameof(eventListener.MethodName))

glass lintel
#

oh sick yeah that works

glass lintel
strange token
glass lintel
#

okay, noted

glass lintel
#

Oh- wait, If I want to send a networked event from A to B, do I do this.SendCustomNetworkEvent, or that.SendCustomNetworkEvent ("that" being the object I'm sending to)

eg:

  • SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(eventHandler.EventTryStartGame), false);
    or
  • eventHandler.SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(eventHandler.EventTryStartGame), false);
#

I'm assuming its this, since you are having this send an event to that.Method

glass lintel
#

Oh yeah, i guess something I want to quickly confirm before something breaks and I don't know why

If I have an empty object, no collider, no way to interact, all it does is run whatever script is on it, is the owner of this object guaranteed to always be the Room Master as long as I do not manually set the owner?

#

because I have a handful of objects like this, and I'd like to assume that on all of the owner of all of them is the same always

cold laurel
#

It's up to you to care or not care about this edge-case.

oblique panther
#

i have issue to the players dont load the song and i cant load url to see video

north thistle
glass lintel
north thistle
#

Of course

north thistle
glass lintel
north thistle
# glass lintel

this script here looks valid; but if you want to use nameof instead of manually writing the string of the method name, you are able to do that

glass lintel
#

but- when using nameof, am I using the method on the object (eg. nameof(eventHandler.MethodName), or the method on the class, in this case nameof(EventHandler.MethodName)

eventHandler is a serialized variable that I have the EventHandler object dragged into, which has the EventHandler script

#

or does it not matter

north thistle
#

as far as I know, the only thing nameof does is covert the name of the method into a string

For example if you had three different class with different methods that all happened to share the name of MyMethod

if in one of those classes you were to call nameof(MyMethod), nameof(MyOtherClass1.MyMethod), and nameof(MyOtherClass2.MyMethod) they would all output the string "MyMethod"

glass lintel
#

okay, noted

#

will fix my events then

north thistle
#

the only thing SendCustomNetworkEvent cares about is that whatever string you provide it matches the string name of the method you are trying to call

glass lintel
#

(its no longer eventHandler.sendblahblahimwakingup, i thought the target would be defined in nameof)

north thistle
#

How you get that string is up to you, manual, nameof, or some other method

glass lintel
#

so yeah, all i gotta do is add the target object back

#

Thank you :D

north thistle
#

np

#

Also for some additional clarification, calling just SendCustomNetworkEvent(etc) within a script will call it on that same script regardless of what string you provide it.

Calling myOtherScriptInstance.SendCustomNetworkEvent(etc) in a script will call it on myOtherScriptInstance regardless of what string you provide it

oak rivet
#

Hi, I want to sync a playerObject's position among all players in the instance, but I need a program source to input into the Udon behavior component, what source should I add if I just want to sync its position, an empty one?

north thistle
oak rivet
#

It happens everytime, even when reloading Unity and my computer

north thistle
#

a quick and dirty way to fix it will be clicking "Regenerate Scene IDs"

However this will most likely change the network id assigned to PlayerObjects and any PlayerObject using persistence that has its network id changed will get all its persistence data wiped.

blazing wing
#

Okay, just want to ask here before doing anything, I have made a lockable door (animations and stuff) with barging and everything already. However, I want to remake it and optimise it further. What would be the best way to make an optimised synced door that can be locked, barged (with a customizable amount of times before unlocking)? Trying to also minimise the amount of syncing/have less data sent-

#

Also, I do have 3 scripts for the door, one for the handle interacting, one for the lock interacting, and the main "DoorManager". Is this a good thing to do, or should I combine it all into one? Originally did this because I wanted the handle/lock to be separated from the highlight. Though there may be a better/simpler way than I originally thought lol

north thistle
#

my own approach would to turn the door into a state machine and sync the state (which could be stored in a single byte)

blazing wing
#

So, in summary, have each door-state be a State in the door's State Machine, then sync it?

#

Meaning, one state for closed, another 2 for left/right open, one for locked, don't need one for barging

north thistle
#

mmhm

blazing wing
#

Gotcha, thanks!

#

Actually, pretty sure I already have the states for the animations

#

Cause rn I'm also trying to ensure that the states are completely synced, to prevent people from opening the door whilst another player is actively locking it

north thistle
#

Also while you can limit state transitions for the the owner who is going to be sending out the sync data, you need to make it so any state can go to another other state on the receiving end; otherwise late joiners can run into issues

cold laurel
#

Yeah, basically move the state machine from the animator to code.

#

And make the code enforce that the animator stays in sync

blazing wing
cold laurel
#

😨

#

You can, but I wouldn't recommend this approach

blazing wing
#

Oop-

#

Too slow, I assume-

#

I did hear how UdonSharp is not as fast as C#

cold laurel
#

Udon is several orders of magnitude slower, yes. But it's not as slow as people think.

#

Performance concerns is not the reason for my recommendation.

blazing wing
#

Fair enough

cold laurel
#

Ideally your animator state tree should be able to transition from anything to anything based on any amount of properties you have.
Then just sync the value of those properties via Udon Manual sync.

blazing wing
#

Also, is there a better way to do something in a script via an Animation Event that does not require using SendCustomEvent() for changing values? Like a way to send an event with parameters/

blazing wing
north thistle
#

SendCustomEvent parameter support when, lol

blazing wing
#

Prepare to throw up, but had to do this as a first attempt lmao

north thistle
#

My state machines are almost exclusively script based, but nothing wrong with using the animator as well. Whatever work best for you.

blazing wing
#

I'll try both, honestly, best to learn along the way ^^

#

See which feels nicer and stuff

north thistle
#

I also make every single manual sync object function via a state machine

cold laurel
# north thistle SendCustomEvent parameter support when, lol

Afaik even if that was implemented it wouldn't work with native UnityEvents such as the ones on ui buttons or animator events as they have a max param count of 1. To call into an UdonBehaviour from C# you need to use SendCustomEvent which already takes one string parameter.

blazing wing
#

Lmao

cold laurel
north thistle
blazing wing
cold laurel
#

yes

north thistle
# blazing wing I'm scared-

It's bad. But if it works it works, and when you have no other choice, something that works is better than something that doesn't work.

blazing wing
cold laurel
north thistle
blazing wing
#

Kill me-

#

Nested Ifs lmaooo

cold laurel
#

Yeah, so, whenever this happens it's usually a sign you should reconsider the architecture of whatever you're building

blazing wing
#

My first attempt was basically "Get it working, then optimise it later"

blazing wing
cold laurel
#

It's normal to code something 2 or 3 times

blazing wing
#

Lemme just quickly uh...... CTRL + A | Backspace

cold laurel
#

I wouldn't do that, unless you're versioning your project with git, which you should be doing.

blazing wing
#

Yeah, currently using Github for project syncing as well

#

This is gonna be fun :p

#

Allows me to also better sync it: that's what "isStateChanging" was for as well, prevent others from doing anything whilst an animation was playing

#

Right, for now ima lock my PC so I can sleep (currently 5AM), then wake up and get a new start on this. Thanks for the input, and will (most definitely) ask for help when I need to!

finite sierra
glass lintel
oak rivet
#

What is the best way to go about deleting a player's playerObject when they leave the game?

vapid pagoda
oak rivet
#

Is this only the case if the playerObject is synced?

vapid pagoda
oak rivet
#

I just have a playerObject cube that isn't synced.

#

It didn't get deleted once that player left.

#

Wait

#

maybe something else went wrong, I'll try again in a bit since it could've actually been deleted.

gentle dove
#

Asking for Sanity check: Is the playerId for VRCPlayerApi the same across all clients connected to an instance?

tulip sphinx
#

yes

gentle dove
#

Also, is there any such id available for networked gameobjects? Like getting the Network Id assigned to an object?

#

Basically looking for a gameobject identifier that is the same across clients for an instance

tulip sphinx
#

i dont think udon has access to network ids, theyre for more internal stuff.

north thistle
#

have to make your own

crude mountain
#

Hi, I'm just starting to use item sync to get multiplayer. Can you recommend a YouTube tutorial to help me understand:
sync video
sync pickup
sync Object position.
Thanks.

fossil kite
#

I've made a networked game that has always worked fine in my testing with myself, even across two devices with two different accounts -- but I just tried it with someone else for the first time, and it would only work for a few seconds before the game would stop updating for me, but continue updating for my friend, until I rejoined the room. I know that's a vague problem, but is this something anyone has run into?

#

the friend is in europe and I'm in north america, so it could be related to lag.... I've considered that it could be a Locale thing, but I'm not using any ToString so I doubt it.....

young thunder
#

Both of you should check the debug logs in-game

#

you want Debug Menu 3

north thistle
#

might have made time sensitive networking code which is generally a big no-no

young thunder
#

500 mile email moment

violet mist
#

Would anyone happen to know what "No Samples" means in Debug Menu 8? I can't find any info about it in the docs

(Context in case it helps: The attached screenshot was taken from the POV of a player client after joining an existing instance with hundreds of VRCObjectSync pickups that were moved previously before they joined, but the joining player only sees the correct synced rotation for each pickup, not the correct position. The player in the screenshot is looking at a pile of 60 of those VRCObjectSync pickups, out of a total group of around 1,300 pickups. The owner of all the objects sees "Should Sleep" instead, while the joining player sees "No Samples" on every pickup until the owner picks one of them up.

I'm trying to figure out if this "No Samples" message will help me diagnose the issue, and even if not, what it means regardless)

tulip sphinx
#

it probably tries to sync too much data initially and dies without receiving any, not very clear what happens when vrc sync exceedes threshold. one player can have like 250, maybe 300 vrc syncs before network is clogged. reduce the amount to like 200 and it should work fine. after that idk, probly make objects to appear in stages.

violet mist
# tulip sphinx it probably tries to sync too much data initially and dies without receiving any...

i'm aware of all of this, yes. when VRChat exceeds the threshold, manually synced scripts try again, continuous scripts do not [and VRCObjectSync is a magic black box that does some secret third thing]. this is outlined in the docs

but I've been told multiple times that VRCObjectSync should be able to handle this use case despite those networking specs in the docs. additionally, if i make a test world with WAY more VRCObjectSync pickups, and with scripts that sync WAY more data than that world in the screenshot, it all works flawlessly no matter how many pickups any given client moves

as a result, i'm inclined to believe that this is something wrong with my world specifically, that's causing some weird VRCObjectSync bug. so i'm trying to find a way to reliably reproduce this issue in a test world so that i can either report it [more than i already have] with more information, OR fix it if it's my fault and within my power to do so

towards that end, i'm wondering what "No Samples" means here, and not necessarily looking for a direct solution to my problem

fossil kite
#

is there a way to tell Udon that an UdonSyncMode.Smooth variable should suddenly teleport to a particular value? like TeleportTo's "lerpOnRemote" being false, but for variables

strange token
violet mist
#

(not implying you're wrong, js it's a weird situation/state)

strange token
#

At the bottom they explain there are 4 possible states

#

But “no samples” isn’t there, so I might suggest making a ticket or seeing if phase chimes in at some point (idk if pinging phase is ok or not)

violet mist
#

i see them mention the states, but i can't find explanations ?

strange token
#

Ima do it lol

#

@frozen igloo any clue why the Status of an object would display “No Samples” in Debug Menu 8 and what that means? There’s nothing in the docs about it

frozen igloo
violet mist
#

v bizarre

frozen igloo
#

don't mix sync types on one object. That sounds like it could be the problem

violet mist
# frozen igloo don't mix sync types on one object. That sounds like it could be the problem

okay so i tried setting the behaviours on the problem pickups to Continuous, and the pickups in my "test world that i can't seem to break" to NoVariableSync (they were also working on "None" before). the problem world seems to work now, but the test world still hasn't broken... my hunch from earlier testing is this issue somehow has something to do with downloading large strings and/or images since, after disabling all downloads in the world, the issue stopped happening. but anyway, yeah, very bizarre that one world is fine with NoVariableSync/None being paired with VRCObjectSync, and another isn't...

but given this proposed solution, new question: won't suddenly setting 1,200 UdonBehaviours to Continuous increase network bandwidth and cause issues? or will Continuous scripts act as NoVariableSync scripts if they don't have any synced variables or networking methods in them?

frozen igloo
# violet mist okay so i tried setting the behaviours on the problem pickups to Continuous, and...

but given this proposed solution, new question: won't suddenly setting 1,200 UdonBehaviours to Continuous increase network bandwidth and cause issues?

When a continuous udonbehaviour is on the same object as objectsync, it will sleep with the objectsync. So if you were somehow not running into bandwidth problems with 1200 objectsync simply because they're not awake often enough, then you're not going to force them awake by having continuous udon on them

#

if the udon needs to be able to sync while the objectsync sleeps, you should do that through a child behaviour that is set to manual

north thistle
hoary frost
#

ive got near 40 pickups on my scene but theyre not all gonna be held at the same time, so id like to know

timber ferry
#

probably in reference to the rigidbody going into sleep mode, since that makes it stop syncing

north thistle
young thunder
#

I want to have a podium that any player can speak from. Only one player can speak from it at a time. Whoever is speaking should have a boosted voice range.

#

I'm thinking I can have a synced variable that identifies who is speaking

#

I can't sync a VRCPlayerApi, but I guess I can just sync VRCPlayerApi.playerId?

minor gale
#

syncing the player id or just inferring who it is from the local perspective of them being in a trigger (which is a bit more resilient to leavers/ownership)

young thunder
#

I could do it locally, yeah – although I want other people to be able to stand nearby without also getting their voices boosted

#

also: is there a value that is guaranteed to never be a valid player ID, or do I need to sync an extra bool to indicate if any player is speaking at all?

minor gale
#

-1 is often used for a null player id

glass lintel
#

every body gangsta until 4,294,967,294 players cycle through the instance and get the id -1

young thunder
#

me when

#

yeah i think i can deal with that

strange token
#

-1 is my “null” for any array reference variables I ever make

tulip sphinx
glass lintel
tulip sphinx
#

well then its 2147483647

minor gale
#

i think cryptic was noting that you'd need to go through the positive range, then most of the negative range to arrive back at -1

lone zealot
#

That is still insanely high

minor gale
#

go from -1 to -1 again by incrementing, the steps needed for that aren't equivalent to int max

north thistle
#

I think player ids start at 1, so 0 will probably never be assigned

You know, in case you want to sync a uint and up your capacity from 2bil joins to 4bil

young thunder
#

i figured they'd be random-looking numbers, like Unity instance IDs

#

but if they're just consecutive integers, then i'm not worried :p

cold laurel
cold laurel
glass lintel
cyan hollow
#

I'm trying to implement a system where one player can transfer money to another. Since I can't modify a remote player's PlayerData, how can I safely do this over the network?
I'm thinking I can't use SendCustomNetworkEvent in this case because that would be susceptible to client users, correct? Or is there a proper way to use it here?

glass lintel
#

If by safely you mean "So hackers cant give someone infinite money"- that'll be a problem as long as there is any exposed way to give yourself or someone else money, nothing would stop someone from giving themselves a bunch and sending it to someone "legitly"

If you mean safely as in "Both parties know 100% the money was transfered"- well, thats the two generals problem

A removes 100 money from themself, tells B to add 100 to themself- what if B never receives the signal? You just lost 100
A sends a signal to B, and B adds 100 to themself, and sends a signal back to A telling them to remove 100 from themself- What if A never receives the signal back? You just duplicated 100

#

The first sentence I'm kinda pulling out of no where- I'm not exactly sure what scripters are capable of accessing- but I'm assuming its any public method/anything exposed to them

cyan hollow
twin portal
#

there's also an additional problem with doing this and saving it persistently

you could have a successful transfer, but then the sender player leaves the game before their persistent data is saved. They return, loads the old save from before they transferred money. Money has been duped again

glass lintel
#

everyone receives it, then its filtered based off playerID

#

there might be a better way, i actually think there is, but thats the only way I personally know

#

I think i remember seeing something on the docs about sending an event to a specific player

cyan hollow
twin portal
#

yeah that's a way to do it

cyan hollow
#

I haven't actually looked at it yet haha

glass lintel
# cyan hollow Using the new variables thing?

SendCustomNetworkedEvent(NetworkEventTarget.All, nameOf(SendMoneyToPlayer), targetPlayer.playerId, moneyAmount)
when you receive, check if the local players playerId is equal to the received one

#

i forget if its NetworkingTarget

#

its probably something else

#

very new to udon

#

like NetworkEventTarget

twin portal
#

it is NetworkEventTarget

glass lintel
#

yeye

twin portal
glass lintel
#

fire I knew there was a better way to do it

north thistle
#

Sorry I think I misunderstood what was being said. To clarify, the sending player's VRCPlayerApi is contained in NetworkCalling.CallingPlayer

#

If you want an event to target a specific player, you can send the event to a PlayerObject owned by the target player, use NetworkCalling.Owner, and then handle it from there

#

Also if you want to add some limited anti-cheat, you can have the player receiving the money modification event determine if the amount received/lost makes sense for the current context

north thistle
#

Also the reason why I say PlayerObject is that afaik the ownership of a PlayerObject will never and can never be transferred and thus you are guaranteed to send the event to no one but the correct player, even in edge cases where for example the target player leaves.

minor gale
#

why not specify the player id?

glass lintel
twin portal
#

yeah it's a viable approach, there are a lot of cases where it's best to have one player centralize some operation

north thistle
glass lintel
#

oh by PlayerEventHandler, i would mean something to handle events for THAT player, so it would be a player object

So, if you want to send an event to a specific player, you'd send it to the owner of the object

minor gale
#

other than saving on a param, i don't really see why you'd send to a player object over some manager object, unless the currency is stored on that player object

north thistle
# minor gale why not specify the player id?

Extra network load with that method. Probably fine for most use cases, but besides the cost of having to sync an additional int, everyone who receives the network event has the incurred network cost of having to responded to the server that they have correctly received the data.

twin portal
#

this case would make the most sense if they are on PlayerObjects yeah

#

if not, sending the ID would be sufficient too

glass lintel
minor gale
#

you can send the player's id as a parameter is the implication here

glass lintel
#

I have an EventHandler (now called CustomEventHandler), you'd have another object for PlayerEventHandler which is a player id

glass lintel
#

its only 4 bytes, but still, its 4 bytes

north thistle
#

Since network events have guaranteed delivery, every receiver of a network event has an additional network cost.

minor gale
#

it's 4 bytes, certainly if you intend to send it extremely often i guess

#

like i think the idea of having less objects in scene via player objects is preferable to saving on 4 bytes, despite both being insignificant. it's also a bit harder to route to a specific player's player object

glass lintel
north thistle
#

It's more than the 4 bytes

minor gale
#

if you're sending an event to a player object it doesn't change the amount of data required for each player to ack

#

everyone still sees the event and responds

glass lintel
north thistle
minor gale
#

oh that's probably true yes

north thistle
#

This cost is probably insignificant for most use cases, but if you're in a network heavy instance, especially one that distributes the network load between players, making networking optimizations like this could be worth it.

glass lintel
#

I'll probably go for that optimization early, since once its setup its setup

#

I'll do it when I need to, so far I dont need to send to anyone but Owner and All

north thistle
#

My own justification for this networking optimization in my own world is that by default I am wanting to near max out the networking load on every player

finite sierra
# north thistle My own justification for this networking optimization in my own world is that by...

Well we can only really so much. With the data limit obviously any and all choices for variable matters. Etc. But even then majority of the cost comes from the very costly overhead. If anything we dont even have upward of 11 kb/s realistically each person might only have 500-1000 bytes / s. All through it heavily depends on how many people. Once you start going into high player counts the limits are really showing.

#

And even then with cont you can only do 200 bytes per serialize. Which is not alot

topaz jay
#

I have a station with "Disable Station Exit" On, as I'm using it for a vehicle.
I tested it with friends and after the first person has used the station it becomes blocked for everyone else.
The weird thing is that when I'm testing it locally with multiple instances (through vrc quick launcher) it works fine

strange token
#

When you get in a station vrc automatically networks to everyone else that you’re in it, which takes the slot for all the others too

topaz jay
#

Do I have to use SendCustomNetworkedEvent to free up the station somehow?

tulip sphinx
#

i think it breaks somewhere and script errors out so stops working for everyone, check out logs. i personally dont like that interact in your case doesnt check anything ie if station is in use already

topaz jay
#

Thank you I might try that
is there a way to see errors while playing online?

timber ferry
gentle dove
#

Things get a bit weird when one client has their VRC Object sync disabled and the other does not

#

The box collider, which should be around the card (as seen by the highlight) has completely detached from the card

tulip sphinx
#

networked objects generally are not to be disabled, unless its managed by a vrc object pool or some other system that makes sure everyone has the same state. disable mesh renderers/colliders etc, not udon/vrcsync

crude mountain
#

Hi, how can I synchronize the text editing of a TEXT TMP to make it visible to all players?

twin portal
#

you can get the text from the TMP input field, put that in a string, and then sync that string with all players
then when other players load that data, put the data from the string back into the TMP field

crude mountain
crude mountain
#

SOLVED !
Hi,
using
SET OWNER
REQUEST SERIALIZATION
I get the synchronization option.
My question is:
How do I remove the Owner to allow another user to be the Owner?

young thunder
#

by default, anyone can take ownership of the object

#

you don't have to do anything special

#

every networked object always has an owner

#

(by default, it's the instance master)

crude mountain
brisk magnet
#

lets assme I have a class called a with an int value called B

and I send a network event with paramater to class C and in the paramater I give it A.B is there a chance it might send an adress to the value instead of the raw int value

young thunder
#

I am not aware of any way that could happen

crude mountain
#

@brisk magnet @young thunder

glass lintel
#

If an object with an animator has VRC Object Sync, will the animator be synced, or do I still need to do that myself?
And, addtionally, even if it DOES sync, should I manual sync anyways?

#

(For context, this is for doors users can open/close)

twin portal
#

no, the animator will not be synced

glass lintel
#

fire, manual sync it is

twin portal
#

VRC Object Sync only syncs position and rotation

north thistle
# glass lintel fire, manual sync it is

also I'd advise putting the manual sync on a different, child object to to vrc object sync object; mixing VRC Object Sync with other networking behaviors, even just event-based ones, seems to result in negative behavior

glass lintel
#

oh im not using a VRC Object Sync anymore

#

i was only gonna use it if it synced the animator

north thistle
#

ahh

glass lintel
#

since it doesnt, its just a script

north thistle
#

I think VRC Object Sync only syncs the transform

glass lintel
#

technically this anim is only rotating and moving the door

#

but

#

just telling everyone what state the door is in is cheaper

north thistle
#

oh, and maybe also some rigibody stuff as well

north thistle
wooden saddle
#

hello can any help pls : so i got holder and it got points that have objects but the off so how would i tell system whin new player join to enable them if the enabled by other player . is problem for new joined players in world . note sync the work fine if the on whin all players in world.

barren scaffold
#

When I want to scripts to interact with one another do I use the sendcustomevent or the sendcustomnetworkevent

glass lintel
# barren scaffold When I want to scripts to interact with one another do I use the sendcustomevent...

Depends on if you are doing it locally or not

If you want the local player, from ObjectA with ScriptA to invoke a method on ObjectB with ScriptB, all you need is a reference to ObjectB's ScriptB from ScriptA

For example:

[SerializeField] private ClassB _classB

public class ClassA : UdonSharpBehaivor
{
  public void InvokeMethodOnObjectB()
  {
  _classB.MethodOnClassB()
  }
}

SerializedField is the easiest way (public variables are serializefields by default), I prefer other ways of obtaining them personally

#

However, if you want OTHER people to receive this event, its:
_classB.SendCustomNetworkedEvent(NetworkEventTarget.[target], nameof(ClassB.MethodOnClassB)

barren scaffold
#

Thank you for the detailed response Cryptic, right now I'm using graphnodes because I'm fairly new to coding but I think I understand what part i'm missing that is prevent the two from talking.

For the udonbehavior.sendcustomnetworkevent it needs an instance so it knows where its sending it correct?

glass lintel
#

Yes

#

object.SendCustomNetworkedEvent sends the event to said object

#

in this case, the "Instance" is the object you are sending it to im pretty sure

#

target is who is receiving the event, and eventName is the name of the method you want to invoke

twin portal
#

you can leave instance blank if your target is the current script itself

barren scaffold
glass lintel
#

wait, in UdonGraph, is the only way to invoke a method on an object other than self, to use SendCustomEvent?

twin portal
glass lintel
#

oh i guess udongraph doesnt have methods

#

so, :p

twin portal
#

they aren't methods, they're "Custom Events" :)

barren scaffold
#

^

twin portal
#

summon is a funny way to word it

barren scaffold
#

Its dirty coding as my friends call it but I'm new, I'll learn C# after I get the basics

glass lintel
#

I would assume yes, LEGOS is probably better to help you

barren scaffold
#

Corrected it Legos

twin portal
#

yeah that's what you'll wanna do, you'll need a variable for the UdonBehaviour you want to send it to

glass lintel
#

I'm currently at that point where its like "what the hell, i understand what im doing"

#

i can just do shit fr

barren scaffold
#

Been working on it for nearly 6 months, would get a pounding headache everytime I would start reading or working. Decided fk it and pushed through it, glad I did

barren scaffold
#

As of right now it isn't disabling the collider

barren scaffold
#

Got it

timber parcel
#

I've mulling over working with ya'll, especially with input from Phasedragon etc, to make best practices moderately complex network object to use as a example for everyone's benefit.

One that both uses state based networking and minimizes it's impact on per frame computing.

I'm trying to think of a good object that walks a balance between "Simple enough that a relativly new coder can get the jist" and "Complex enough to hit most common networking issues"

I'm currently thinking a handheld Minigun, which has to keep track of it ammuntion and reload state, idle > spinning up > firing states and has a way of sending damage (which I know is often difficult to handle with our limitations) to other objects that may be owned by other players.

If you've had some experience wrestling with udon# networking I'd like to know your thoughts on if that proposition seems like a useful example, and if it covers enough bases to be worthwhile.

fallow mountain
#
  • A gun is mainly a event based item, you are triggering an event that does not persist, a state is more like a persistent status that you might want a late joiner to see, for example the current high-score, or if a particular target is dead (and lying on the floor) or if a door is opened or closed.
  • Sending damage is now quite straightforward with the new “network events with parameters”
  • if you combine the gun (to show events networking) and some targets that has states eg HP amount then it’d be a nice tutorial
timber parcel
# fallow mountain - A gun is mainly a event based item, you are triggering an event that does not ...
  1. I'm glad you've brought this up as it's one of the core teaching points of the system. A well coded gun is also state based, not event based. Especially any type of automatic firearm, but this is also true for single shots. I know it seems logical that you should use a event system, but this can lead to lost shots for example.

  2. This might be the one event we will use then, but I'd love to avoid even this.

  3. Yes this is a good idea.

finite sierra
#

Hmm. I think i hit a udon limit again.. apprently when there is 48 players in a world using my system it sometimes breaks completely for them. And or refusing to work. I still have. To find out if its truly network limited or something else is going on. However it only breaks for the person it broke for anything else works

hoary frost
hoary frost
twin portal
#

yea. When you do a regular method call, it is converted to SendCustomEvent

#

same for setting variables from other scripts, it uses SetProgramVariable

hoary frost
#

.>

lone zealot
#

Not if its a method you wrote and its on the same behaviour

hoary frost
#

I see

#

Not that bad then

lone zealot
#

Then U# inlines it or just uses jump instructions (dont remember how exactly it handles it, but Im sure you can find documentation somewhere xd)

twin portal
#

that makes sense

hoary frost
#

Guess ill just limit my scripts calling onto other scripts instead haha

#

Assumed it would be cheaper to use defined types and calling their methods but i guess not

#

Ive got interfaces for OnTriggerStay events for instance so those need to be efficient

#

Built a little “sender -> reciever” system to relay events and reuse them

bold crescent
#

Small question: using "[NetworkCallable]" is causing an error in Unity.

[NetworkCallable]
public void hurt(int damage)
{
  //...
}

I cant seem to figure out which namespaces to include?

using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
#

Unless this attribute isnt required? I am a little confused with the docs

twin portal
#

are you on the latest SDK?

#

NetworkCallable is only required if you're using network events with parameters; omitting the attribute will cause it to run as a "legacy" event, as in they'll work just the same as they did before the parameter update

bold crescent
bold crescent
twin portal
#

your SDK should be 3.8.2 for the latest, I think parameters were added in 3.8.0

bold crescent
#

oooh I see!! thank you, updating now ^^

bold crescent
#

damn, I seem to be running into errors now...

System.Exception: Accessor VRCSDKBaseVRCPlayerApi.__get_gameObject__UnityEngineGameObject is not exposed in Udon

twin portal
#

you're trying to get the player as a GameObject?

#

players are protected objects, so attempting to reference them will just give errors like that, or just return null

finite sierra
hoary frost
north thistle
bold crescent
bold crescent
twin portal
#

yea it's not gonna let you do that directly

#

you'll need to have the object follow the player's position instead

bold crescent
#

Alright, thank you so much ^^ that fixed it

bold crescent
#

How do you recompile udon scripts?

twin portal
#

they should compile automatically when you save the file, then tab back into Unity, for U# at least. There's also a button on any of the UdonSharp program asset file (the green one) to Compile All UdonSharp Programs
For graph, the graph compiles as soon as you place a node, most of the time. But you can manually compile & reload with the buttons in the top-right of the UI

young thunder
#

You can configure U# to not instantly try to compile when the files change

#

I prefer this -- I have Unity set to only reload assets when I tell it to

#

the default is pretty aggressive :p

twin portal
#

especially alongside using Rider's default autosave options

bold crescent
#

Yeah Im just having some strange behaviours upon updating, currently trying to figure out whats going on

#

I only have a couple of compiled scripts...

#

But I have lots of udon scripts

twin portal
#

that is potentially a problem

#

every .cs file should have a paired U# asset file

bold crescent
#

can I generate these asset files?

twin portal
#

yes, but ideally they're already created when you make the U# script file

#

there's an option to make both simultaneously

#

I'm opening Unity to double check but I think it's under Create > VRChat > Udonsharp something? You want UdonSharp Program Asset

#

the name of the asset file must match the name of the script file

bold crescent
#

oh okay... lemme see

#

ty for your help btw!

#

okay, so it seems like I accidentally deleted these asset files, but they were still in my recycling bin... im unsure how i did that haha

twin portal
#

to create an U# script with both files you need, it's option #1. If for some reason you just need the green thing, it's option #2

bold crescent
#

oh okay, tysm!

foggy jackal
minor gale
hoary frost
#

And when lots of shots are fired, you run the risk of network clogging

twin portal
#

it clogs, but they will always eventually be sent

hoary frost
#

Ah really? Pretty sure ive had issues with never sent events in the past

#

Maybe it was due to something else

barren scaffold
#

How do I pull a specific player from the network? I want to tie their id to a toggle but am struggling to figure out how to pull that specific id in the first place

twin portal
barren scaffold
#

The toggles are going to be next to the name they affect. So its crucial that the id matches up with the display name

#

The result will look like this (Enjoy the beautiful art)

twin portal
#

yeah I don't see why you can't do that

barren scaffold
#

I'm not sure where I messed up calling the array

#

It shoulv'e been calling int's shouldn't of it?

twin portal
#

I'm assuming the GetProgramVariable is an array
you can't SetPlayerTag on an array, you need to loop the array first and get an individual element

barren scaffold
twin portal
#

yes you'd need to use a for loop on it

barren scaffold
#

My value here wouldn't be a display name right? (I know its not finished) I would need the ints correct?

twin portal
#

right, you want GetPlayerById instead

barren scaffold
#

I think I got it?

twin portal
#

no that's GetPlayerCount

barren scaffold
#

Cant do that?

twin portal
#

no lol. GetPlayerCount is the number of players in the instance

#

doesn't correspond to a player

#

for the For loop, the start int needs to be 0, the end int needs to be the array's Length

#

it should let you plug GetProgramVariable into VRCPlayerApi[] Get Length

#

wait it's an int array, so int[] Get Length

barren scaffold
#

I would feed the int output to the for and getplayerbyid?

twin portal
#

yeah

barren scaffold
twin portal
#

int > GetPlayerById > Get DisplayName > Set into your Playernames array

#

the Length doesn't go into both the loop and GetPlayerById

#

you need the actual value(s) in the array.

barren scaffold
#

My playernames is a string[] variable does that need to be a int[]?

twin portal
#

no, you're trying to store the display names in the end, no?

barren scaffold
#

Its hard to explain:

The system is like that in bars where they have a tablet for the bouncers where it displays a name and a toggle for access.

This is the script for the toggle, I need to be able to call the player in some fashion and assign a tag to them so that they receive permission to pass through a barrier (Barrier is finished)

#

Tbh I don't care it the tag is assigned via displayname or int I just need the ability to target a single player out of a group and then assign them a tag

barren scaffold
#

Legos, when I'm assigning the int value do I use the get length? Then end that with the setvalue? or do I need to feed that into the string[] variable?

twin portal
#

something along these lines

#

you use the Get node to get the value out of the array, and you want to do that based on the For loop. so the int to get is the index of the loop

barren scaffold
#

Sorry for making ya make it for me, I swear I'm trying, the for loops confuse the hell outta me

twin portal
#

no worries. loops are kind of convoluted in graph

#

it's also made slightly more complicated due to using the GetProgramVariable; if you have the actual array variable, there's a shortcut to automatically create a For loop

barren scaffold
twin portal
#

it's Int[]

barren scaffold
#

Thank ya

twin portal
#

if you have the array as a variable, you can hold SHIFT + F, then click on the node, and it will automatically add nodes for a For loop that loops over it

barren scaffold
#

Oh thats cool, I'll note that

twin portal
#

you may need to store the result of GetProgramVariable into an int array first

#

might not like trying to just use it directly

barren scaffold
#

Or wait, does the array need to be after the for loop?

twin portal
#

no it needs to be in an array first

barren scaffold
#

Tempstorage is a int[] variable

twin portal
#

right, it needs to be in an array before you loop said array

barren scaffold
#

The get length is having an issue? Should've I not of used Tempstorage for get length?

twin portal
#

no it didn't like it

#

maybe need to cast it first?

barren scaffold
#

Cast?

twin portal
#

not sure how do to that with an array type in graph though...

#

cast means to convert

barren scaffold
#

oh thats just convert, what does it need to be converted to?

#

Unless I'm thinking the wrong thing

twin portal
#

the GetProgramVariable outputs a variable of type Object, but we need it to be an int array. I assumed it would automatically convert it, but we may need to explicitly do it

#

I'm only seeing a node to convert a single int, not an array

barren scaffold
#

i got it

#

Fingers crossed anyways

twin portal
#

I don't think that will work

barren scaffold
#

It did not

#

Now it has a problem with setplayertag

#

Does the setplayertag (Near the end) need to have an instance to reference the player its assigning the tag to?

shy galleon
#

I have got myself confused about how object syncing works. Can anyone help me? I have a game object with a script that is on manual sync. This object's owner is being set correctly according to the debug menu. The object has a child object with an animator and VRCObjectSync. When the parent applies a parameter change to the child's animator, only the owner sees the animation play. Is VRCObjectSync not enough to ensure the animation is synced? I even tried RequestSerialization() on the parent after applying the parameters but the animation doesn't play for anyone but the owner

The tree looks like

Parent - Script
  |-- Child - Animator - VRC Object Sync
#

I'm having a hard time in general at understanding what will sync

tulip sphinx
#

ie set local player as owner, locally set some synced variable in your code, request serialization, both locally and ondeserialization for everyone else read that variable and apply it to animator

shy galleon
#

On another topic, I wanted to see if there's a common pattern for games. What I ended up with is something like

Game GameObject
  |-- Player 1 GameObject
  |-- Player 2 GameObject
  ...

It makes sense to me that each player owns their own object, but then someone has to own the game too. I thought maybe the first person to join the game would own it. It seems that with the different owners, in order for a player input to somehow affect the game, the player has to store some input on its object, serialize, send a network event to the game owner, and then the game script observes/iterates all the player objects to find out what happened. Is there something more robust or less cumbersome than doing that?

tulip sphinx
#

you can send network events with parameters now, so for example perhaps you can reference all player objects into an array with player id as index in main game script, and then player sends game owner event with its own player id as a parameter, so master can easily find it

shy galleon
#

Ooh interesting

#

I guess a lot of events need to be broadcast to everyone anyway

#

I've been trying to think about different vrc games I've played and wondered how I would imagine they handle networking

#

Although I kind of guess that some of them are just not late joiner friendly :D

warm steeple
#

is using a player object and getting its networked owner on custom event the recommended way to know who pressed the UI button?

hoary frost
#

That would just be sending a networked event on click, sending the local player’s id if necessary

warm steeple
#

basically what I want to do is change the label on the button for local player only, but handle the button logic on the networking owner

strange token
warm steeple
#

that's nice

strange token
warm steeple
#

I have a "manager" script which I use to call methods the from the button

#

there's only one copy of it since it handles global stuff

glass lintel
#

i prefer not to have util classes :(

glass lintel
#

When does OnDeserialization get invoked? I'm in clientsim right now, and when I do RequestSerialization(), I never see OnDerserialization() get invoked

#

I'm wondering if I need a different solution for the object owner (eg. Object owner requests serialization, then invokes method locally, Remote players see OnDeserialization and invoke method locally)

twin portal
#

OnDeserialization is never invoked by the sender

glass lintel
#

yikes alr that makes sense

twin portal
#

it's only invoked by the receiving players

#

functions that you'd need the sender to run, you can fire in OnPostSerialization

glass lintel
#

Oh I didnt know that existed

#

I thought thats what deserialization was lol

twin portal
#

calling right after requesting serialization technically works too, but doing this is assuming that the serialization is going to be successful

#

in OnPostSerialization, you can check if the serialization was successful, and how much data was sent

glass lintel
#

I'm setting a synced byte variable, then when everyone recieves that change, they need to do something with the variable

#

its just a byte

twin portal
#

makes sense

#

I assume you already have a setting function, something that is called to do something with the synced data, so you'd call this in both OnDeserialization and in OnPostSerialization

#

have you also looked into FieldChangeCallback?

glass lintel
#

nope!

#

All I'm doing is:

  • owner handles door logic
  • owner tells everyone "this is the new door state"
  • everyone sets the animator state
  • the door opens/closes for everyone (based off the animator state)
twin portal
#

instead of messing with OnDeserialization, you also have the option to set up the variable to use FieldChangeCallback
you can use this to fire a function when it is detected that the variable's value has changed to a new value

#

this works great for setting animator states

glass lintel
#

What about new players?

twin portal
#

new players always get synced variables when they join, so they will also fire OnDeserialization and FieldChangeCallbacks

glass lintel
#

oh so FieldChangeCallback would work

#

awesome

twin portal
#

this example shows how to make a synced bool that automatically runs logic when it gets a new value, you can edit this to just SetInt etc. on the Animator, rather than needing a separate function just to do that

glass lintel
#

So when I do [UdonSynced, FieldChangeCallback(nameof(Method))], it calls that method when the value changes?

twin portal
#

it runs the setter for it

minor gale
#

just as a caveat, it doesn't guarantee that you have all synced data when it fires, which won't matter for a single variable, but if you had linked variables it could matter

glass lintel
#

oh yeah i assumed so

#

I'm not new to networking, I'm very used to handling race conditions n shit

twin portal
#

this works best when you only care about running logic based on just this one variable

glass lintel
#

I'm only relatively new to C# and new to U#

#

So, if im understanding this correctly, there are 2 variables

  • private bool _syncedToggle
  • public bool SyncedToggle (or maybe this is a property? idk, C# is weird)

The public bool has something it goes when it gets, and something it does when it sets:

  • On set, log that you are toggling it, set _syncedToggle to value (?), set the active state of toggleObject to value (?)
  • On get, return _syncedToggle

is value the value of the variable/property SyncedToggle?

frozen igloo
#

Properties are just a way to gate access to a variable. So rather than setting/getting the variable itself, you go through the property instead and the property manages the variable.

So you only have one variable, and then you have a property which accesses that variable. And then FieldChangeCallback links the variable to its corresponding property in order to make the setter happen when you receive changes to that variable over the network

#

value is what was sent in to the property on set, which you can then use to set the backing variable

glass lintel
#

Pretty sure I understand

twin portal
#

yeah in this example SyncedToggle is your "real" variable, how I think of it at least. The backing variable so it all works, and you only ever mess with it in the setter & getter

it's kind of an abstraction, in a way, I guess. _syncedToggle is technically where the value is actually stored, and SyncedToggle just defines what to do with it

but in the rest of your code you just treat SyncedToggle like a normal variable

#

this is one of the few things that actually makes a bit more sense in graph

glass lintel
#
[UdonSynced, FieldChangeCallback(nameof(OpenState))]
        private byte _openState;
        
        public byte OpenState
        {
            set => _openState = value;
            get => _openState;
        }
#

setter method is that for now, but I could say, insert _doorAnimator.SetInteger() there, yeah?

twin portal
#

yeah, in the setter

#

see how the example can immediately do stuff with the new value, like SetActive

#

I use this for bools all the time, even in a context where they aren't synced. It's nice just setting a bool whenever I want a thing to happen, I used it for debugging to show different states being on and off

glass lintel
#

I wonder if after all this work.. it still might just be better to use an event, because I still need to do things like update cooldowns, do some logs, etc, and it would be gross doing that when the value changes

twin portal
#

yeah it all depends on what you're using the variable for

#

even if a ton of stuff needs changing, if everything is driven by just this variable changing, then why not just do it in the setter anyway?

glass lintel
#

just feels gross doing it there

twin portal
#

eh it all depends lol, sometimes the alternative can be more gross

glass lintel
#
        [NetworkCallable(maxEventsPerSecond: 5)]
        private void ToggleDoor(byte state, bool force)
        {
            float cooldown = (openState == 0 ? CloseToOpenCooldown : OpenToCloseCooldown) + Time.time;
            if (Networking.IsOwner(gameObject))
            {
                _globalHandleCanUseAt = cooldown;
                openState = state;
                RequestSerialization();
                SendCustomNetworkEvent(NetworkEventTarget.Others, nameof(ToggleDoor), state, force);
            }
            
            _localHandleCanUseAt = cooldown;
            _doorAnimator.SetInteger(_animatorOpenState, state);
        }
#

i guess I dont technically need to send the openState through, but, for late joiners the variable still has to be synced

#

Would probably be better if I made it a different method?

twin portal
#

I wouldn't do any networking in the setter

glass lintel
#

this isnt in the setter

#

i'm not using fieldchangecallback anymore

twin portal
#

oh didn't see that lol

glass lintel
#

lol

minor gale
#

is your variable meant to replace this network event then?

twin portal
#

yes, you'll want a different method for that, CustomNetworkEvents will not run for late joiners

#

ToggleDoor should just be put in OnDeserialization

glass lintel
twin portal
#

why can't they use the same method?

glass lintel
#

thinking about it, NOW they can

#

since im removing some stuff

#
        private void ToggleDoor()
        {
            float cooldown = (openState == 0 ? CloseToOpenCooldown : OpenToCloseCooldown) + Time.time;
            if (Networking.IsOwner(gameObject))
            {
                _globalHandleCanUseAt = cooldown;
            }
            
            _localHandleCanUseAt = cooldown;
            _doorAnimator.SetInteger(_animatorOpenState, openState);
        }

        public override void OnDeserialization()
        {
            ToggleDoor();
        }
#

and then when the owner sets the state, its just:

openState = (byte)(openState == 0 ? (isLeftHandle ? 2 : 1) : 0);
ToggleDoor();
RequestSerialization();
minor gale
#

if you want things to align a bit better you could store the time of the door event in network time as seen by the owner, in terms of when others believe the cooldown is over. the cooldown is currently susceptible to longer times on higher latency clients (as they interpret the cooldown at time of receive), though i don't think it's imperative

#

or infer it from the packet time on deserialize which passes you that innately

glass lintel
minor gale
#

oh i see

#

that makes sense then

glass lintel
#

To avoid race conditions in the first place, everyone locally goes "Can i interact with this?", and if yes, they then tell the owner, and the owner then confirms if they can, and does logic accordingly

#

In other news, the door works perfectly now, and in theory, should sync

minor gale
#

yeah running it through the owner is nice, a lot of vrc logic tends to rely on ownership transfer and variable updates which can lead to race conditions

glass lintel
#

I try to handle everything locally when I can, but sometimes its just easier to run through owner

#

Besides, not like this is network heavy, AT WORST the owner is getting 16 calls per second

#

and thats if EVERYONE is interacting with a unique door at the same time

#

its just unlikely

minor gale
#

you're actually enforcing that it's 5 there, which the server will enforce to the recipient i believe

glass lintel
#

But thats 5 calls per second per player

#

If every player interacts with a unique door, thats 16 calls per 1 second (because thats the local cooldown, and the network cooldown)

minor gale
#

does it only apply to sends?

glass lintel
#

yeah, but if there are 16 players, all 16 of them can send 1 request per second

#

thats 16 incoming, and thus there has to be 16 outgoing (16 people request to interact, 16 interactions have to be sent updating the door state)

minor gale
#

i just expected that it would also enforce the incoming rate as it mentions the server enforcing the rate, but i think you're right now that i'm re-reading it

#

you can get sorta high throughput depending on what you're doing, i've been having fun with this lately

barren scaffold
#

Setplayertag is failing since its not referencing anyone would I just use a getplayerbyid on it?

#

Like this?

minor gale
#

the index of the for-loop isn't their player id, you'd need to pull it from the array at that index (where int32[] expects an instance)

barren scaffold
timber parcel
#

I've made good progress on making a strictly state based gun but oof the code is a little cursed

Currently the only networked variables are shotsRemaining and timeReloaded. Both are shorts, so the total bandwith required is very mild.

I haven't set up a damage system yet, so that'll increase the bandwidth obviously.

network event count still zero.

warm steeple
#

for a game world I honestly don't know how you would go without one as something has to control the game

young thunder
#

every game has a "manager"; the only question is how many layers of abstraction you put up to pretend it doesn't have one (:

glass lintel
#

Because there should always be a case where there is a better place to put them

timber parcel
#

Cursed idea, I don't plan on using it:
Can I use reparenting as a type of networking an intended target?
eg:
Client 1 shoots a object owned by client 2. His gun grabs a damage token game object from the pool, reparents it to the target and writes to the tokens synced damage variable.
On deserialisation the damage token tells it's parent how much damage it just got hit for.

I'm certain this meathod would have many issues. Hypothetically, lets explore them

cold laurel
timber ferry
#

#udon-showoff message
speaking of ^

if i change a individual bool in an udonsynced bool array, then request serialization, does that sync the array correctly?

#

like, if i set doorLocks[3] in an array of bool[5], and i then run Apply that does a for loop for all colliders, and sets each collider’s enabled state to each bool’s state

minor gale
#

if it's initialized (on the person modifying and serializing it*) that should work yes

harsh pebble
minor gale
barren scaffold
#

What do I plug into the int value here?

#

I feel like using playercount would be wrong but I'm not sure how it works entirely

young thunder
#

What are you trying to do?

barren scaffold
#

Honestly assign roles to players without using Setplayertag

young thunder
barren scaffold
tulip sphinx
twin portal
# barren scaffold So I guess where I'm struggling is how do I know what to plugin there?

you're working with an array, a list of values. the Get node returns a specific value from that "list"; the input to that node is what number from the list you want.
this "number" is usually referred to as the "index" of the array. The index of the array starts at zero.
So if you have an array of five players: [player1, player2, player3, player4, player5]
player1 is at index 0. player2 is at index 1. and so on, until player5, who is at index 4.

The "Get" node for an array, then, is asking what element you want to get from the array, by index. Your current node will always return index 0, only the first player in the list.

If you need to run code for all elements in an array (in other words, for all players in the list), you need to use a loop to go through all entries.

The index of the loop can be plugged into the input for the Get node, so that code runs once for each element in the array.

remember I showed you that you can create a For loop automatically by SHIFT + F + Click on an array node.

barren scaffold
barren scaffold
twin portal
#

not the For loop itself. Most often a For loop will loop through all elements in the list

#

all a For loop does is do X thing multiple times, however many times you tell it to do so. I don't think you can make the exit condition very complicated in Udon Graph

barren scaffold
#

Alright so the VRCPlayerApiarray.Get is what pulls the information out? I'm trying to logically figure out how the value gets out of the loop.

twin portal
#

yes, that gets a single element out of the array

barren scaffold
#

Got ya, I think I can figure it out from here. Thank you Lego's and Least 🙏

#

If I wanted to assign "role" a value for the purpose of separate "permissions" would I use setvalue after set role?

#

(Ignore the event.update its there for testing purposes)

twin portal
#

SetValue will set the value of the element in an array at that index. You'll need a separate array to store a permissions value

#

probably a bool or an int array

barren scaffold
twin portal
#

well, yeah. You need to remember what players have what permissions after all.

#

or have a separate list for players, a list that is only authorized players

#

there's a lot of ways to do it

barren scaffold
#

Thats the point of the role variable which is a int[]. To seperate the authorized players from unauthorized.

twin portal
#

that works

barren scaffold
#

I hope its setup right so far-

twin portal
#

mmm the second half is not doing what you think it does

barren scaffold
#

Its not putting the player id's into a array?

twin portal
#

no. you're constructing a new array, with the length of the player's ID, and then assigning this empty array to Role.

#

(if it was connected, that is)

barren scaffold
#

So how do I store the collected role data if I can't use a constructor to store the list?

twin portal
#

that's a great question

barren scaffold
twin portal
#

what, one big string with all of the playerIDs?

barren scaffold
#

I mean, wouldn't it work?

#

Store the int values in the array as a string[] that I could refrence later as long as I can seperate it or identify the ints for permissions

#

(If even possible)

twin portal
#

why store ints in a string array when you can just store ints in an int array?

barren scaffold
#

🤷

I'm working with the knowledge of a toddler, I'm just winging it.

I'm afraid that if I use just a normal Int variable* that the players will lose their permission once the next player is assigned. I don't know how vrchat handles that information and I can't find anything about how vrchat stores player information.

twin portal
#

it's a learning process

barren scaffold
#

Most definatly, I'm trying my best with what I know. Hence why I'm asking so many questions, I know it can get annoying but I swear i'm trying. Wish the setplayertags worked better. Besides that:

If I take the getplayerid how am I supposed to store that into an int[] when the output isn't an array? I thought constructor did that but I guess not...

twin portal
#

so the current way you're trying to do it could technically work, but it's going to run into a lot of potential problems

twin portal
#

playerID is just an int. not an array of ints. so one playerID can go into one "slot" of the array

#

the array constructor you're using? that creates a new empty array, where the int input is the number of elements (slots) in the array.

#

but there's a big problem here. In C#, array size cannot be changed once it is created. It has to stay the same size once constructed.
You can cheat this by constructing a new array of a bigger size, then assigning it back to your array variable

#

but I wouldn't recommend doing this for this use case, as this can potentially be computationally expensive

#

whenever a player joins or leaves, you'd have to reset and resize the Roles array

#

This is why I suggested using PlayerObjects for this. Every player can have a copy of a small script, and this script can hold variables to store info for that specific player, like what role they have

barren scaffold
twin portal
#

could do, yeah. Each role could be its own bool

barren scaffold
#

So should I scrap this again and try the playerobject over?

twin portal
#

it is a little tricky to manage PlayerObjects for this use case, but I think the end result would have a lot less headaches

barren scaffold
twin portal
barren scaffold
#

The only thing I'd like to know is how do I reference a game object or value via playerobjects?

#

Like I understand how the playerobjects work but how do I reference their item?

twin portal
#

you can call GetPlayerObjects from a VRCPlayerApi, which will output a GameObject array of all of their assigned PlayerObjects

#

so if you've got the playerID, you'd go playerID > GetPlayerById > GetPlayerObjects

#

it is slightly weird to work with because this gives you an array, of course this is because the player can possibly have multiple PlayerObjects

barren scaffold
#

If I knew how I could reference a specific object in the array (Cube_1, Cube_2, etc) than I could just tie a cube to them, reference their cube and check the values on the cube or whatever object I use. Thats where I run into a wall. When it asks for a instance and I give it the object it only references that object not the other ones that are generated.

twin portal
#

I think you've pretty much got the idea

barren scaffold
#

Right track?

twin portal
#

pretty sure, you just need to know how to do it

#

so how could you get a specific object, out of an array of GameObjects?

barren scaffold
#

Forloop?

#

Like put the gameobjects into an array than forloop it

twin portal
#

that's a given, but what would this loop be doing?

barren scaffold
#

Mmmmm, the idea would be to check the value of the object for a certain int to allow it to pass through to send a custom event...

So I would need to make a variable of gameobject[] store the gameobjects, forloop them, and feed the results of the found one into a setvalue which gives it a different int value?

twin portal
#

for this case you probably wouldn't need to store it in a variable, you'd just set values on it then and there

barren scaffold
#

I was not aware I could do this...

#

I thought I could only do that on variables?

twin portal
#

it works on anything that's got an array as an output

barren scaffold
#

Sweet, good to know, let me throw this together rq

barren scaffold
twin portal
#

no, SetValue is for arrays

#

and you aren't editing the PlayerObjects array. You need to set a value on a script that's on the PlayerObject

barren scaffold
#

Sorry I'm a bit lost by your statement

twin portal
#

let's first understand, what in the end you're actually doing

barren scaffold
#

Assigning a role to someone

twin portal
#

with a PlayerObject, yes.
So You'll be making a PlayerObject, let's call it RoleManager. This is the GameObject's name, and has an Udon script also named RoleManager on it.
RoleManager can probably do some role-related functions in the future, but for now we know this script will just have variables on it that will track what roles a certain player has. This variables can be just booleans; true if they have the role, false if not.

To "assign" a role to a player, we just need to set the value of that role's boolean to true. That's the end goal.

#

so, how do we change the variable of a different script than the one we're running from?

barren scaffold
#

We use custom events to communicate between scripts

#

Oh wait a variable

twin portal
#

that should work
-# surprisingly wasn't what I was thinking to do lol

#

a custom event, or setting the variable, both can work

#

we just need to tell the other script, hey, change this variable.

#

using events might be better? since then we don't have to make the role variables public

barren scaffold
#

Alright, so the actual assignment of the roles will be handled outside the playerobject, correct?

#

Like toggles and button that will send the true/false value

twin portal
#

most likely will be. Unless you'd set up this same PlayerObject to have an interface to change the roles, that sounds like a headache

barren scaffold
#

Id rather it just be outside the playerobjects that way I don't keep duplicating it

twin portal
#

I imagined you'd have a UI with a list of players, and press buttons on it to assign roles

barren scaffold
#

Thats the idea, its not made yet besides the display name

twin portal
#

keeping it separate will help in an object-oriented sense too

barren scaffold
twin portal
#

so what do you like better, SendCustomEvent or SetProgramVariable?

barren scaffold
#

Id rather use customevents since I know those better

#

Plus their easier to work with

#

The display is a bit complicated but I can figure that out later. As of right now there are 2 parts to this that need figured out.

The role system, which allows players to pass through barriers, use objects, etc

Then the screen system which is a little more complicated.
As players join their display name and a toggle appears on the screen allowing the role to be assigned via the toggle

twin portal
#

yeah think of them as separate things, worry about making one system at a time, piece by piece

barren scaffold
#

Right

twin portal
#

so with custom events, you'll likely need events to add and remove roles. Something like EnableAdminRole and DisableAdminRole, for example

#

these functions on the PlayerObject, all they'd do is just set the role bool to true or false, that's all it'd really need to do

#

but our mystery system, who's job it is to call these functions to actually run the process, needs to be set up to do that

#

To do so, it needs to get the player (VRCPlayerApi), then their PlayerObjects, then find the RoleManager object, then find the UdonBehaviour on that object, then call SendCustomEvent on that UdonBehaviour.

barren scaffold
#

And all this would happen inside of rolemanager or the mystery script?

twin portal
#

mystery script that does the assigning. I say mystery but this should be the graph you've already been writing, I suppose