#udon-networking

1 messages · Page 20 of 1

scenic sky
#

xd

strange token
#

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

strange token
#

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

scenic sky
#

to clarify is the staff array gonna be playerids ?

strange token
#

Yea that’ll be the most efficient

twin portal
#

it'll make it really easy to sync

scenic sky
#

right

strange token
#

No reason to sync strings when a single number suffices

scenic sky
#

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()
    {
        
    }
}
strange token
#

Hehe let me grab some docs

twin portal
#
var players = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];  
VRCPlayerApi.GetPlayers(players);
strange token
twin portal
#

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

strange token
#

I forgot legos was lurking

#

He’s way more efficient of a tutor lmao

#

Ping me if y’all get lost!

scenic sky
#

thank you

scenic sky
twin portal
#

yes, you will need both

scenic sky
#

should it be priv or pub?

twin portal
#

one array is all players, the other is your admin list

twin portal
#

probably just this one, no?

scenic sky
scenic sky
#

i didnt think that far ahead hmm maybe i can keep it all in one script

twin portal
#

probably best

scenic sky
#

usually i try to keep things in one script

twin portal
scenic sky
#

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 ^

twin portal
#

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

scenic sky
#

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);
        
    }
}
twin portal
#

yep. so now whenever FindPlayers() is called, it will fill the playersList array with all players

scenic sky
#

okay, now the times we want to call this, would i be right to assume on join/leave and start?

twin portal
#

just join and leave is all that's needed, since OnPlayerJoined is called when you join a world anyway

scenic sky
#

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();
    }
}
twin portal
#

that'll do

scenic sky
#

okay, so everyone is updating their own local copy of the playerList correct?

twin portal
#

pretty much

#

so then you've got a couple of things to do now

scenic sky
#

right, so now I need to figure out how to add players in now for the staffList

#

this is the harder part xd

twin portal
#

the idea of it is easy yeah...

scenic sky
#

but the execution, not so much

twin portal
#

so you'll probably need the UI setup at this point

scenic sky
#

Yeah, ill need to create two UI refs i assume for regular players in the instance and a list of who is staff correct?

twin portal
#

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?

twin portal
#

either that or, maybe your arrow idea

scenic sky
#

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

twin portal
#

could start simple with the arrows and upgrade later if you want

#

it's all gonna be mostly up to you really

scenic sky
#

true

#

so i got these guys down right now
public TextMeshProUGUI staffUI;
public TextMeshProUGUI playerUI;

twin portal
#

that will be just the text display

scenic sky
#

mhm, i believe i will need a reference to the button text now?

twin portal
#

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

scenic sky
#

sorry i meant to change the text of the button

twin portal
#

then that would actually be the TextMeshProUGUI component that's on the button

scenic sky
#

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

scenic sky
#

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();
    }
}
drowsy jackal
#

Does anyone have a video or something explaining network synchronization and such? I think I saw one being posted a while ago.

scenic sky
# drowsy jackal Does anyone have a video or something explaining network synchronization and suc...

i know this channel has a bit of content on networked stuff https://www.youtube.com/@PlayerBush001/videos

scenic sky
#

i never knew that existed

#

gonna watch shortly

#

100%

fallow mountain
#

discord exclusive (?)

scenic sky
#

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 ^^

fallow mountain
#

for loop
(idk the code i just know graph)

scenic sky
#

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

twin portal
#

yes you'll want to keep track of an index that you're currently "looking" at the array with

scenic sky
#

ai right for once? lmao

twin portal
#

it's really annoyingly written imo

scenic sky
#

how would you right it?

#

write*

twin portal
#

well... just in a way that I personally write things, future-proofed when I add things later

scenic sky
#

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 ?

twin portal
#

no, you'd just access the index directly

scenic sky
#

oh?

twin portal
#

with playerList[currentPlayerIndex]

scenic sky
#

hmm

#

and im assumign i would grab the player's id from that too?

twin portal
#

from there you'll grab anything and put it in your UI where you'd want it

scenic sky
#

how would you write this?

twin portal
#

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

scenic sky
#

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

twin portal
#

it is more important that you understand what it's spit out at you there

scenic sky
#

im just having a hard time find the info i need and understanding it

#

yeah

twin portal
#

can you tell me what it's told you to do? what is that code doing?

scenic sky
#

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();
}
twin portal
#

that's right

#

though I don't forsee a condition where there will be no players in the instance

scenic sky
#

yeah

twin portal
#

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

scenic sky
#

instead of "playerButtonUI.text = $"Player ID: {selectedPlayer.playerId}";" itll be playerButtonUI.text = selectPlayer.displayName; i assume

twin portal
#

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

scenic sky
#

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?

twin portal
#

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

scenic sky
#

doesn't really matter if they can see i think

twin portal
#

so probably isn't worth the effort to sync this part

scenic sky
#

yeah

twin portal
#

it will make more sense to sync the actual setting of admins later

scenic sky
#

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

twin portal
#

well, probably worth testing this far

#

you should now have all the kit to cycle through the usernames of everyone in the instance

scenic sky
#

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

twin portal
#

that's right

scenic sky
#

oh yeah, i dont remember, what was the way to make the names get displayed vertically?

fallow mountain
#

vertically?

scenic sky
#

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

fallow mountain
scenic sky
#

thanks that will probably work when i get to that part

scenic sky
#

at least in client sim it is

#

im gonna check with build and test with rejoins and leaves

drowsy jackal
#

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

scenic sky
#

im not 100% sure but that looks accurate

drowsy jackal
#

Ye. I know AI can be mistaken on things. Just want to make sure I get the right picture.

vapid pagoda
scenic sky
#

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

drowsy jackal
scenic sky
#

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

twin portal
#

FindPlayers() should already be doing that, because it's being called in onPlayerJoined and OnPlayerLeft

scenic sky
#

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?

twin portal
#

you'll "remove" them from the list by subtracting them from the admin list, which will need to be synced

scenic sky
#

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

twin portal
#

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

scenic sky
scenic sky
twin portal
scenic sky
#

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

twin portal
#

ah I see what is

scenic sky
#

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;
    }```
twin portal
#

that's it

scenic sky
#

it gets called when we leave/join

#

and the index is prob gone

#

when it gets called

#

so it crashes

twin portal
#

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

scenic sky
#

good idea

#

the question is, how do i do it without breaking the button?

twin portal
#

well, what function updates the playersList?

scenic sky
#

FindPlayers() seems to be the one updating the playerList array

twin portal
#

that's right. If we reset the currentPlayerIndex there, it should avoid the issue

#

so how would you reset currentPlayerIndex?

scenic sky
#

setting it back to 0?

twin portal
#

that's right

#

should never have a problem if anything tries to access index 0 of that array

scenic sky
#

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?

twin portal
#

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

scenic sky
#

alright

twin portal
#

currentPlayerIndex could be a million and those functions will still handle it

#

rather, they should handle it

scenic sky
#

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?

twin portal
#

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

scenic sky
#

ahh right

twin portal
#

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

scenic sky
#

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

twin portal
#

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

scenic sky
#

unless the script is crashing again or im missing something

twin portal
#

you may need to wait 1 frame later before running the events in OnPlayerLeft

twin portal
scenic sky
twin portal
#

delayedframes yes

scenic sky
#

oh frames instead?

#

also i tested in clientsim, it removes all but the last player

twin portal
#

that's ideal, no?

scenic sky
#

like i had 4 clones plus main player, it removes all the clones except one

twin portal
#

oh ok

scenic sky
#

as you can see here ^^

#

ill try the delay

#

im assuming like this?

twin portal
#

that works

scenic sky
#

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

twin portal
#

no problemo

hoary frost
fallow mountain
#

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

scenic sky
#

Hey @twin portal are you available?

twin portal
#

sorta

scenic sky
#

fair, don't feel any pressure xd

twin portal
#

ask away and either me or somebody else will assist

scenic sky
#

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

twin portal
#

sounds easy enough, you've mostly already got the foundation for it

#

got anything so far?

scenic sky
#

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

twin portal
#

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

scenic sky
#

yeah

#

and

#

should my staffList also be a vrcplayerapi[]?

#

i see i have it as a int[]

twin portal
#

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

scenic sky
#

ah okay

#

hmmm so updating the text logic will need to be different then\

twin portal
#

for the admin list, any time you need to actually access the associated player, you'll use GetPlayerByID

twin portal
#

interesting choice to use g as an iterator name

scenic sky
#

xd

twin portal
#

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...

scenic sky
#

where it shows their display name

twin portal
#

that makes sense

scenic sky
twin portal
#

you would do that

scenic sky
#

since the id will be used in different part i think'

twin portal
#

you need to get the VRCPlayerApi to get their display name, no?

#

and we're storing the player's playerID in the staffList array

scenic sky
#

correct

twin portal
#

so to convert a playerID into a VRCPlayerApi, you use getPlayerById

scenic sky
#

so i havent actually done the id storing part

scenic sky
twin portal
#

might be working a bit out of order then XD

scenic sky
#

wait does that actually work

#

i just guessed xd

twin portal
#

yes that should be right

scenic sky
#

(adding the VRCPlayerApi.GetPlayerById

#

alright so

#

now to get back on order

twin portal
#

later on you'll probably need to add some IsValid checks here

scenic sky
#

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?

twin portal
#

hmm maybe here

#

no matter what, GetPlayers is going to give you all players

scenic sky
#

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?

twin portal
#

I actually think it might be easier to, internally, keep playersList to always have all players

scenic sky
#

oh?

twin portal
#

but when you display the list, and when you move through the next and back buttons, it skips over any admins

scenic sky
#

ahh i see ya

#

that brings us back here

twin portal
#

yes, so it's these buttons that need to check "the currentPlayerIndex I'm on, is this player an admin?"

scenic sky
#

so i think we stick the check right before we actually change the index

twin portal
#

ooooo you might have an opportunity here to use a pretty rarely-used type of loop.

scenic sky
#

whats that?

twin portal
#

a do-while loop

scenic sky
#

ohh

#

i have noteso n that somewher

#

i made note of it like this

twin portal
#

nice

scenic sky
#

its been a hot minute since iv used it xd

twin portal
#

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

scenic sky
#

interesting

twin portal
#

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

scenic sky
#

xd

twin portal
#

because what if all players are admins? lol

scenic sky
#

okay lets see what we can do here

scenic sky
#

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?

twin portal
#

the button won't be spammed, no

#

this will all happen with just one button press

scenic sky
#

alright

#

im trying to figure out what i need to do here

#

my example helps a bit

twin portal
#

agh, the adminList might make more sense as a DataList. but I don't know if you want to get into that

scenic sky
#

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?

twin portal
#

DataLists are essentially the answer if you need a dynamically changeable list of items

twin portal
scenic sky
#

then yes

#

i wish List would be exposed already-

#

so useful

#

i got the docs up for datalist now

twin portal
#

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

scenic sky
#

ah

#

yes iw anna learn

#

alright i changed the staff list to a datalist now

twin portal
#

so let's worry about actually adding a player to the list at this point, then

scenic sky
#

alright

twin portal
#

we still probably want to store the playerIDs in the DataList

scenic sky
#

that would be here then

#

this is the part where we press the button in the middle < player name button >

twin portal
#

alright that works

#

so this function needs to, at least:

  1. get that player we're selecting (the one with their name on the button)
  2. store their ID in the admin DataList
  3. sync this change with all players
scenic sky
#

yup

#

and side note

#

gotta fix this after xd

#

but thats later

twin portal
#

yeeaaaa put a pin in that one

#

or even comment it out

scenic sky
#

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?

twin portal
#

That's the correct assumption

#

Arrows have done their job, and now currentPlayerIndex is the player we have selected

scenic sky
#

i just took a guess of what i think i needed to do?

twin portal
#

That's basically it

scenic sky
#

is the check needed?

twin portal
#

Yes, because playersList is all players

scenic sky
#

its unlikely itll ever happen since the arrows will skip over staff i believe

#

but it doesnt hurt to have it

twin portal
#

Right you can never be too careful

scenic sky
#

i think we should also make button UI get updated and move the index over too

#

like the arrow

twin portal
#

That may make more sense to do once we sync

scenic sky
#

since that player would no longer be available

#

true

#

would it be wise to make a separate method to call for that then?

twin portal
#

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

scenic sky
#

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

twin portal
#

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

#

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

scenic sky
#

oh god

#

lets see

twin portal
#

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

scenic sky
#

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

twin portal
#

wouldn't be my first time being credited in other people's works

scenic sky
#

its deserved

#

you are the goat xd

twin portal
#

:P

scenic sky
#

so that it makes sense of what they are doing

#

just trying to think of what i should put

twin portal
#

that's a very good idea
(you'd think examples would have more comments in them...)

scenic sky
#

true lmao

#

accurate or nah?

twin portal
#

pretty much

scenic sky
#

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

twin portal
#

it's doing the opposite, it's converting the DataList into JSON and storing it in the string

strange token
#

Legos is the goat mhm

scenic sky
#

:#

#

:3

strange token
#

👀

scenic sky
#

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

strange token
#

I use comments religiously

twin portal
#

my code is usually like half comments

strange token
#

Taught a friend to do it and he thanked me because it makes his life so much easier lol

scenic sky
#

alright let's review what we got so far

#

that should be correct?

twin portal
#

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

scenic sky
#

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

twin portal
#

so you'll essentially need another Next and PreviousArrow buttons, but for the staffList instead

scenic sky
#

i believe we are gonna need 2 other arrow methods and another index

#

yup

twin portal
#

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

scenic sky
#

oh?

#

what should i do?

#

i think we will be doing something similar anyways for that thing we need to fix

twin portal
#

so you'll still need an index that you're currently looking at

scenic sky
#

right

#

having a hard time formatting it

twin portal
#

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

#

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

scenic sky
#

alright

twin portal
#

the "value" variable in this example is our DataToken. To get its value as an Int, you'd use value.Int

scenic sky
#

mhm

twin portal
#

and then this would be our playerID! which you'd just GetPlayerById as normal.

scenic sky
#

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

twin portal
#

lol

#

so instead of 0 in TryGetValue, that's our index, so currentStaffIndex would go there

scenic sky
#

xd

#

anyways

#

llemme try the remove button

#

im so close

#

just

#

messing it up a bit

#

is this correct?

twin portal
#

you don't need to cast value, it should have value.Int as a property

scenic sky
twin portal
#

it's case-sensitive

#

capital I

scenic sky
#

oosp

#

rookie mistake

twin portal
#

this should work, under ideal conditions

scenic sky
#

ideal conditions?

twin portal
#

if for some reason there is no value at currentStaffIndex, the DataToken will contain an error instead of the value expected

scenic sky
#

would 0 count as no value?

twin portal
#

no, index 0 would be the first element in the DataList

scenic sky
#

right

#

so, if we have no staff

#

will it break?

twin portal
#

believe so

scenic sky
#

so would it be wise to add a check?

#

chances are the staff list is gonna empty in a lot of instances

twin portal
#

that's why you want to wrap it in an if statement like the example does

scenic sky
#

better?

twin portal
#

kinda

scenic sky
#

kinda?

twin portal
#

you're still getting Value.Int, even if it were to fail lol

scenic sky
#

uhh

twin portal
#

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?

scenic sky
#

i thought i was being smart but i guess not

twin portal
#

bit tricky to think about

scenic sky
#

and we didnt reset it?

#

(the index)

twin portal
#

could happen! staffList doesn't yet remove an admin that left, does it?

scenic sky
#

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?

twin portal
#

that's right

#

but then there was one condition that you mentioned earlier. What if there are no admins?

scenic sky
#

uhhh

#

shoot

twin portal
#

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

scenic sky
#

uhh right

#

one thing at a time

twin portal
#

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

scenic sky
#

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)

twin portal
#

that probably makes things easier

scenic sky
#

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

twin portal
#

ok, so for future work, it'll be really useful to know if the list is completely empty or not

scenic sky
#

yeah

#

right so we have a few things to get sorted out

twin portal
#

you could add some checks with DataList.Count, and if it's zero, then you know it's empty

scenic sky
#

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

twin portal
#

so for this, you probably don't want to be removing the player unless you've found them successfully, no?

scenic sky
#

correct

#

this is what i reverted it back to

twin portal
#

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

scenic sky
#

like this?

twin portal
#

yep. and maybe even the RequestSerialization as well, since we don't need to request a sync unless we've actually changed something, right?

scenic sky
#

true

twin portal
#

that should work a bit better

scenic sky
#

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

twin portal
#

right

#

that function will work mostly the same, just with the DataList functions like Count instead of Length

scenic sky
#

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

twin portal
#

I think you'll do DataToken value -> Int -> GetPlayerById -> VRCPlayerApi.displayName

#

this would save you from looping the entire player list

scenic sky
#

uhhh trying to think how i would write that

#

is this right/

twin portal
#

yep

scenic sky
#

ayy

twin portal
#

though you store value.Int in staffPlayerID... but use value.Int in GetPlayerById anyway lol

scenic sky
#

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

  1. we need to fix that issue in the UpdateListUI for the stafflist
  2. do we need to reset the staffindex
  3. 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?

twin portal
#

looks like it'll do something

scenic sky
#

something good i hope?

twin portal
#

:)

scenic sky
#

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?

twin portal
#

that may actually be because the DataList needs to be initialized

scenic sky
#

oh?

#

how would i do that/

twin portal
#

I think all you need is, when you first declared the variable, just set it to = new DataList();

scenic sky
#

apologies for my lack of understanding

#

but where?

twin portal
#

that

#

that's the variable declaration

scenic sky
#

so this?

twin portal
#

yep

scenic sky
#

huh

#

oh

#

lag

twin portal
#

weird the example doesn't initialize it but I'm pretty sure you need to do that

scenic sky
#

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

twin portal
#

that's awesome

scenic sky
#

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

twin portal
#

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

scenic sky
#

it wont let me remove everyone it forces me to keep one

twin portal
#

so Debug.LogError(value.ToString());

#

it makes you keep one? interesting

scenic sky
#

yeah

twin portal
#

you can do DataList.Clear() to remove all values from a DataList

scenic sky
#

alright added the logs

scenic sky
#

doing a quick test

#

to see what those logs produce

twin portal
#

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

scenic sky
#

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

twin portal
#

so that's when it's fully empty, the Index is 0 of an empty list, so it's out of range

scenic sky
#

right

twin portal
#

which we foresaw before

scenic sky
#

so we need a safety check to prevent actions when it is empty correct

twin portal
#

yep, we shouldn't be trying to get any values if the list is completely empty

scenic sky
#

so should i do an if stafflist.count == 0 return?

twin portal
#

could do, maybe additionally make it say "Empty" or "no players in list" when it's empty

scenic sky
#

so that + return?

twin portal
#

yep, or wrap the whole current function into an if statement

#

if staffList.count !=0 -> do normal stuff
else -> display it empty

scenic sky
#

ah

twin portal
#

return works but it's not a good habit to have, imo

scenic sky
#

why is that?

twin portal
#

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

scenic sky
#

ah i see

#

understandable

twin portal
#

my earliest coding stuff used try-catch to wait for a specific error to happen, then continue the code. it was bad

frozen igloo
#

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

twin portal
#

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

frozen igloo
#

I'll take a dozen guard clauses over a dozen nested if's any day

twin portal
#

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

frozen igloo
twin portal
#

many ways to fry an egg

frozen igloo
#

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

twin portal
#

yeah that's a better way to put it. They are not bad, they can just be used poorly

scenic sky
#

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

twin portal
#

no problemo 👍

brisk magnet
#

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?

cold laurel
brisk magnet
tulip sphinx
#

wish their docs had git-like public change history, cause the first time seeing this part for sure.

brisk magnet
# tulip sphinx wish their docs had git-like public change history, cause the first time seeing ...

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.

finite sierra
#

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.

brisk magnet
#

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

finite sierra
brisk magnet
#

And if it turns out to be the reason the the extra load is just a cost I'll have to eat

finite sierra
#

what exactly are you trying to do that that makes you concerned of prioity?

brisk magnet
#

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

finite sierra
#

what kind of failed sync? manual or on cont?

brisk magnet
#

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

finite sierra
#

as in not getting the events what do you mean? does it not trigger on Deserialize? or does it trigger it?

brisk magnet
#

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

finite sierra
#

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?

brisk magnet
#

Never only on game start every object has a specified owner

finite sierra
#

right but the first person that joins will always get ownership of all objects

brisk magnet
#

Yup and then the first time a client interacts with the one object assigned to them they will get ownership of it

finite sierra
#

right. make sure that they actually have the ownership before you start doing anything with it.

brisk magnet
#

Logs say they do every time

finite sierra
#

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?

brisk magnet
#

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

finite sierra
#

and those objects do they have any udonsynced things in them?

brisk magnet
#

Yws

#

Yes*

finite sierra
#

and it does not Trigger OnDeserialize to other people what so ever? even if the object never changed owner etc?

brisk magnet
#

If the bug is active yup but most of the time the scripts all work exactly as intended this bug is seemingly random

finite sierra
#

im curious to know if it only happens to objects that changed ownership recently.

brisk magnet
#

But once it happens one time it will continue indefinitely unless the game is reset

finite sierra
#

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?

brisk magnet
#

Well out of 100 tests or so there's only been one time it's not happened out of the gate

finite sierra
#

and test with very simple things?

brisk magnet
#

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

finite sierra
#

that uses a previous version of the SDK?

brisk magnet
#

There both on the same version

finite sierra
#

right.

#

soo the issues are likely rooted in how you are handling some things.

#

if your older version does not have the issue

brisk magnet
#

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

finite sierra
#

oh and udon sharp or graph?

brisk magnet
#

Udon sharp

finite sierra
#

okay so just to go over it again tell lme step by step how does it happen? what do you do to trigger it?

brisk magnet
#

All I did was refactor the code pack the functionality into relatable functions

brisk magnet
finite sierra
#

right.

brisk magnet
#

But it happens a he'll of a lot more often in offline testing

finite sierra
#

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?

brisk magnet
#

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

finite sierra
#

it might be something in the host that causes the issue. if changing host can fix it.

#

or sometimes can

brisk magnet
#

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

finite sierra
#

well you just gotta debug everything the host does then. and see if the expected things happens or not.

brisk magnet
#

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

finite sierra
#

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

brisk magnet
#

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.

finite sierra
#

GC will never do that.

brisk magnet
#

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

finite sierra
#

then it wasn't in scope. or actively used.

brisk magnet
#

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

finite sierra
#

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.

brisk magnet
#

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

finite sierra
#

also are you potentially talking about Temporary objects? not fixed like on Variables?

brisk magnet
#

Nope just prefab that were spawned with scripts on fhem

#

The scripts would lose al data not stored as a pointer

finite sierra
#

right but how? because if you dont use a Variable it will always be considered a Temp value.

brisk magnet
#

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

brisk magnet
finite sierra
#

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?

brisk magnet
#

I can send you the whole project later

#

All you have to do is replace the arrays with normal declarations and it will happen

finite sierra
#

i assume you did something like GameObject temp = instantiate(prefab) etc?

brisk magnet
#

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

faint rock
#

What's the correct way to move an object in a single frame without it tweening it between the two positions for other users?

finite sierra
#

yea just fyi doing it that way only temporary assigns them. but then again would not surprise me if udon has issues there.

brisk magnet
#

And have remote clients move it when they run on deserilization

faint rock
#

That sounds like it adds a load of complexity due to possible edge cases. Can the tweening/smoothing mechanism not be disabled temporarily?

brisk magnet
twin portal
faint rock
#

@twin portal Yes

twin portal
brisk magnet
#

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

faint rock
finite sierra
#

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

twin portal
#

das right

foggy jackal
finite sierra
#

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?

twin portal
#

ooo. the order they are received is also guaranteed

#

idk if that's actually new

finite sierra
#

Maximum configurable rate: 100 events per second this is actually pretty nice.

#

i only need maybe 1-3 events per person per second.

twin portal
#

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

finite sierra
#

yeea but 100 a second is quite alot

brisk magnet
twin portal
#

yep

brisk magnet
#

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?

twin portal
#

you just need the beta SDK

brisk magnet
#

Awesome is there any info on what the timeline is from beta to release state?

finite sierra
frozen igloo
finite sierra
#

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?

brisk magnet
brisk magnet
#

I'll upload to a new private world first so I don't downgrade what's up there tho

finite sierra
#

okay i might be dumb here but i never used the open beta how do i get it through the SDK?

foggy jackal
#

you have to enable pre-release versions in whatever package management tool you use

finite sierra
#

oh yea i found it

brisk magnet
#

8 params is perfect I only need 3 of those

#

Are we allowed to pass references to entire udon scripts?

#

Or just prims

twin portal
#

only the syncable types can be used

brisk magnet
#

Ah ok not a big deal

strange token
#

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 😂

twin portal
#

docs are pretty solid for the new stuff, I'm digging it

brisk magnet
foggy jackal
#

it's in the beta notice

twin portal
#

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)

twin portal
brisk magnet
#

Ty ty

fallow mountain
#

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?

frozen igloo
twin portal
#

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

fallow mountain
#

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?

frozen igloo
#

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

fallow mountain
#

interesting

brisk magnet
fallow mountain
#

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)

brisk magnet
frozen igloo
frozen igloo
brisk magnet
#

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

frozen igloo
#

single-digit events per second shouldn't have any problem at all as long as the data is small

brisk magnet
#

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

frozen igloo
#

30 bytes is very reasonable for a network event. It'll probably take more than that for the overhead of the event itself anyway

brisk magnet
#

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

frozen igloo
#

the limits are per-player so your only limitation would be if one person can play multiple games at once Laugh2

#

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 ^^

brisk magnet
#

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

frozen igloo
#

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.

brisk magnet
#

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

scenic sky
#

Hey @twin portal you free?

scenic sky
#
  • 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
twin portal
#

wussup

scenic sky
#

working on the issues from yesterday now

#

im currently trying to set up a way to skip over staff on our button arrows

twin portal
#

ah right

#

can still use a do-while loop for that

scenic sky
#

i thought about that

#

probably better than what i was writing here

twin portal
#

you want to do -> increment the index, while -> player at current index != admin

scenic sky
#

i was starting to write this out but have my doubts

scenic sky
#

let's see what i can do

twin portal
scenic sky
#

should i make a method for this or just use an if statement for the while part/

twin portal
#

I'd put it in this same function

scenic sky
#

kk

scenic sky
#

how does this look

#

shoot, i forgot the logic for the length

twin portal
#

I think you might have your bools backwards

scenic sky
#

probably

#

and it seems like i messed something up

twin portal
#

give isStaff a default value

#

either true or false shouldn't matter since it should be changed by the loop

scenic sky
#

true

#

are my bools incorrect?

twin portal
#

uuuh

scenic sky
#

i think they are right?

twin portal
#

should isStaff be set to false, if we DID find a player that's staff?

scenic sky
#

uh

#

we want the loop to go forward again if the player is staff

#

uh

#

i think... i did that?

twin portal
#

oh you're also flipping it in the loop condition

scenic sky
twin portal
#

that works

#

this and the old one would have ran the same, but now the name of the variable makes more sense

scenic sky
#

gotta fix bottom part tho

twin portal
#

yea you'll need a bit more actually

scenic sky
#

this should be better

scenic sky
twin portal
#

I sent that before the images lol I'm reading it

scenic sky
#

oh

twin portal
#

so, you probably want the bounds check before you assign selectedPlayer

scenic sky
#

right

twin portal
#

in fact you probably don't need to assign selectedPlayer until the loop is over

scenic sky
#

i thought i would need that to check for the staffList.Contains?

twin portal
#

ah I'm getting your two lists mixed up

#

this one has VRCPlayerApis, not the other that has just ints

scenic sky
#

yeah

twin portal
#

alright it's almost there...

#

this will still be an infinite loop if all players are admins

scenic sky
#

f- right

#

okay i just got the left arrow up to same spot

#

now

#

how to see if all players are staff

#

hm..

twin portal
#

might be better to do that check outside of the loop

scenic sky
#

yeah

#

maybe before the loop?