#udon-networking
1 messages · Page 26 of 1
i wouldn't personally design around the suspended state, but i don't make game worlds
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
In my experience so far trying to make Udon work, this is far too many assumptions already to feel confident about it, lmfao
not a dig at you, just my own comfort level with the 'certainty' of these systems
no I understand
my networking strategy is to drive the car until it explodes
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
Don't know how revelent that documentation is is anymore considering its age
Well it's more up-to-date than the original Udon# docs...
I mean, you still have a point, but ALSO that
recent Udon dcoumentation
What's the most up to date docs you use? Genuinely curious
'cuz there are like... what, 3? 4? maybe more? different domains that have creator docs now
and I'm losing track lol
Well right now that would be the Network Events page, lol
And yes, I do realize how not useful that is
url?
just so I know for sure what we're talking about
they tend to put the most updated docs on the Beta pages
Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.
Okay yeah so the Creator docs
though I realize different pages have different... ages
and the beta docs can have out-of-date info iirc
yep
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
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
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.
check in your VRChat settings, there's an option for what kind of logs you want, including one for Error Only
That has to be new I think. I've never seen that one before.
You were right
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
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
that's exactly what I did in the end
maybe this behaviour should be documented on the Networked Variables doc page
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.
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
Ah, how would I set that up?
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
that's not the change node
as you're dragging it, it says the key to press, I forget
the top is what you want
ok
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
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
like this?
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
Ah, it looks like it's fixed. Thank you
that's it
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
I’m glad I checked this channel later. But it’s for the teachers in my community. I don’t want the toggle only works for me.
Although it would be nice to have like a list of people that can have access to the toggle. Like the VIP room.
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
Ah ok.
you need to plug Get IsMaster into your Branch node, it now currently has no input
How secure is string/image loading against MitM attacks with https urls and an altered root certificate store?
How would you add persistence through UDON Graph for toggles? Like, something simple as this?
Or is persistence purely though Udon# (Which I have 0 knowledge on, I don't code)
@fringe agate https://creators.vrchat.com/worlds/examples/persistence/post-processing-settings/ or smth. also #world-persistence
Save and load bloom settings with PlayerData.
Didn't know there was a channel for it, it was hidden, thanks~!
I think all you'd need is after SetActive, just save the value of Get activeSelf to a persistent Bool
then next time the player joins, you'll need the event OnPlayerRestored, and simply plug this value into another SetActive node for the same object
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?
all the array gets sent out during deserialization, why would it matter if its got resized
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
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
Sorry, yes, poor semantics on my part
I meant reinitialized/replaced with an array of another size
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
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?
It decreases in size, yes. Each serialization is constructed independently, it does not care what was serialized before
Alright, cool. Thanks for the clarity ✨
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.
that's correct, yeah
part of it is the cost of serializing length and variable type, part of it is the byte alignment which is done for speed
Byte alignment? Would knowing how that works allow for more efficient packing of data?
ehh, maybe. You'd have to do your own research on that
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?
a VRCPlayerApi is not a syncable type
so yes, you'll want to sync the playerID instead
How do you sync a 2d or 1d array of integers via variables?
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
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
Datalists on their own are not, but they can kind of be synced, just takes some tricks
the docs have an example for it
Data Lists store Data Tokens by index, similarly to C# Lists. Most Data List functions are just wrappers for the underlying C# list, so the C# list documentation also applies if you are looking for more specific details.
you serialize it to a JSON string, sync that string, and then deserialize it back into the DataList once the data is received
I guess I am better of wrangling arrays then.
if it's a DataList of only integers then you should be fine just doing that
nope.
type 'int[][]' is not supported by Udon sync
believe you can only do single-dimension arrays for syncing
I hate it here. Time to employ hacky workarounds again.
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
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
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
it'd be inputSubmitHandler.SendCustomNetworkEvent
I see I see thanks!
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?
Ondeserialization is guaranteed to run after field change callbacks. Because the variables must be set before you receive ondeserialization, and the field change callbacks happen in direct response to the variables being changed
Great, thanks ❤️
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
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
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
}
}
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();
}
}
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);
I tried using TeleportTo, worked even less reliably
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);
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
You kinda always need to have a small delay after a transfer. And if you do alot you need it even more so.
It really depends
When i had this issue, i would just make it wait a whole second instead and it would usually work
Have you tried setting the pos and rot of the pickup's Rigidbody in the same frame as setting the transform?
Okay, quick question because now that I'm working with this sort of stuff, its something I need to handle
- How do I send data to other players? eg. a player needs to send some ints to the room master
- 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?
Multiplayer experiences are at the heart of VRChat. To create a world that reacts to players and synchronizes data between them, you need to understand networking in VRChat.
You shouldn't directly combine variable syncs with network events. They're two separate networking methods with different intents that work differently.
variable sync is good for synching the state of a thing
network events are good for indicating why something happens or for requesting a remote player to do something
Note that network events have the ability to also send parameters
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?
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
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
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)
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
just understand there is a global limit on Network events. so dont use them to often.
As in, data limit, or a cap on how mant you can have?
I know RR has the "Networking heat" limit which is what I'm used to, its 2kb/s
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.
Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.
yeah thats nornal, data cap and RPC cap
its abit down on here but it tells you how much
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
but just to quickly give u that info its Maximum configurable rate: 100 events per second
globally.
Can network events only send primitives like int/string/float etc, or can they send custom objects/classes?
no only primitives. so int,float etc
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.
worst case i have strings
so make sure every event is under 1024 bytes. otherwise it will cause two
on a side note, DataLists and DataDictionaries have a built-in json serializer that you can use to sync them across the network
[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());
}
}
Sometimes I wish we could unreliably sync stuff over the network...
isnt that the whole point of events? or am i missing some irony?
Events are still send reliably
Quick question, can I send transforms through events, and if not, can I send Vector3's through events?
The only networking in Udon that is unreliable as far as I know is continuous sync and VRCObjectSync (although I suspect VRCObjectSync sends out a single reliable sync when it sleeps)
That's why I champion VRCObjectSync as the most efficient way to sync erratic movement despite how annoying it can be to use.
By events do you mean networked events, or SendCustomEvent?
yeah, SendCustomNetworkedEvent
these are all the variable types you can directly send over the network https://creators.vrchat.com/worlds/udon/networking/network-details#synced-variables
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
transforms are references and cannot be synced
fire
You could do a Vector3 or give your transforms an id and reference it by that
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
do not that the second method would only sync the reference, not the local data that transform holds
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
mmhm
Is there any reason why an udon synced variable is not syncing for me. My synchronization is set to manual.
are you successfully firing RequestSerialization?
That would probably help wouldn’t it?
One day, one day I’ll understand this
become owner - change stuff - request serialization - do reaction on stuff.
on deserialization (for everyone else) - do reaction on stuff.
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
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
you need to ensure the Owner is running this function
Ok i think i can do that
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
So what should i do with OnDeserialization
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
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;
}
you can surround your code in triple backticks ` to format it as code
ok i will do that instead of flooding the chat next time
dw about it we aren't running out of chat real estate space
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
I don't think I'm seeing you set the seed though
the seed is an int btw, not a string
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
but isn't seed the synced variable?
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
Alright i might just but all my stuff in one gameobject
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
Yeah thats probably a better idea than trying to fix the abomination ive made without knowing how this works
haha yeah, I'd recommend taking that approach anytime you're working with something unfamiliar
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
get something small working first, then piece it together into what you need
IT WORKS!!! Oh my gosh IT WORKS
hell yeeeaaaahhh
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
is anyone else having a hard time writing a code to summon players to you
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
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.
hopefully it's not network ids bugging out
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
are you trying to use SendCustomNetworkEvent on or from an instantiated gameobject?
instantiated object do not support networking
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
Do you have an OnDeserialization event?
No i do not, I do have a OnPostSerialziation which triggers and only lately used for testing
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
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?
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
- 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.
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
@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
ideally you'd just send an event to everyone, with a parameter included for the position
then have the player teleport to that position
the data is guaranteed to arrive eventually; if network conditions are so bad that the data can't arrive then the player is going to be timeout kicked from the instance anyways
Ah i see, thats good to know!
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?
The rate limit is local. As in, the queue for events waiting to be sent is per-player, every player has their own queue and limits how much data they send out
fire
Enums are not a syncable type
Check the Networking doc page for a full list of available types; you can sync arrays of the available types too
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
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
oh, can you not call networked events on methods starting with _?
That's right
fire
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
oh lol, im already doing that
any event I want to be networked, im using [NetworkCallable(maxEventsPerSecond:X)]
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
If I want to send an CustomNetworkedEvent to another script, do I have to use "MethodName" and not nameof(MethodName)?
Jusy fyi..that is only for network events that has parameters. Wont do anything for non parameter once i believe
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
It came with the parameter update sooo
You just have to remove it from the methods that has no parameter
alternatively, i make each event like 1 byte bigger by adding a useless bool
Eh. That will make you eat the rate limit fast lol
well, i guess ill figure out in testing unless someone can tell me in confidence that it NEEDS parameters
1 byte of 1024?
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
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
Oh sorry its 100 but its dynamically adjusting the limit. If the docs are to go by
You can use namespace bit have to re-access the script to reference the method name
So eventListener.SendCustomNetworkEvent(nameof(eventListener.MethodName))
oh sick yeah that works
"EventHandler" is my own class, would I use EventHandler.MethodName (the method on the class), or eventHandler.MethodName (the object that has the script)
The object that has the script 👌
okay, noted
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);
oreventHandler.SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(eventHandler.EventTryStartGame), false);
I'm assuming its this, since you are having this send an event to that.Method
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
Yes*
* as long as no malicious client users take ownership of them
It's up to you to care or not care about this edge-case.
i have issue to the players dont load the song and i cant load url to see video
You can rate limit events without parameters. You likely need to use the [NetworkCallable] tag to get all the new network event features, however.
Oh, the question was, can i call SendCustomNetworkEvent on an event that has no parameters
Of course
Also if you want to call SendCustomNetworkEvent on another script and still want to use nameof, you can do nameof(MyOtherClass.MyMethod)
Okay, so its OtherClass.Method, not objectWithScript.Method?
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
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
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"
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
(its no longer eventHandler.sendblahblahimwakingup, i thought the target would be defined in nameof)
How you get that string is up to you, manual, nameof, or some other method
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
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?
if you want a quick and easy solution, remove that UdonBehaviour component and replace it with "VRCObjectSync"
Thanks, but putting the component on my prefab playerObject returns this error when building:
It happens everytime, even when reloading Unity and my computer
You'll need to go here to address that error:
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.
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
my own approach would to turn the door into a state machine and sync the state (which could be stored in a single byte)
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
mmhm
Gotcha, thanks!
Actually, pretty sure I already have the states for the animations
Yeah
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
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
Ah, understandable
Yeah, basically move the state machine from the animator to code.
And make the code enforce that the animator stays in sync
To make sure, you can get properties from the animator, right?
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.
Fair enough
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.
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/
Gotcha, will do that then
Unfortunately not.
Damn, was confused to why an event wouldn't send at first, would be nice to see implemented in the future
SendCustomEvent parameter support when, lol
Prepare to throw up, but had to do this as a first attempt lmao
My state machines are almost exclusively script based, but nothing wrong with using the animator as well. Whatever work best for you.
I'll try both, honestly, best to learn along the way ^^
See which feels nicer and stuff
I also make every single manual sync object function via a state machine
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.
Lmao
Ahh good point
Saying you "had" to do it is a bit sus. Were you held at gunpoint by any chance?
You should have seen what people were doing before you could send parameters with network events
-# still a beginner ;-;
But yeah, trying to learn along the way on optimisation and better ways to do things, I learn fastest that way-
I'm scared-
yes
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.
The motto of a dev: "If it works, it works."
So this is basically the exact opposite approach of what we're suggesting, your animator state machine is telling your script what to be
More, "adapt to your environment"
Want to see something worse then-
Kill me-
Nested Ifs lmaooo
Yeah, so, whenever this happens it's usually a sign you should reconsider the architecture of whatever you're building
My first attempt was basically "Get it working, then optimise it later"
Oh definitely, that's why I wanna start from scratch with it
It's normal to code something 2 or 3 times
Lemme just quickly uh...... CTRL + A | Backspace
I wouldn't do that, unless you're versioning your project with git, which you should be doing.
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!
Is this from 2020 lol
. Considering you dont need to make a method per one. True or false. You could just do a single one for each that uses Tenary and reverse the state
hey i know that repo
What is the best way to go about deleting a player's playerObject when they leave the game?
They are deleted automatically after OnPlayerLeft is called
Oh, odd, since they aren't deleting on other player's games.
Is this only the case if the playerObject is synced?
What are you refering to
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.
Asking for Sanity check: Is the playerId for VRCPlayerApi the same across all clients connected to an instance?
yes
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
i dont think udon has access to network ids, theyre for more internal stuff.
have to make your own
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.
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.....
might have made time sensitive networking code which is generally a big no-no
500 mile email moment
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)
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.
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
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
I’m inclined to believe no samples means there hasn’t been any recorded data on that object, but it’s weird that it’s considered a status without being mentioned in their docs
yeah, though I'm p sure the docs unfortunately don't really explain any of the possible states outside of just mentioning most of them exist
would also be weird if "No Samples" meant "no data" bc clearly it knows what the object rotations should be... surely that should count as "at least some data" lol
(not implying you're wrong, js it's a weird situation/state)
These are the tools you can use to debug your worlds in-game.
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)
i see them mention the states, but i can't find explanations ?
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
The real question(s) are #udon-networking message
should sleep: objectsync thing
held: objectsync thing
discontinuity: objectsync thing
player: player thing
I don't know what no samples means, but I would expect something like that to come from anything that's not objectsync or players
well the only other ~potentially networked~ component on each pickup is a "NoVariableSync" UdonBehaviour, so...... idk what else would be showing that status...
v bizarre
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?
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
Around ~40 active VRCObjectSync objects will clog the network in a near empty instance (due to how parts of VRC;'s networking works, you'll get less bandwidth available to use as more players are in the instance).
Inactive VRCObjectSync objects, I don't think there is a limit since they stop using bandwidth anyways.
what qualifies as inactive?
ive got near 40 pickups on my scene but theyre not all gonna be held at the same time, so id like to know
probably in reference to the rigidbody going into sleep mode, since that makes it stop syncing
It being asleep, which happens after a few seconds of it not moving.
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?
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)
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?
-1 is often used for a null player id
every body gangsta until 4,294,967,294 players cycle through the instance and get the id -1
-1 is my “null” for any array reference variables I ever make
☝️ 🤓 uint32 cannot be negative
playerID says it returns int, is that not the case?
well then its 2147483647
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
That is still insanely high
go from -1 to -1 again by incrementing, the steps needed for that aren't equivalent to int max
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
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
Doing this would be pointless.
Yeah, playerId is a monotonic integer that starts at 1 which is given to the first player that joins the instance.
If that player later rejoins they will be given a new playerId, even though they already had one in that instance.
2147483647 + 1 = -2147483648
-2147483648 + 2147483647 = -1
2147483647 * 2 = 4294967294
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?
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
Not trying to account for the 2 generals problem here lol (although Tom Scott does have a great video about it)
Mainly concerned about the former.
Regardless, how would I use SendCustomNetworkEvent and have the remote user know it's supposed to go to them?
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
send their playerID in the event
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
Using the new variables thing?
yeah that's a way to do it
I haven't actually looked at it yet haha
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
it is NetworkEventTarget
yeye
https://creators.vrchat.com/worlds/udon/networking/events/#sending-events-with-parameters @cyan hollow
Network events allow simple one-way network communication between your scripts. When a script executes a network event, it executes the event once for the target players currently in the instance.
fire I knew there was a better way to do it
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
interesting
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.
why not specify the player id?
Would this be best in like, a PlayerEventHandler object? or something of similar name? I know i personally dont like having everything in one place (thats why i have an InstanceData and PersistentData object)
ig it doesnt really matter, but, point is still there
yeah it's a viable approach, there are a lot of cases where it's best to have one player centralize some operation
It really depends how you want to handle it. The time I've used the technique specified, I had it target a PlayerObject titled "PlayerManager"
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
yeye
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
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.
this case would make the most sense if they are on PlayerObjects yeah
if not, sending the ID would be sufficient too
sending to a manager object doesnt do anything if you want to send it to a specific player, all that does is send an event to that objects
sending the event to the owner of a player object, thats always going to be said player
you can send the player's id as a parameter is the implication here
I have an EventHandler (now called CustomEventHandler), you'd have another object for PlayerEventHandler which is a player id
its only 4 bytes, but still, its 4 bytes
Since network events have guaranteed delivery, every receiver of a network event has an additional network cost.
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
Is that how that works in VRC?
I'm used to "sending an event to one person vs every person" costs the exact same, since you just send data to the server thats limited
Ig in VRC the cost increases per receiver because its the cost of the server sending it to the players?
It's more than the 4 bytes
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
not if you send it to the owner
For the sender the cost is the same, yes. But when a player receives an event or a manual sync, they then need to upload to the VRC server that they have correctly received the data.
oh that's probably true yes
mmm that makes sense
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.
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
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
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
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
Yeah there can only be one player per station
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
I meant even after exiting no one else can use that station
Do I have to use SendCustomNetworkedEvent to free up the station somehow?
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
Thank you I might try that
is there a way to see errors while playing online?
yeah, if you have the launch option set (i don’t remember what it is), you can press RightShift+~+3 to open the console in-vrc
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
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
Hi, how can I synchronize the text editing of a TEXT TMP to make it visible to all players?
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
ok, I miss knowing how to sync the string
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?
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)
Thanks, in fact it was my code that had an error, I used the variable change to execute the synchronized instructions but with the second user it did not change anything and therefore it does not activate.
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
I am not aware of any way that could happen
@brisk magnet @young thunder
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)
no, the animator will not be synced
fire, manual sync it is
VRC Object Sync only syncs position and rotation
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
oh im not using a VRC Object Sync anymore
i was only gonna use it if it synced the animator
ahh
since it doesnt, its just a script
I think VRC Object Sync only syncs the transform
technically this anim is only rotating and moving the door
but
just telling everyone what state the door is in is cheaper
oh, and maybe also some rigibody stuff as well
that'll work, yah
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.
When I want to scripts to interact with one another do I use the sendcustomevent or the sendcustomnetworkevent
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)
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?
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
you can leave instance blank if your target is the current script itself
It is not unfortunately, its communicating to a seperate script but thank you for the extra info 🙏
wait, in UdonGraph, is the only way to invoke a method on an object other than self, to use SendCustomEvent?
yes; under the hood, U# is actually doing the same thing.
they aren't methods, they're "Custom Events" :)
This is where I would create a udonbehavior variable and call the other script?
^
summon is a funny way to word it
Its dirty coding as my friends call it but I'm new, I'll learn C# after I get the basics
I would assume yes, LEGOS is probably better to help you
Corrected it Legos
yeah that's what you'll wanna do, you'll need a variable for the UdonBehaviour you want to send it to
Oh yea, I learned C# SO FAST after different visual scripting
I'm currently at that point where its like "what the hell, i understand what im doing"
i can just do shit fr
I just had that moment this morning
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
On the recieving script do i need anything special? Or just a custom event?
As of right now it isn't disabling the collider
Got it
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.
- 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
-
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.
-
This might be the one event we will use then, but I'd love to avoid even this.
-
Yes this is a good idea.
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
Isnt sendcustomevent an extern call? Therefore more expensive than a regular method call
Check you arent hardcoding things like GetPlayers with a const int instead of playercount
yea. When you do a regular method call, it is converted to SendCustomEvent
same for setting variables from other scripts, it uses SetProgramVariable
.>
Not if its a method you wrote and its on the same behaviour
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)
that makes sense
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
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
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
Im using the Vrc CC program, so I hope so?
unfortunately I have to use a parameter for damage
your SDK should be 3.8.2 for the latest, I think parameters were added in 3.8.0
oooh I see!! thank you, updating now ^^
damn, I seem to be running into errors now...
System.Exception: Accessor VRCSDKBaseVRCPlayerApi.__get_gameObject__UnityEngineGameObject is not exposed in Udon
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
you do know you can check player count via the instance lol? and the ingame loggin. so no. i was made aware of this from people who used the map/system that at 48 players which is the max i allow it breaks
Pretty sure its something close to VRCPlayerAPI.GetPlayerCount
try using VRC.SDK3.UdonNetworkCalling;
Yeah, I tried that before, but i was on the wrong version! I think thats okay now :)
aaah... I was trying to add a gameobject as a ( transform ) child of a player
yea it's not gonna let you do that directly
you'll need to have the object follow the player's position instead
Alright, thank you so much ^^ that fixed it
How do you recompile udon scripts?
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
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
especially alongside using Rider's default autosave options
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
can I generate these asset files?
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
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
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
oh okay, tysm!
yeah I'd be fine with the default if it would do it in the background or faster, I just hate when it makes me stop and wait
how do you lose shots in an event-based setup?
Id guess just because RFCs/networked events are unreliable by nature
And when lots of shots are fired, you run the risk of network clogging
it clogs, but they will always eventually be sent
Ah really? Pretty sure ive had issues with never sent events in the past
Maybe it was due to something else
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
you would need to get the player as a VRCPlayerApi, then just GetPlayerId on that. Then save the int somewhere
Can I use udonbehavior.getprogramvariable if I have a array with the int values on another script?
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)
yeah I don't see why you can't do that
I'm not sure where I messed up calling the array
It shoulv'e been calling int's shouldn't of it?
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
So feed it through a for loop? or what would I even start with? From there I can figure it out
yes you'd need to use a for loop on it
My value here wouldn't be a display name right? (I know its not finished) I would need the ints correct?
right, you want GetPlayerById instead
no that's GetPlayerCount
Cant do that?
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
I would feed the int output to the for and getplayerbyid?
yeah
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.
My playernames is a string[] variable does that need to be a int[]?
no, you're trying to store the display names in the end, no?
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
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?
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
Sorry for making ya make it for me, I swear I'm trying, the for loops confuse the hell outta me
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
it's Int[]
Thank ya
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
you may need to store the result of GetProgramVariable into an int array first
might not like trying to just use it directly
no it needs to be in an array first
Tempstorage is a int[] variable
right, it needs to be in an array before you loop said array
The get length is having an issue? Should've I not of used Tempstorage for get length?
Cast?
oh thats just convert, what does it need to be converted to?
Unless I'm thinking the wrong thing
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
I don't think that will work
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?
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
@shy galleon #udon-networking message
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
Interesting, ok, I think I get it. I rather embarrassingly didn't even realize OnDeserialization() existed so that should help. It seems that my missing info here is that all players need to be informed of the update.
edit: Yeah neat, that seems to have got it.
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?
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
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
is using a player object and getting its networked owner on custom event the recommended way to know who pressed the UI button?
I dont usually use player objects so i can tell you about a way without using them
That would just be sending a networked event on click, sending the local player’s id if necessary
do you send the id with a synced variable or smth?
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
You can send networked events with parameters now so you can just create the event expecting an int as a parameter then pass the playerID in when you call it
that's nice
If you wanted to handle the button logic on the networking owner you could put the button code on your playerobject script then on press get local playerobject and modify then sync the values on the playerobject? 🤔
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
i prefer not to have util classes :(
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)
OnDeserialization is never invoked by the sender
yikes alr that makes sense
it's only invoked by the receiving players
functions that you'd need the sender to run, you can fire in OnPostSerialization
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
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
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?
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)
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
What about new players?
new players always get synced variables when they join, so they will also fire OnDeserialization and FieldChangeCallbacks
All supported attributes in UdonSharp
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
So when I do [UdonSynced, FieldChangeCallback(nameof(Method))], it calls that method when the value changes?
it runs the setter for it
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
oh yeah i assumed so
I'm not new to networking, I'm very used to handling race conditions n shit
this works best when you only care about running logic based on just this one variable
I'm only relatively new to C# and new to U#
So, if im understanding this correctly, there are 2 variables
private bool _syncedTogglepublic 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
_syncedToggletovalue(?), set the active state oftoggleObjecttovalue(?) - On get, return
_syncedToggle
is value the value of the variable/property SyncedToggle?
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
Pretty sure I understand
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
[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?
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
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
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?
just feels gross doing it there
eh it all depends lol, sometimes the alternative can be more gross
[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?
I wouldn't do any networking in the setter
oh didn't see that lol
lol
is your variable meant to replace this network event then?
yes, you'll want a different method for that, CustomNetworkEvents will not run for late joiners
ToggleDoor should just be put in OnDeserialization
Yeah, im going to use 2 different methods, one for the owner (SetDoorState), and one for everyone (ToggleDoor)
why can't they use the same method?
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();
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
It shouldn't be
_localHandleCanUseAt is the local cooldown (once per second on use, or based off cooldown on door toggle)
_globalHandleCanUseAt is the global cooldown, since all interaction requests go to the room owner, this can be localized to just the room owner
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
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
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
you're actually enforcing that it's 5 there, which the server will enforce to the recipient i believe
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)
does it only apply to sends?
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)
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
Setplayertag is failing since its not referencing anyone would I just use a getplayerbyid on it?
Like this?
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)
So my tempstorage which is a int[]
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.
do you mean static classes or "managers" of global things that I mentioned?
for a game world I honestly don't know how you would go without one as something has to control the game
every game has a "manager"; the only question is how many layers of abstraction you put up to pretend it doesn't have one (:
nah, managers are fine, I have a bunch
i mean like, I dont want to ever have a Utils.cs or something that's just a bunch of methods
Because there should always be a case where there is a better place to put them
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
Parenting isn't synced. Also, you should just use network events with parameters in this case.
#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
if it's initialized (on the person modifying and serializing it*) that should work yes
Those aren't all networked synced somehow right? 😂
they are synced, though there isn't a networked behaviour on them. there's a level of local simulation before i consider them "settled"; but i do propagate them to late joins (they're instantiated, not pooled in this case)
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
What are you trying to do?
Honestly assign roles to players without using Setplayertag
AllPlayers is an array of players. The number you plug into the Get node determines which player is retrieved from the array
So I guess where I'm struggling is how do I know what to plugin there?
@barren scaffold #udon-networking message
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.
Gotcha and that answers my question so utilize the forloop on the array to feed that into the get to get the player I want, understood.
Question, does it run the forloop until it finds that specific piece of data for the person its looking for than exits the forloop?
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
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.
yes, that gets a single element out of the array
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)
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
I have to store my permissions as a value? Like if I wanted to disable a object or wall?
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
Thats the point of the role variable which is a int[]. To seperate the authorized players from unauthorized.
that works
I hope its setup right so far-
mmm the second half is not doing what you think it does
Its not putting the player id's into a array?
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)
So how do I store the collected role data if I can't use a constructor to store the list?
that's a great question
String?
what, one big string with all of the playerIDs?
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)
why store ints in a string array when you can just store ints in an int array?
🤷
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.
it's a learning process
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...
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
you have to think of the "array" and "the array's elements" as two entirely separate things. You don't assign the IDs as the array itself, you assign them as elements of the array
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
I tried to use the playerobjects and did for some of it. I ran into a problem when trying to give them roles though. I don't know how to refrence the copied playerobjects. Everytime I look up they give me a reference with a component look up.
Would that system still work If I wanted to assign 2-3 roles?
could do, yeah. Each role could be its own bool
So should I scrap this again and try the playerobject over?
it is a little tricky to manage PlayerObjects for this use case, but I think the end result would have a lot less headaches
I'm to the point I just want it working while learning udon 😂
That is what I would recommend. But you've got free will and can do whatever you want.
Your other option would be to set up the Roles array to accurately store the information you need. It's doable but there's a ton of edge cases to consider and you'd have to account for all of them
I'd rather scrap and do it with the player objects.
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?
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
(Which they do in this case since each one has a nametag* script)
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.
I think you've pretty much got the idea
Right track?
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?
that's a given, but what would this loop be doing?
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?
for this case you probably wouldn't need to store it in a variable, you'd just set values on it then and there
it works on anything that's got an array as an output
Sweet, good to know, let me throw this together rq
For the instance of the setvalue I wouldn't use the getplayerobjects right?
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
The one under networking?
Sorry I'm a bit lost by your statement
let's first understand, what in the end you're actually doing
Assigning a role to someone
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?
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
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
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
Id rather it just be outside the playerobjects that way I don't keep duplicating it
I imagined you'd have a UI with a list of players, and press buttons on it to assign roles
Thats the idea, its not made yet besides the display name
keeping it separate will help in an object-oriented sense too
so what do you like better, SendCustomEvent or SetProgramVariable?
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
yeah think of them as separate things, worry about making one system at a time, piece by piece
Right
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.
And all this would happen inside of rolemanager or the mystery script?
mystery script that does the assigning. I say mystery but this should be the graph you've already been writing, I suppose