Network Update Season & Master Transfer (Creator Feedback Wanted!)

For me it’s not 100% clear what is logic-level purpose of master in Udon code. Where knowledge ow who is instance host is useful?

In the past we used this as “admin rank” assuming master is in control in the instance. But now we have more freedom and can check IsInstanceOwner, this is good practice and I see lots of people do that novadays.

Maybe it’s good timing to review community cases where Master is used and suggest extra APIs, like figuring out instance types, group roles, etc. So, world creators and their udon code can access full info about player’s “power”.

For networking we usually GameObject-bound and refer to that GameObject-owner and its Ownership Transfer.

Maybe I am wrong, and Master is useful somewhere else besides permissions checks, but I only meet that use.

We have already thought about introducing an “OnMasterSwitched” (or similar) event however, as well as an explicit “GetMaster” function (probably at that point also “GetInstanceOwner”).

That would be great.

1 Like

It could be useful for world authors to list a set of preferred hosts, for keeping security systems well… secure.

This is extremely good point I want to grab some attention for.
In Russian community we always have some… problematic users… and for security reasons it would be nice to have some kind of priority queue for being network hosts. Like marking some users of some groups as “trusted” using role, or something like that. As well as “untrusted”. Both, for specific worlds and for groups-instances.

I believe this can help very much. As I know lots of “hacks” rely on master abilities in photon code and “master hacks” was always capable of doing most insane glitches and breaks.

2 Likes

Whatever fix or change is implemented, it should come with some opinionated documentation and examples of how to implement networking logic in the first place. Any change made in the future has to deal with the inertia of documentation and resources made in the past, which includes existing scripts and even the public user-facing interfaces of various VRC systems.

Mostly saying so because the overall vibe I’m getting here is “nobody should really have been relying on the old master logic anyway”, but both the way the old logic worked and didn’t work has been fairly codified into various systems and common prefabs, and the “instance master as admin” logic has essentially been commonplace since before even Udon.

5 Likes

I’m glad to see this being addressed. The master serving as a “host” and potentially having an unreliable connection has always been a pet peeve of mine with VRC networking.

I typically have a preference for IsMaster rather than IsOwner for game logic for two reasons:

  1. It ensures that the same person is handling the core game logic, which in turn reduces network overhead. (i.e. There isn’t a bunch of delay waiting for different people to sync the scripts they’re “in charge” of.)
  2. I worry that using IsOwner makes things more susceptible to abuse by clients. This worry is largely unfounded. :man_shrugging:

I always make sure to code systems in a way that it should never matter when the master changes. Personal opinion here: Anyone whose code will break because the master changed without them leaving deserves to have their code broken.

VRChat networking is still in a pretty depressing state so I’m glad to see progress (potentially) being made!

1 Like

ClientSim won’t be enough here - we need to test with multiple copies of the world running to see how things might desync. Having the ability to force a master change at a very specific time will be critical here - particularly if this normally doesn’t happen on PC.

I’ll also note that older worlds might not be able to handle this - think of all the worlds where only master can start a new game, for example. Having a world tag that can be set to disable this behavior will be important as well.

3 Likes

It’s really difficult to tell how much the master changes would affect the current worlds. I personally don’t use IsMaster checks, and rely on ownership instead, so I don’t think that would make a big difference to me.

However, Cyan’s Object Pool relies on it and is used by some of the bigger worlds. So I definitely think you should look into that, considering how much player dedicated objects help us manage complexity. Especially if your goal is to help people with general stability and maybe even ease of use.

I would love to see some kind of baked-in solution, or at least some supporting API to implement such a system without covering a million edge cases yourself.

Okay, update time!

Thank you for all the comments and engagement! It’s been very helpful for our decision making so far, and we appreciate everyone that took the time to respond so far.

Based on your feedback and internal discussion, we will not ship the full master transfer behaviour for now, and only reconsider once we have a clear path for a transition that would not affect prefab authors disproportionately.

Initially we thought the upsides of such a system (much lower chance of mobile players affecting instance stability) were great enough to outweigh the downside of affecting some existing content or prefabs. The feedback we’ve received shows that especially prefab maintainers disagree with this due to the scope of breakage and increased complexity, even if a toggleable solution were to be implemented.

Let’s continue the conversation on some other aspects though!

New APIs

We are considering adding the following APIs to Udon scripts to allow for code to be written more agnostic to such behaviour in the future:

  • OnPlayerSuspended(VRCPlayerApi remotePlayer)
  • OnMasterTransferred(VRCPlayerApi previousMaster, VRCPlayerApi newMaster)
  • VRCPlayerApi Networking.GetMaster()
  • VRCPlayerApi Networking.GetInstanceOwner()

Names not final - I made them up on the spot to communicate the intent :)

The idea here is to give creators the tools to handle such situations themselves, but not break any existing content. The burden of a proper fix would still be on you, but the implementation becomes optional.

Timeouts

As mentioned before, we are looking at fixing and improving our existing network timeouts too. Our current design plan differs a bit from what we had before:

  • 1 minute timeout after suspend for master client
  • 3 minutes for any non-master player

This helps mitigate the issue to some extent by kicking unresponsive master players faster than others.

Just to be clear, these are also subject to change, and in no way should you be relying on these numbers in your scripts :) They are communicated here solely for feedback gathering.

Intelligent Master Selection

To quote ourselves from the original post:

  • We’re considering changing [master selection] separately. When the current master leaves, the new could instead chosen based on other factors (for example, preferring PC clients or choosing players with a stable internet connection).
  • We believe the potential of breaking content with just this change is low enough that it could be worth releasing it [even without master transfer].

Thoughts specifically on that? The only breakage here would be if scripts rely specifically on master having the lowest ID, or always switching to the oldest player. This new behaviour would still only trigger when the current master leaves for good.

As before, this is obviously a balancing act. The upside of this change could be an improved game experience in a wide variety of worlds, and a reduced chance for the original (completely instance breaking!) bug to occur.

5 Likes

Really love this idea, and I agree that the amount of broken worlds/prefabs by this should be minimal.
One additional note to this is when considering who the new master should be, the time in the instance should also be considered to a degree. For example, someone who just joined 15 seconds ago should not be able to be master (assuming there’s enough players in the world) this would likely break worlds where Late-Syncing takes longer than 1 “network frame”.

1 Like

I feel like a few examples of use would be helpful. Clearly we know when OnPlayerSuspended would be called but what sort of action could be taken? I know it would depend upon what the world is doing and who the player is BUT is “suspended” a property? Can player objects be checked for suspension? Can suspension be set (or cleared) in code?

I’m not sure about GetMaster() I like the idea of having a method but we can determine the master if we loop through the players right? And of course we could check for other properties as well?

It was my understanding that the InstanceOwner doesn’t have to be present in the world so do we get a player object for someone in another world?

Is there a chance that OnMasterTransferred gets called a second time while processing the first one? In other words newMaster is no longer the newMaster? And both players might be invalid I assume if the players bailed about the the same time as the transfer occurs.

I believe the impact of the intelligent switching would be very minor at great benefit. The assumption that the master can switch implicitly already exists. WHO it switches to is entirely determined in the blackbox. Making that technical decision process more ‘smart’ would be a good boon for general stability.

I’d like to see the heuristics decisions for “choosing the master” specifically documented for people to refer to and have general knowledge about, but that’s certainly up to the team on how much transparency they want for the networking blackbox. It’d be good to have for the high skill creators when they are doing deep investigations and complex systems design.

1 Like

GetMaster makes it so much easier though and removes any performance overhead of iterating through the full player list.

It would likely just return Null if the InstanceOwner wasn’t in the instance anymore, this way it could also serve as an easy check for InstanceOwner’s presence in the current instance.

What @miner28_3 said, basically. The two “Get” methods would be simple helpers that have been requested often.

OnPlayerSuspended would trigger once when a player minimizes the app on their phone or takes off their Quest headset. It would trigger on remote players, as on the local one it wouldn’t make too much sense - that device is now asleep after all. You could use this to claim ownership for objects that user had previously owner, or on a less technical and more practical example, kick them from an ongoing game for being AFK, or mark them as such.

OnMasterTransferred would likely have the same guarantees as the existing OnOwnershipTransferred.

I’m mostly spitballing here - we decided on these based partially on the feedback from this post so far, so we don’t have any full specs available yet. They would be documented when they get implemented of course.

Oh and this might be an example of a “pre-event event”. Would OnSuspending be useful? If it was cancellable then each individual world builder can control the timeout or conditions.

I am really loving this! This sound amazing for game creators for sure! Now I wish there was a way to figure out when a PC user gets up from their chair :laughing:

Sounds good. I’m trying to visualize what we have to work with theoretically of course. So if we get an array of players is the suspended one in there? Can we check a property? I assume there is an unsuspend or what happens when the player puts the headset back on?

I’m not expecting them to be restored just asking whether there is any notification or ability to determine status.

We are talking about a hardware suspend here, e.g. a user physically pressing the power button on their headset. Can’t cancel that, I’m afraid :smile:

Very good questions! Given we have the information available now, I don’t see why we couldn’t give an unsuspend event or a property on the VRCPlayerApi object too.

And… assuming that a suspended player is no longer communicating does this mean that SendCustomNetworkEvent wouldn’t affect suspended players? This would be checked automatically even if we specify NetworkEventTarget.All?

And no synced variables are synced right? So do they need an OnDeserialization call if/when they are unsuspended?

What will happen to the suspended player?

My understanding is as follows. Is it correct?

  • If the system detects the transition to the suspended mode, it calls OnPlayerSuspended on the remote player’s side. (Not calls on the player side because it is immediately suspended.)

  • While in the suspended mode, Udon programs do not run entirely. For the remote players’ side, it is an unreachable client.)

  • If the timeout time elapsed while in suspended mode, the system kicks the user and calls OnPlayerLeft. The system also calls OnMasterTransferred if the user is the master.

  • If the player resumes before timeout, nothing happens.

Assuming it is correct,

  • We may need another callback for the resume.

  • On the player side, what happens to the reliable events during the suspension? Are they queued, and will they be sent after all?

Out of curiosity, what occurs for a player who’s alone in their own session? I assume with no one else to transfer to it remains with the current master?

Good point! We might reuse the same event and pass in a state variable for that.

No change in behaviour for suspended players to how it behaves on live. That is, during suspension, events will be queued, and then received and processed as updates after unsuspend, unless you’ve been kicked for idling in the meantime.

The timeout will still apply if you’re alone with the same timings, master transfer only occurs on leave (since we decided against spontaneous master transfer).

1 Like