#udon-networking
1 messages · Page 20 of 1
You shouldn’t need to sync the GetPlayers array because everyone in the instance runs a method that just erases the list and re-assigns all players that are currently there whenever anyone joins or leaves
hmm interesting
But you do need to find a way to sync that you updated the staff list after adding or removing someone
So two different arrays
Sync the staff one ye
to clarify is the staff array gonna be playerids ?
Yea that’ll be the most efficient
it'll make it really easy to sync
right
No reason to sync strings when a single number suffices
so, how would i got about doing the GetPlayers one now? iv never done it like that before
all i got rn is this xd
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
void Start()
{
}
}
Hehe let me grab some docs
These nodes are useful for getting an individual Player, a group of them, or all of them.
var players = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(players);
- Udon Events
basically just have an array of type VRCPlayerApi[]; then every time you need to update it, you have those two lines kinda
one line to create an array that is the same length of the player count, then the actual GetPlayers call to fill the array
I forgot legos was lurking
He’s way more efficient of a tutor lmao
Ping me if y’all get lost!
thank you
so, would i create my own array like the staffList[]?
yes, you will need both
should it be priv or pub?
one array is all players, the other is your admin list
that depends if this script is going to handle what we actually do with this data, or a different one
probably just this one, no?
would i be right to assume we would be copying this data over to the VRCPlayerApI[] we made?
uhhhh good question
i didnt think that far ahead hmm maybe i can keep it all in one script
probably best
usually i try to keep things in one script
that's what the second line of that example does; the input of that function is the array, and it will fill it with the VRCPlayerApis of everyone in the instance
alright, so should i change the players to be playerList?
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
void Start()
{
}
public void FindPlayers()
{
var players = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(players);
}
}
context ^
yes so... you'll want to change var players to just playersList, and change the GetPlayers function to also use playersList
we don't want var players because this declares a new variable, we want to actually keep the data for later
got it
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
void Start()
{
}
public void FindPlayers()
{
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
}
yep. so now whenever FindPlayers() is called, it will fill the playersList array with all players
okay, now the times we want to call this, would i be right to assume on join/leave and start?
just join and leave is all that's needed, since OnPlayerJoined is called when you join a world anyway
alright, 1 sec
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
}
}
that'll do
okay, so everyone is updating their own local copy of the playerList correct?
right, so now I need to figure out how to add players in now for the staffList
this is the harder part xd
the idea of it is easy yeah...
but the execution, not so much
so you'll probably need the UI setup at this point
Yeah, ill need to create two UI refs i assume for regular players in the instance and a list of who is staff correct?
yeah, you'll probably need some sort of way to display both lists
I haven't tried it, but maybe a ScrollRect that Instantiates some new buttons in the UI for every player?
i'v never done that before o.o
either that or, maybe your arrow idea
maybe a combo of both, display the names, but have a button on the bottom to cycle through the available names
no extra buttons just 1 per list
could start simple with the arrows and upgrade later if you want
it's all gonna be mostly up to you really
true
so i got these guys down right now
public TextMeshProUGUI staffUI;
public TextMeshProUGUI playerUI;
that will be just the text display
mhm, i believe i will need a reference to the button text now?
may not need a button reference, only if you want to modify the button itself
all you need the button to do is send an event
sorry i meant to change the text of the button
then that would actually be the TextMeshProUGUI component that's on the button
like we got a list like this
Player 1
Player 2
Player 3
< player name >
pressing an arrow will change the button text to match the position in the array
so if we pressed the right arrow, we would be on the next slot
< Player 2 >
if we press the Player 2 button, itll do the logic
right so just 2 more refs xd
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
}
public void UpdateListUI()
{
}
public override void OnDeserialization()
{
UpdateListUI();
}
}
Does anyone have a video or something explaining network synchronization and such? I think I saw one being posted a while ago.
i know this channel has a bit of content on networked stuff https://www.youtube.com/@PlayerBush001/videos
Heyo, Bush here.
Learning unity and VRChat together is hard, especially when there aren't enough videos to get you started. Hopefully I can help with that! I hope to make tutorials that are fast and to the point. Ones that you can look over without have to resort to 2x speed, but also contain everything a newcomer would need, should they be wil...
#udon-networking message this?
imo this vid should be pinned btw
discord exclusive (?)
anyone know how to cycle through an array?
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
}
public void LeftArrow()
{
//Cycle back once and update the button text to match
}
public void RightArrow()
{
//Cycle forward once and update the button text to match
}
public void SelectPlayer()
{
//Perform the logic on the assosciated player
}
public void UpdateListUI()
{
//Update the listUIs
}
public override void OnDeserialization()
{
UpdateListUI();
}
}
context ^^
for loop
(idk the code i just know graph)
xd
fair enough
not sure if this AI solution is wise (i hate using AI for code help outside of debugging)
this is what it suggested
public void LeftArrow()
{
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex--;
if (currentPlayerIndex < 0)
currentPlayerIndex = playersList.Length - 1;
UpdatePlayerButtonText();
}
public void RightArrow()
{
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
currentPlayerIndex = 0;
UpdatePlayerButtonText();
}
hmm it seems like it could work but i need to somehow use that index now in the list
yes you'll want to keep track of an index that you're currently "looking" at the array with
ai right for once? lmao
it's really annoyingly written imo
well... just in a way that I personally write things, future-proofed when I add things later
hmm
now how would i make use of this index ?
would i be using a loop on the playerList array to match up the index ?
no, you'd just access the index directly
oh?
with playerList[currentPlayerIndex]
from there you'll grab anything and put it in your UI where you'd want it
how would you write this?
playerList[currentPlayerIndex] will be "this is the player we're currently wanting to display on the screen"
it's not bad to use AI because it's wrong. It's bad because you aren't going to learn from anything it shows you
yup
thats why i try to avoid it
i used to have a horrible habit of that
and i dont want to go back down that path
it is more important that you understand what it's spit out at you there
can you tell me what it's told you to do? what is that code doing?
i told me to create the currentplayerindex
then add these methods and said "You want the index to wrap around when it goes below 0 or above the array length."
public void LeftArrow()
{
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex--;
if (currentPlayerIndex < 0)
currentPlayerIndex = playersList.Length - 1;
UpdatePlayerButtonText();
}
public void RightArrow()
{
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
currentPlayerIndex = 0;
UpdatePlayerButtonText();
}
then "This will change the playerButtonUI text to show the selected player’s ID (or name, if you prefer)."
private void UpdatePlayerButtonText()
{
if (playersList == null || playersList.Length == 0)
{
playerButtonUI.text = "No players";
return;
}
VRCPlayerApi selectedPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = $"Player ID: {selectedPlayer.playerId}";
}
then "Make sure the index resets when the player list changes"
public void FindPlayers()
{
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
currentPlayerIndex = 0; // reset index
UpdatePlayerButtonText();
}
that's right
though I don't forsee a condition where there will be no players in the instance
yeah
so all your actual displaying of UI things will be in UpdatePlayerButtonText
playersList[currentPlayerIndex] will always refer to the VRCPlayerApi that we currently want to display places
might be good to cache this at the start of the function
instead of "playerButtonUI.text = $"Player ID: {selectedPlayer.playerId}";" itll be playerButtonUI.text = selectPlayer.displayName; i assume
yep
this player actually may need to be a global variable
you'll probably want some sort of function later like SetAdmin, and it needs to know what player to set
would be very useful if a function like that could read the current value of selectedPlayer
i guess a good thing to ask now is, would it be needed to make the button text synced? or does it not matter and can change locally?
I think that would be up to you, do other players need to be able to see what player the current admin is looking at on their permissions UI?
maybe not
doesn't really matter if they can see i think
so probably isn't worth the effort to sync this part
yeah
it will make more sense to sync the actual setting of admins later
mhm
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
private int currentPlayerIndex;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
}
public void LeftArrow()
{
//Cycle back once and update the button text to match
if (playerListUI == null || playersList.Length == 0)
{
return;
}
currentPlayerIndex--;
if (currentPlayerIndex < 0)
{
currentPlayerIndex = playersList.Length - 1;
}
UpdatePlayerButtonUI();
}
public void RightArrow()
{
//Cycle forward once and update the button text to match
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
{
currentPlayerIndex = 0;
}
UpdatePlayerButtonUI();
}
public void UpdatePlayerButtonUI()
{
VRCPlayerApi selectPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = selectPlayer.displayName;
}
public void SelectPlayer()
{
//Perform the logic on the assosciated player
}
public void UpdateListUI()
{
//Update the listUIs
}
public override void OnDeserialization()
{
UpdateListUI();
}
}
this is what i got atm
well, probably worth testing this far
you should now have all the kit to cycle through the usernames of everyone in the instance
right.. now to implement and test
thank you for the help so far, ill quickly set this up
to my understanding, this is entirely local atm
since non of this needs to be synced, for the players list that is
that's right
oh yeah, i dont remember, what was the way to make the names get displayed vertically?
vertically?
uhhhh
its a bit hard to explain
there was this way where it would make the names appear like this
Player 1
Player 2
Player 3
without it, it maybe appear like this
Player 1 Player 2
Player 3
if the name is really small like Rui for example it would display 2 names on the same "line"
in the mean time imma quickly test the cycling
how are you updating the text? you can add a <br> after every player i guess https://docs.unity3d.com/Packages/com.unity.textmeshpro@4.0/manual/RichTextLineBreak.html
thanks that will probably work when i get to that part
can confirm that the button cycling is working
at least in client sim it is
im gonna check with build and test with rejoins and leaves
I was chatting claude ai about serialization and such. I want to make sure this is how it works with VRC networking? If somebody can confirm?
The OnPreSerialization and OnPostSerialization events occur only on the client that calls RequestSerialization() - they are local events that happen as part of the sending process.
Let me explain the complete network synchronization flow:
On the Sender's Side (Owner):
The owner calls RequestSerialization()
During the next network tick, their OnPreSerialization() is called
The data gets serialized and prepared for transmission
Their OnPostSerialization() is called with the result
The data is sent over the network to other clients
On the Receiver's Side (Other Players):
They receive the serialized data from the network
Their OnDeserialization() event is called
Note: Their OnPreSerialization() and OnPostSerialization() are NOT called
This is a key distinction to understand:
OnPreSerialization() and OnPostSerialization() are sender-side events only
OnDeserialization() is the receiver-side event
im not 100% sure but that looks accurate
Ye. I know AI can be mistaken on things. Just want to make sure I get the right picture.
Do you have plans to use Pre and PostSerialization? Personally ive never bothered with them
same
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
private int currentPlayerIndex;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
}
public void LeftArrow()
{
//Cycle back once and update the button text to match
if (playerListUI == null || playersList.Length == 0)
{
return;
}
currentPlayerIndex--;
if (currentPlayerIndex < 0)
{
currentPlayerIndex = playersList.Length - 1;
}
UpdatePlayerButtonUI();
}
public void RightArrow()
{
//Cycle forward once and update the button text to match
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
{
currentPlayerIndex = 0;
}
UpdatePlayerButtonUI();
}
public void UpdatePlayerButtonUI()
{
//selectedPlayer is the player that we are currently targeting based off the current index
VRCPlayerApi selectedPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = selectedPlayer.displayName + " ID: " + selectedPlayer.playerId;
}
public void SelectPlayer()
{
//Perform the logic on the assosciated player
}
public void UpdateListUI()
{
//Update the listUIs
}
public override void OnDeserialization()
{
UpdateListUI();
}
}
this is what i got atm
seems to be working for the player list buttonm
it changes to the names as expected
i added the ID since in local testing on build and test has the exact same name so you would have no way of knowing
I might in the future. I am working to understand the theory/concept of how things like that works.
this looks correct to me
what i need to do next is update the player list now based on who's in the instance, should be simple but probably isnt xd
FindPlayers() should already be doing that, because it's being called in onPlayerJoined and OnPlayerLeft
sorry, i didnt explain proper, i meant update the UI
i was provided with this example by AI
public void UpdateListUI()
{
if (playersList == null || playersList.Length == 0)
{
playerListUI.text = "No players in the room.";
return;
}
string listText = "";
for (int i = 0; i < playersList.Length; i++)
{
VRCPlayerApi player = playersList[i];
listText += player.displayName;
if (i < playersList.Length - 1)
{
listText += "\n"; // add newline except after the last name
}
}
playerListUI.text = listText;
}
i didnt keep the " {
playerListUI.text = "No players in the room.";
return;
}"
seems like it is working
testing in vrc now
since it is handled locally i didnt slap it in ondeserialzation i just slapped it in on join/leave
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
private int currentPlayerIndex;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
UpdateListUI();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
UpdateListUI();
}
public void LeftArrow()
{
//Cycle back once and update the button text to match
if (playerListUI == null || playersList.Length == 0)
{
return;
}
currentPlayerIndex--;
if (currentPlayerIndex < 0)
{
currentPlayerIndex = playersList.Length - 1;
}
UpdatePlayerButtonUI();
}
public void RightArrow()
{
//Cycle forward once and update the button text to match
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
{
currentPlayerIndex = 0;
}
UpdatePlayerButtonUI();
}
public void UpdatePlayerButtonUI()
{
//selectedPlayer is the player that we are currently targeting based off the current index
VRCPlayerApi selectedPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = selectedPlayer.displayName + " ID: " + selectedPlayer.playerId;
}
public void SelectPlayer()
{
//Perform the logic on the assosciated player
}
public void UpdateListUI()
{
string listText = "";
for (int i = 0; i < playersList.Length; i++)
{
VRCPlayerApi player = playersList[i];
listText += player.displayName;
if (i < playersList.Length - 1)
{
listText += "\n"; //Add newline except after the last name
}
}
playerListUI.text = listText;
}
public override void OnDeserialization()
{
UpdateListUI();
}
}```
although. im not sure but the list may need to become networked, or at least include some sort of work around for when we start removing/adding people to it
when we choose someone from it to become staff we will need to remove them from it
and doing that only locally doesnt make much sense i think
maybe we call a networked event each time we add/remove someone from staff that will call for the list to be updated and within that update we cross check to see if the player is already staff or not, if they are then skip them from being added to the list?
you'll "remove" them from the list by subtracting them from the admin list, which will need to be synced
the admin list needs to be synced correct?
also, when i "left" on the 2nd player client, it didnt update the text for me
i tried rejoining, nothing happened then i tried full closing the game and it still didnt update
hm
yeah you'll most likely want to sync the admins, as all players will probably need to know who the admins are, one way or another
for both the button and the list as well
yeah i got that synced with [UdonSynced]
hm, UpdateListUI should be taking care of that
here's the script
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using TMPro;
public class PlayerListHandler : UdonSharpBehaviour
{
[UdonSynced] public int[] staffList;
private VRCPlayerApi[] playersList;
private int currentPlayerIndex;
public TextMeshProUGUI staffListUI;
public TextMeshProUGUI staffButtonUI;
public TextMeshProUGUI playerListUI;
public TextMeshProUGUI playerButtonUI;
void Start()
{
}
public void FindPlayers()
{
//Whenever FindPlayers(); is called it will fill the playerList array with all players
playersList = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
VRCPlayerApi.GetPlayers(playersList);
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
UpdateListUI();
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
FindPlayers();
UpdatePlayerButtonUI();
UpdateListUI();
}
public void LeftArrow()
{
//Cycle back once and update the button text to match
if (playerListUI == null || playersList.Length == 0)
{
return;
}
currentPlayerIndex--;
if (currentPlayerIndex < 0)
{
currentPlayerIndex = playersList.Length - 1;
}
UpdatePlayerButtonUI();
}
public void RightArrow()
{
//Cycle forward once and update the button text to match
if (playersList == null || playersList.Length == 0)
return;
currentPlayerIndex++;
if (currentPlayerIndex >= playersList.Length)
{
currentPlayerIndex = 0;
}
UpdatePlayerButtonUI();
}
public void UpdatePlayerButtonUI()
{
//selectedPlayer is the player that we are currently targeting based off the current index
VRCPlayerApi selectedPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = selectedPlayer.displayName + " ID: " + selectedPlayer.playerId;
}
public void SelectPlayer()
{
//Perform the logic on the assosciated player
}
public void UpdateListUI()
{
string listText = "";
for (int i = 0; i < playersList.Length; i++)
{
VRCPlayerApi player = playersList[i];
listText += player.displayName;
if (i < playersList.Length - 1)
{
listText += "\n"; //Add newline except after the last name
}
}
playerListUI.text = listText;
}
public override void OnDeserialization()
{
UpdateListUI();
}
}```
i think i could be having a out of bounds error too
if thats the case maybe the script is crashing and thats why its not updating
ah I see what is
my guess is this line here
{
//selectedPlayer is the player that we are currently targeting based off the current index
VRCPlayerApi selectedPlayer = playersList[currentPlayerIndex];
playerButtonUI.text = selectedPlayer.displayName + " ID: " + selectedPlayer.playerId;
}```
that's it
it gets called when we leave/join
and the index is prob gone
when it gets called
so it crashes
that's right. if you have index 1 selected, and a player leaves, then playersList will get updated to only have 1 player in it (index 0). When you try and access index 1 again, it'll crash
so whenever the playersList is updated, you need to reset currentPlayerIndex
well, what function updates the playersList?
FindPlayers() seems to be the one updating the playerList array
that's right. If we reset the currentPlayerIndex there, it should avoid the issue
so how would you reset currentPlayerIndex?
setting it back to 0?
that's right
should never have a problem if anything tries to access index 0 of that array
okay, that should work but here's my other concern
what should i do about the arrows and the player select button
if i change the index back to 0
wouldn't it screw that up if it was on a different index?
then they'll keep working from 0.
those two functions don't care what currentPlayerIndex was before. All they do is modify its current value and operate from there
alright
currentPlayerIndex could be a million and those functions will still handle it
rather, they should handle it
xd
quick question
is it bad/unneeded to call "UpdatePlayerButtonUI" in on join/leave?
actually. maybe that will be resetting name back to the first index anyways?
if we didn't update it when someone left, then it could have an old player's username in the box even though they left
ahh right
setting it to 0 will display the first player in the list, yes
if you want it to "stay" on a particular player, maybe if someone else left but they didn't, it'll take a little more logic
hmmm
im having two other issues rn
so currently, when client 2 leaves it does reset the name and index back to 0 which is good
however, it doesnt actually remove them from the array, i can still change the arrow and itll show them
the other issue is the client 2 isnt being removed either from the list
well that's odd,
actually, it's possible the player is still in the list at the time of OnPlayerLeft
I can't remember the specifics of it
unless the script is crashing again or im missing something
you may need to wait 1 frame later before running the events in OnPlayerLeft
you should open your console in game and see
so should i do a sendcustomeventdelayedbyseconds?
delayedframes yes
that's ideal, no?
like i had 4 clones plus main player, it removes all the clones except one
oh ok
that works
alright ill try rq
i just realized the time- i got work very soon. after this i gotta go, is it alright if we pick this up again later tomorrow?
and thank you for your patience and helping out! it's really appreciated
ayyyy the delaybyframes worked
iv never used that before so that is huge to learn
thank you
no problemo
That is correct, onplayerleft is called one frame before the player actually leaves
Would it makes sense to put SendCustomEventDelayedFrames into a new custom event lets call it UpdateListFlagger, it checks if isUpdatingListNextFrame is false and flags it true, and then SendCustomEventDelayedFrames to do UpdateChanges.
This way you won't run the whole thing and update the list multiple times in a frame in the first frame when you join an instance. Saves lag especially when joining.
and resets it next frame
but you might need to run it in order 1, so it can flag next frame again if there is an update in the same frame
Hey @twin portal are you available?
sorta
fair, don't feel any pressure xd
ask away and either me or somebody else will assist
alright
i believe the next part of the script i need to figure out is how to move a player from the playersList to the Staff List and sync the staff list. as well having everyone update their playerList after a change has been made
sounds easy enough, you've mostly already got the foundation for it
got anything so far?
no, i just got on my pc xd
im just trying to think right now of how im gonna do it
i also need to add a condition to check if a player is already staff or not so they don't get added back
well, you've already got a system that lets you select one player from the list of all players
now you'll probably need some sort of "Make Admin" button
yeah
and
should my staffList also be a vrcplayerapi[]?
i see i have it as a int[]
no keep it as int[]; you want your staff list to have the playerIDs instead of the full VRCPlayerApis, so it will be easier to sync
for the admin list, any time you need to actually access the associated player, you'll use GetPlayerByID
right
interesting choice to use g as an iterator name
xd
so to fix that error, you'll do GetPlayerById(staffList[g])
hmm. The staffList will also need to be dynamic in some way, shape, or form wouldn't it...
just for context, the section im updating is the display part
where it shows their display name
that makes sense
yup
so in that case, what would i do if i wanted their display name instead?
you would do that
since the id will be used in different part i think'
you need to get the VRCPlayerApi to get their display name, no?
and we're storing the player's playerID in the staffList array
correct
so to convert a playerID into a VRCPlayerApi, you use getPlayerById
so i havent actually done the id storing part
i must be doing it in the wrong format then
might be working a bit out of order then XD
oops xd
wait does that actually work
i just guessed xd
yes that should be right
later on you'll probably need to add some IsValid checks here
true
so this is what we used for making the list for the players
would i be right to assume we need to do something similar for the staff list?
and we should add a check here too
i think here,
the check to ensure we dont add a staff player?
maybe a loop to check the list to see if any of the players on the list are staff or not, if they are remove them?
I actually think it might be easier to, internally, keep playersList to always have all players
oh?
but when you display the list, and when you move through the next and back buttons, it skips over any admins
yes, so it's these buttons that need to check "the currentPlayerIndex I'm on, is this player an admin?"
so i think we stick the check right before we actually change the index
ooooo you might have an opportunity here to use a pretty rarely-used type of loop.
whats that?
a do-while loop
nice
its been a hot minute since iv used it xd
a do-while loop runs its condition check after the body runs, rather than compared to a while loop that runs the check before the body
this makes sure the body runs at least once
interesting
so for your arrows, we want it to increment/decrement at least once, just to move the menu. then we check what player we're looking at; if it's an admin, loop the body again to increment once more
with.... some sort of check to make sure it isn't infinite
xd
because what if all players are admins? lol
okay lets see what we can do here
very possible
groups will likely have all their staff join first
and assign them
before inviting anyone else
anyways, im trying to think of how to frame this loop
trying to realllly think about this to understand
a quick question too, the arrow button is meant to be pressed one at a time, will doing this loop cause it to be spammed?
agh, the adminList might make more sense as a DataList. but I don't know if you want to get into that
honestly, i have very little experience with DataList but i do want to learn it
since it seems really powerful
and more capable
it's very similar to a List</> isnt it?
DataLists are essentially the answer if you need a dynamically changeable list of items
preetttyy much
then yes
i wish List would be exposed already-
so useful
i got the docs up for datalist now
wonder why they created DataList rather than just exposing List
probably some really interesting technical reason, alas
the con of using a DataList is it'll take a few more backflips to sync it. Compared to the int array being much easier
but if you wanna learn it, this would be a good time
so let's worry about actually adding a player to the list at this point, then
alright
we still probably want to store the playerIDs in the DataList
that would be here then
this is the part where we press the button in the middle < player name button >
alright that works
so this function needs to, at least:
- get that player we're selecting (the one with their name on the button)
- store their ID in the admin DataList
- sync this change with all players
gonna control x and stick it here lmao
//fix me
string staffListText = "";
for (int g = 0; g < staffList.Length; g++)
{
VRCPlayerApi staffPlayer = VRCPlayerApi.GetPlayerById(staffList[g]);
staffListText += staffPlayer.displayName;
if (g < staffList.Length - 1)
{
staffListText += "\n";
}
}
staffListUI.text = staffListText;
anyways, should we grab the info we need from the arrows or is that not needed?
well prob not since im assuming we are using the currentplayerindex?
That's the correct assumption
Arrows have done their job, and now currentPlayerIndex is the player we have selected
That's basically it
is the check needed?
Yes, because playersList is all players
its unlikely itll ever happen since the arrows will skip over staff i believe
but it doesnt hurt to have it
Right you can never be too careful
i think we should also make button UI get updated and move the index over too
like the arrow
That may make more sense to do once we sync
since that player would no longer be available
true
would it be wise to make a separate method to call for that then?
For the syncing? Hmm
I'd have to review syncing the DataList, but we need the owner to edit the data and send it out; we might just SetOwner here and request serialization all from this function
from the selectplayer?
I'm assuming we will check if they are the owner or not and grant it if needed then perform the logic
I'm assuming we would then need a method to call from onDeserialization to sync it up
The buttons/ui that is
Since the datalist is UdonSynced it'll handle itself I assume
actually...
you can't UdonSync a DataList
it's a little more tricky. But thankfully the docs have an example on how to do 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.
ideally, if you set it up right, it'll seemlike the list syncs automatically, as if it was UdonSynced
but there's a couple more steps involved
essentially, you convert the DataList into a JSON string, and strings can be synced. You sync the string, then once it arrives, convert it back into the DataList
uhh alright
let's learn this
should i be copying this example logic?
oh yeah @twin portal i wanted to say this too, if we end up getting this working nicely and it survives the tests in the real world I am gonna make a video on making this from scratch as well as provided source code so that anyone else who wants a system like this can learn from it and in the video you would be getting mentioned because you are carrying this xd
wouldn't be my first time being credited in other people's works
:P
anyways, i want to add comments for this
so that it makes sense of what they are doing
just trying to think of what i should put
that's a very good idea
(you'd think examples would have more comments in them...)
pretty much
what about the pre?"
what exactly is it doing?
//Pack up the information on the Datalist and store it on the string so it can be synced
thats my assumption
it's doing the opposite, it's converting the DataList into JSON and storing it in the string
Legos is the goat mhm
👀
comments need to be a thing more often
helps people who have no idea what they are looking at or people for forget (me)
xd
I use comments religiously
my code is usually like half comments
Taught a friend to do it and he thanked me because it makes his life so much easier lol
yep, and you don't even really need to check if the local player is already the owner first. You just SetOwner and it'll steal it from whoever is currently the owner, current player or otherwise
true
i just have a habit
of doing that lol
this guy would be next i believe
this would be the other middle button
right button is our add player to staff button
left is remove player from staff button
so you'll essentially need another Next and PreviousArrow buttons, but for the staffList instead
but the biggest difference being that this is a DataList instead of an array
well. if you wanted, you could just ToArray the DataList and then the stuff will basically be the same
oh?
what should i do?
i think we will be doing something similar anyways for that thing we need to fix
so you'll still need an index that you're currently looking at
but to access the elements of the list, you'd use TryGetValue
https://creators.vrchat.com/worlds/udon/data-containers/data-lists/#trygetvalue
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.
right, it's a little weird, but see the example
so TryGetValue is a function that returns a bool if getting the value was successful or not
so most of the time, functions like this, you put in an if statement
in the example where the "0" is, that's the current index we'd want to look at
then you may need to know a little bit about DataTokens
https://creators.vrchat.com/worlds/udon/data-containers/data-tokens/
Data Tokens store data. Each token stores one and only one variable. Data Tokens are used in Data Dictionaries and Data Lists.
our DataList doesn't store our data directly, it stores it in what's called a DataToken. Most of the time, you get this Token, and then just get the value stored within it
alright
the "value" variable in this example is our DataToken. To get its value as an Int, you'd use value.Int
mhm
and then this would be our playerID! which you'd just GetPlayerById as normal.
right (brain hurts)
so
in our previous method
we are cycling through
in our new method
i assume we are cycling through as well so that we can grab the player id through the other method?
for context
im trying to do the staff arrow
but i think you were trying to help me with the remove buttonn
xd my bad
lol
so instead of 0 in TryGetValue, that's our index, so currentStaffIndex would go there
i knew it!
xd
anyways
llemme try the remove button
im so close
just
messing it up a bit
is this correct?
you don't need to cast value, it should have value.Int as a property
this should work, under ideal conditions
ideal conditions?
if for some reason there is no value at currentStaffIndex, the DataToken will contain an error instead of the value expected
would 0 count as no value?
no, index 0 would be the first element in the DataList
believe so
so would it be wise to add a check?
chances are the staff list is gonna empty in a lot of instances
that's why you want to wrap it in an if statement like the example does
kinda
kinda?
you're still getting Value.Int, even if it were to fail lol
uhh
so, if we try and get a value from the DataList, and it ends up failing to find a value at the specified index, what could have possibly happened?
i thought i was being smart but i guess not
bit tricky to think about
uhhh player left
and we didnt reset it?
(the index)
could happen! staffList doesn't yet remove an admin that left, does it?
no it doesnt
we reset our index here
and this is called when players join/leave
so.. im assuming we need to do somethign similar ?
when a player leaves we need to see if they are staff and if they are remove them reset the index?
that's right
but then there was one condition that you mentioned earlier. What if there are no admins?
so... you'd need some sort of indicator to tell when the admin list is empty. Otherwise, even if we reset the currentStaffIndex to 0, it'll still try to access element 0 and fail
I've got some ideas but I'll let you keep asking
but curious, if there are no admins, then who or how does someone get added to the admin list? Maybe by default, if the list is empty, the Master gets added as an admin? kind of up to how you want it
no, it wouldn't assign anyone by default since its meant to be a patreon thing so only people with access will be able to become the first (ill have to sort that out later)
that probably makes things easier
like i want it to be a thing where if there is no staff and you are a patreon you can make yourself a staff
however if there is already staff it wont let you do it
ok, so for future work, it'll be really useful to know if the list is completely empty or not
you could add some checks with DataList.Count, and if it's zero, then you know it's empty
while we were talking i added this but im concerned that it will fail without the delay
but back on topic of what were doing
i need to fix this one
so for this, you probably don't want to be removing the player unless you've found them successfully, no?
so the section that removes the player, you probably want to move it to the "successful token" partof TryGetValue
along with getting the playerID from the token
yep. and maybe even the RequestSerialization as well, since we don't need to request a sync unless we've actually changed something, right?
that should work a bit better
great
now, is it time to move on to the arrows?
this is for the player list
but since we are using a datalist
it will need to be different right?
i believe we were in the middle of attempting a do while loop but decided to switch things up to the datalist
so we didnt actually finish it
right
that function will work mostly the same, just with the DataList functions like Count instead of Length
right i got that part down
now im trying to figure out how to grab the players name
like this guy
i think i need to grab the token again
trying to think of how im gonna get the display name of the currently selected player id
maybe i need to grab the player id through the token then maybe do some kind of loop through all the players in the instance and if their id matches the token value then i grab their display name and update the ui with it?
no idea if that would work xd
I think you'll do DataToken value -> Int -> GetPlayerById -> VRCPlayerApi.displayName
this would save you from looping the entire player list
yep
ayy
though you store value.Int in staffPlayerID... but use value.Int in GetPlayerById anyway lol
oops
alright lets do a bit of review
these should be good
oh
gotta fix
this one
there we go
forgot to chabge it to staffindex
button ui should be good now
add and remove looking good
now some other topics of concern
- we need to fix that issue in the UpdateListUI for the stafflist
- do we need to reset the staffindex
- for both the player and staff button we need to cycle it forward or back once we add/remove someone and sync it
//fix me
string staffListText = "";
for (int g = 0; g < staffList.Length; g++)
{
VRCPlayerApi staffPlayer = VRCPlayerApi.GetPlayerById(staffList[g]);
staffListText += staffPlayer.displayName;
if (g < staffList.Length - 1)
{
staffListText += "\n";
}
}
staffListUI.text = staffListText;```
this was the broken one
how does this look?
looks like it'll do something
something good i hope?
:)
just working on problem #3 rn
it broke d:
error at line 229 object not set to an instance of an object
im assuming because staff has no one correct?
that may actually be because the DataList needs to be initialized
I think all you need is, when you first declared the variable, just set it to = new DataList();
yep
weird the example doesn't initialize it but I'm pretty sure you need to do that
lets see what happens
oh and i send a networked event for these guys whenever we add or remove someone
huh..
some weird stuff happening
that's awesome
i saw a lot of failed to retrieve token
so from this test
the script is failing to skip over admins but it looks like i didnt even add that logic lol
but
it does manage to add them to the staff list
but the staff list ui doesnt update
aside of the button
i can remove all remote players but i cant remove myself, itll say failed to retrieve token
oh isee
it may help to change the Debug.Log text from "Failed to retrieve token!" to just value.ToString()
since it failed, this will be an Error token, and doing ToString() on it will output the error description
it wont let me remove everyone it forces me to keep one
alright
yeah
you can do DataList.Clear() to remove all values from a DataList
alright added the logs
where and under what context should i do that
doing a quick test
to see what those logs produce
well, if it forces you to keep one value, but you're trying to remove it, you can check if the Count is 1, and instead run Clear() instead of trying to remove it specifically
ah
this pops up on start up
when i spawn a remote player another one pops up
okay here is some info
i can add both myself and remote player
but it seems to break a bit when i try to remove both
okay so i added both
i can remove myself then when i try to remove last one it gets out of range error
but it still succeeded in removing it
interesting
because i can add it back
so that's when it's fully empty, the Index is 0 of an empty list, so it's out of range
right
which we foresaw before
so we need a safety check to prevent actions when it is empty correct
yep, we shouldn't be trying to get any values if the list is completely empty
so should i do an if stafflist.count == 0 return?
could do, maybe additionally make it say "Empty" or "no players in list" when it's empty
so that + return?
yep, or wrap the whole current function into an if statement
if staffList.count !=0 -> do normal stuff
else -> display it empty
ah
return works but it's not a good habit to have, imo
why is that?
some would disagree, but I find it makes the code a bit more unreadable; additionally, return, break, and continue are often used by those starting out learning to code as a shortcut, when they should really be writing proper logic for it to work without needing these statements
my earliest coding stuff used try-catch to wait for a specific error to happen, then continue the code. it was bad
that's a hot take 
I'm sure there's weird ways you could use those return, break and continue but that doesn't mean the function itself is bad. Many of the best examples of clean code practices utilize them to great effect. Guard clauses are a good example
definitely
certainly not saying not to use them, but when used poorly it can make things flow in an odd way, and in some cases as I've worked on it makes it harder to modify later
I'll take a dozen guard clauses over a dozen nested if's any day
like you could have a while(true) loop, and just run it forever, until a certain condition you check for happens, then you do return or break or whatever. but wouldn't it be easier to have this condition be the loop's condition instead?
guard clauses are a good example of return.... but I tend not to do them because I actually like having the nested ifs lol
That's a valid point but only true if you can calculate the condition within a small space. Sometimes you put stuff inside a loop which requires multiple operations and conditions and then returns or breaks or continues. It's not necessarily a good thing to just scrunch all of that into the loop condition
many ways to fry an egg
here's an example: if I'm iterating through two different arrays and want to ensure that my script doesn't crash, you could break if i is out of bounds of the second one, or you can just build the condition into the for loop. For that specific case I'd absolutely agree with you that break is not the best way to fry that egg - but that's not an indicator of break being bad, just that this is a poor use case of it
yeah that's a better way to put it. They are not bad, they can just be used poorly
well, this is all the time i have for today we made huge progress. There is some issues that need sorting out but thats a next time thing
issues i spotted
- We need a way to skip over players who are staff when using the player button arrows
- The staff list UI pastes the same player name over and over again when adding new staff instead of the added players name
- I think there is some UI issues but ill dive deeper into that tomorrow
Thank you for the help again @twin portal i think we are very close
no problemo 👍
Ive been reading the docs and it says that vrchat prioritizes network on objects that can be seen by the player. What does this mean for objects that contain network data or activity, but do not have a mesh renderer? do they lose priority?
Can you link to the page where it says this?
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
wish their docs had git-like public change history, cause the first time seeing this part for sure.
I've been combing over all of them to try to explain the odd bugs I've been having and this stuck out to me as at least 10 networked scripts in my game do not have mesh renderers and exist purely to save bandwidth so I can sync data in groups rather then the entire game each time.
Most likely cause is I've hit a limitation or bug in udon that's not comenly know. But I'm not done ruling other reasons out.
I think I'm going to just add mesh renders to all my objects and see if there's an impact.
that will cause a huge overhead. since each mesh render causes drawcalls. and i can tell you that there is zero difference in something that has a mesh render or not in terms of networking. i use several objects that are not shown. aka no mesh render nothing. and they are all the time loaded. obviously they are also always doing something
also note that is only says Udon periodically checks the visibility of all mesh renderer children of synchronized objects
aka only objects that have a Object Sync on them.
Not nessisary, in my case the scripts are reporting data sent but clients are not getting the data and I'm trying to rule out weather or not it's beacuse of the above.
The seemingly random occurance would support it
But i am just taking shots in the dark as I don't have any crash logs or reports of any errors from udon
Its odd the 2 days ago I played 20 consecutive games with several players no issues last night by the 2and round the bug was constant
yes it will... each Mesh render causes extra load. thats just how it works.
I'm not referring to the load
And if it turns out to be the reason the the extra load is just a cost I'll have to eat
what exactly are you trying to do that that makes you concerned of prioity?
Its not what I'm doing it's what udon is doing. Its a very un explainable bug with no hard steps to reproduce and no known consistent trigger conditions so I'm systematically ruling out any possible cause. And so far I've brought it down from an 80 percent occurance rate down to somtimes 0 percent for those reasons I think my line of thinking will eventually lead to a solution. This is just the next item on the list to check.
I'm planning on remaking the game with a new design all together. But I'd like to learn why the current one fails first
And udon reporting a failed sync as successful and not clogged is just interesting to me
what kind of failed sync? manual or on cont?
Manual
Its not reporting that it's failing but clients are not getting the data or getting events from the serlization
Another interesting factor is that the client who takes ownership of the main game (not the bugged object)
Has an impact on weather it happens or not.
But I've ruled out instance master being an issue i thought it was at first but logs have scince proved otherwise
I've alao ruled out high ping got an aussie friend help me with that one
Also specifically in local offline testing. If the first client window to finish loading. Is the one to take owner ship of the main game. The bug will never happen. That's the only constant I have. And why i initially suspected some kinda ownership issue related to master
as in not getting the events what do you mean? does it not trigger on Deserialize? or does it trigger it?
Correct remote clients do not fire the on deserilization event at all. I'm addition to not getting the data despite on post serilzation reporting sucsess along with correct bytes sent. On the sending client
and in the mean time do you change ownership?
and have you actually checked if it fires it ? by just putting in a Debug.log into it?
Never only on game start every object has a specified owner
right but the first person that joins will always get ownership of all objects
Yup and then the first time a client interacts with the one object assigned to them they will get ownership of it
right. make sure that they actually have the ownership before you start doing anything with it.
Logs say they do every time
aka wait til its confirmed on the client & Previous owner
logs as in what logs? some you made yourself? or are you just watching when ownership changes aka the number that says who owns it?
I've allready ruled out ownership issues. Even made a script to pre emptivly flag objects that don't have the right owner far ahead of time it never trips
Both
and those objects do they have any udonsynced things in them?
and it does not Trigger OnDeserialize to other people what so ever? even if the object never changed owner etc?
If the bug is active yup but most of the time the scripts all work exactly as intended this bug is seemingly random
im curious to know if it only happens to objects that changed ownership recently.
But once it happens one time it will continue indefinitely unless the game is reset
it diff feels like something on your end that causes it. but thats just me shooting in the dark.
have you tried to replicate it in a fresh new project?
Well out of 100 tests or so there's only been one time it's not happened out of the gate
and test with very simple things?
So it's possible that something in the start sequence is the trigger
But I have an older version of the game with an identical start sequence that does not have the bug
that uses a previous version of the SDK?
There both on the same version
right.
soo the issues are likely rooted in how you are handling some things.
if your older version does not have the issue
The main change was a ui change also the old code was a spegatii mess of things not being factored properly
And it's odd that that's the version that works
oh and udon sharp or graph?
Udon sharp
okay so just to go over it again tell lme step by step how does it happen? what do you do to trigger it?
All I did was refactor the code pack the functionality into relatable functions
Haven't figured that one out if I invite you to the world we might play all day and never see it. We might also not be able to finish a single game beacuse of it
right.
But it happens a he'll of a lot more often in offline testing
the offline testing isnt really replicateable to online as it never does seem to do the same.
so just to get it right. Does it happen more if a object changed ownership or does it happen more if a object has not changed?
It does not seem related to ownership change. Ownership changes only occur once per game and I've seen it happens suddenly 40 moves into the game
Reseting the game can somtimes clear the bug
But usually the bug will keep repeating unless a different client becomes host
Usually if let's say client a was host the bug happens for everyone except a
Then client b resets the game and owns it the bug will happen for no one
But if client a starts the game next time the bug will be back again so it seems detriministic on who owns the game
But most of the time it just doesn't happen at all
it might be something in the host that causes the issue. if changing host can fix it.
or sometimes can
Yea i just don't know what host attribute is the cause
And why a very similar version of the game is not affected
And it's only a single function of the game affected
All other 80 network scripts including the other instances of the bugged one continue to work
well you just gotta debug everything the host does then. and see if the expected things happens or not.
Yup been at it for 2 weeks now never had the same trigger conditions more then a few times
And it's hard to test for beacuse of how rare it's become
But considering that users have a chance to have it constantly happen on an random day I don't consider it acceptable to leave it in
I should note changed i made to improve the issue had nothing to do with networking witch i can't explain
well thats kinda just how annoying it is to use Udon because its hard to really create proper test and have the data at hand. cause well we cant really ask every person to submit a bug report when it happens
Like I did things like re route the code path. Search for object with a loop rather then using a direct reference etc
Things like that I made dozens of macro changes that have an impact and I still haven't ruled out the execution path starting on a prefab
Beacuse I've had very funny things happen with prefabs in the past
Things like the gc deciding to recycle stuff still in scope.
GC will never do that.
I can send you a project file that says otherwise when I get home
You can check with the frame debugger
The fix is the force the value onto the heap where the gc can't get at it
then it wasn't in scope. or actively used.
The gc ate it the same frame it was allocated
Verified with frame debugger
You can see the value appear then go null after the gc call
that would only ever happen if it was deemed as a object that is no longer being used.
otherwise that is a pretty major Udon bug.
Yea.. it is but it's easier to work around then what I'm dealing with now
Once I figured out the values are safe on the heap I just kept all the data in size 1 arrays
also are you potentially talking about Temporary objects? not fixed like on Variables?
Nope just prefab that were spawned with scripts on fhem
The scripts would lose al data not stored as a pointer
right but how? because if you dont use a Variable it will always be considered a Temp value.
But in this new project the prefabs are free placed
And ofc I'm not getting the gc issues on this project
I've just noted that I've confirmed udon has bugs relating to prefabs in the past and I haven't ruled that out yet
For this particular project
I tried to use it right away and it was allready null
depends on how you instantiate them. and if you store the reference to them or not.
right. could you tell me how your code looked?
I can send you the whole project later
All you have to do is replace the arrays with normal declarations and it will happen
i assume you did something like GameObject temp = instantiate(prefab) etc?
Yup again mind you when you get the project it will allready be fixed so you'll hace to break it again but it's not hard to do
Its on my PC at home and I'm still several hours before I'm home again for the day
What's the correct way to move an object in a single frame without it tweening it between the two positions for other users?
yea just fyi doing it that way only temporary assigns them. but then again would not surprise me if udon has issues there.
Just modify its transform position directly with a sync variable
And have remote clients move it when they run on deserilization
That sounds like it adds a load of complexity due to possible edge cases. Can the tweening/smoothing mechanism not be disabled temporarily?
The references are kept and stored by external scripts
is this on an object using ObjectSync?
@twin portal Yes
call FlagDiscontinuity() to disable the smoothing for that frame, then do the teleport as normal
https://creators.vrchat.com/worlds/components/vrc_objectsync/
The VRC Object Sync component synchronizes the transform of a GameObject with all players in the instance. It synchronizes the object's:
But I also suspect that my issue stems from the number of scripts and the long reference chain so as sinful as it may be im thinking of packing all 3k lines of code into mostly 1 scripts just to see what happens
That's the one, thanks. Someone mentioned it ages ago but I couldn't remember what it was.
WAITT HUH TL;DR: You can now specify up to 8 parameters on custom network events sent via SendCustomNetworkEvent. All types supported by Udon Sync work. In UdonSharp, you need to use the new [NetworkCallable] attribute. Your existing Udon scripts will continue to work without any changes.
we are getting Parameters nowww?
its in the open beta
das right
heck yeah!
dam finally we can actually do alot more proper things.
wait Total outgoing data is hard-capped at approximately 18 KB/s. This includes all network overhead however, so in practice it is unlikely you will see more than 8-10 KB/s. ???? we are getting a upped limit to 18 kb/s now?
Maximum configurable rate: 100 events per second this is actually pretty nice.
i only need maybe 1-3 events per person per second.
the max is 100 but it also states that there is a global overall limit of 100 sent per second, which may change in the future
yeea but 100 a second is quite alot
Can it be used in live worlds rn?
yep
Well i might aswell try that to see if it works better before I nuke the project file again xd. Do I need to do anything special to make them usesble in my project?
you just need the beta SDK
Awesome is there any info on what the timeline is from beta to release state?
seems like it? but i would make a seperate version for Beta only. so you dont have any issues with the live one
Open betas usually last between 2-4 weeks, can be extended if still iterating on issues
actually question that 100 Events Limit is Global as in a total 100 events can be in Queue on the network correct? regardless of player?
Oh in that case I have very little reason to not early adopt as my world may take me longer then test to finish still
I've allready got a whole new project file started if anything goes wrong I'll just jump ship for the new one as allready planned my current release is allready a beta with issues anyway.
I'll upload to a new private world first so I don't downgrade what's up there tho
okay i might be dumb here but i never used the open beta how do i get it through the SDK?
you have to enable pre-release versions in whatever package management tool you use
oh yea i found it
8 params is perfect I only need 3 of those
Are we allowed to pass references to entire udon scripts?
Or just prims
only the syncable types can be used
Ah ok not a big deal
I want to be excited about the update but it takes so much work for my brain to learn when to and when not to do shit that it’ll just confuse me for months 😂
docs are pretty solid for the new stuff, I'm digging it
Mind tossing a link to the new stuff
it's in the beta notice
it also looks like there are some additional errors for the events; it'll now actually give you an error if you tried to call an event that doesn't exist (no more guessing if you spelled that event name wrong)
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.
Ty ty
There is no limit on the amount of queued messages
Uh oh, is there a way of clearing the queue if it exceeds too much? I can imagine it is very easily triggered if someone is spamming their mouse in an FPS game, but the queue gets filled up and fires at max rate per second, but the player stops clicking, but the queue is gotten so long that the gun keeps firing non-stop...
Or do we need to manually do checks to prevent the events from sending more than the max rate?
Yes that's one of the many footguns that come with the territory of reliable network events 
docs do warn
There is no limit on the amount of queued messages, so be careful not to send messages too quickly indefinitely as that will cause issues with all Udon networking in your world.
but if you use the MaxEventsPerSecond, you can restrict how many calls to this function there is per second
i guess it is just a matter of checking if NetworkCalling.GetQueuedEvents is less than the maximum, and only sends if true
btw can we get a NetworkCalling.GetMaxEventsPerSecond for easy checking? Or better yet, a NetworkCalling.GetIsQueueFull?
There is no limit on the amount of queued messages
any limit you impose is entirely up to the specific demands of your context-specific code
interesting
So if I want 60 very small 1 byte events per second in shorts Burts between 20 or so players will it be able to accommodate?
Ok another question: does sending with NetworkEventTarget.Self work if the sync mode is none? Because this can effectively be local events with parameters, isn't it? So we can potential use this in non-networked scripts? And saves some amount of variables if we need to pass some stuff between custom events? Would it make sense performance wise, or is making variables the "traditional" way faster? (in udon graph)
Just to clarify in my example 60 per second total across all 20 players not 60 per
60 events per second is a good use case for batching multiple events together
oh, with networking in vrchat we typically talk about rate limiits in terms of outbound from a single player - each player is independently rate limited. So if that's only 3 events per second per player, that's not so bad
Oh perfect the reason is, I want my game to go as fast as the players are able. And I imagine as players get experienced they will be able to do 3 to 4 moves per second each. For periods of the game.
I except 4 players per table with 10 tables in one world.
Is the goal even thinking of doing a 10 player table in the future but that might. Not be possible will have to see
But ofc it's not going to be that rate constantly but the game can't accurately predict when the windows for mass input will open
single-digit events per second shouldn't have any problem at all as long as the data is small
Yea it's going to be 1 byte to 30 bytes in worse case per request
I could make it smaller if needed with some bit shifting shenanigans
30 bytes is very reasonable for a network event. It'll probably take more than that for the overhead of the event itself anyway
Yea I'm not running into many bandwidth constraints but I'm trying to keep it as low as possible so I can have more instances of the game
the limits are per-player so your only limitation would be if one person can play multiple games at once 
I think in your specific use case, you should pay more attention to the code cleanliness than the bandwidth limitations. Network events have timestamps, use em ^^
I'm leaning towards not allowing it. To save myself some mental work but it's. Not out of the question
I'm also thinking of having the option to allow one player to opt in as the host for a table without playing in the event they Wana help out other players with poor internet
You should have ownership of the table not break if it transfers simply because that's a good networking practice and that will be more robust to sudden disconnects. But I wouldn't worry too much about specifically having that for the purpose of helping people out with bad internet.
Yea it's in the nice to have bucket but not a priority
My game could handle disconnects mid game for most clients but not if the host leaves that's beacuse the host has state info. That dosint need to be synced beacuse only the host needs to calculate it. I could fix this with just a cost of 100 bytes per second on the game
Maybe I'm being over conservative with the data
Hey @twin portal you free?
grabbing this formyself
- We need a way to skip over players who are staff when using the player button arrows
- The staff list UI pastes the same player name over and over again when adding new staff instead of the added players name
- I think there is some UI issues
wussup
working on the issues from yesterday now
im currently trying to set up a way to skip over staff on our button arrows
you want to do -> increment the index, while -> player at current index != admin
i was starting to write this out but have my doubts
right
let's see what i can do
since this isn't a loop, it will only increment once more if the current player is an admin
should i make a method for this or just use an if statement for the while part/
I'd put it in this same function
kk
I think you might have your bools backwards
give isStaff a default value
either true or false shouldn't matter since it should be changed by the loop
uuuh
i think they are right?
should isStaff be set to false, if we DID find a player that's staff?
uh
we want the loop to go forward again if the player is staff
uh
i think... i did that?
oh you're also flipping it in the loop condition
that works
this and the old one would have ran the same, but now the name of the variable makes more sense
yea you'll need a bit more actually
this should be better
?
I sent that before the images lol I'm reading it
oh
so, you probably want the bounds check before you assign selectedPlayer
right
in fact you probably don't need to assign selectedPlayer until the loop is over
ah I'm getting your two lists mixed up
this one has VRCPlayerApis, not the other that has just ints
alright it's almost there...
this will still be an infinite loop if all players are admins
f- right
okay i just got the left arrow up to same spot
now
how to see if all players are staff
hm..
might be better to do that check outside of the loop