Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions text/0036-client-network-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Client Network Variables
[feature]: #feature

- Start Date: `2021-12-12`
- RFC PR: [#0000](https://github.com/Unity-Technologies/com.unity.multiplayer.rfcs/pull/0000)
- SDK PR: [#0000](https://github.com/Unity-Technologies/com.unity.multiplayer.mlapi/pull/0000)

# Summary
[summary]: #summary

Today, on the server may write to `NetworkVariables`. This RFC introduces a bifurcation where in addition to the existing (server) NetworkVariables, we now have ClientNetworkVariables, which clients may write to.

# Motivation
[motivation]: #motivation

To enable users to more simply communicate to the server (especially input) without having to use RPCs, this feature is proposed. It is an extension of the existing `NetworkVariables` differing mainly in that clients can write to them.

# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

Today, one adds a (server) `NetworkVariable` by declaring one in a `NetworkBehaviour` like so:

```
public class NetworkVariableTest : NetworkBehaviour
{
public NetworkVariable<int> SomeSeverVar = new NetworkVariable<int>();

// ...

void Update()
{
if (NetworkManager.isServer)
{
SomeServerVar.Value = 123; // OK
}

if (NetworkManager.isClient)
{
// InvalidOperationException: Client can't write to NetworkVariables
// SomeServerVar.Value = 456; // ERROR
}

}
}
```

Note, writing to the `SomeServerVar` in the server case succeeds (and is replicated to all clients), but fails with indicated exception in the comment.

This RFC proposes a dedicated `ClientNetworkVariable` type that will work like this:

```
public class NetworkVariableTest : NetworkBehaviour
{
public NetworkVariable<int> SomeSeverVar = new NetworkVariable<int>();
public ClientNetworkVariable<int> SomeClientVar = new ClientNetworkVariable<int>();

// ...

void Update()
{
if (NetworkManager.isServer)
{
SomeServerVar.Value = 123; // OK
SomeClientVar.Value = 456; // ERROR!
}

if (NetworkManager.isClient)
{
SomeServerVar.Value = 123; // ERROR!
SomeClientVar.Value = 456; // OK
}

}
}
```

When the `SomeClientVar` code is executed...
- The `SomeClientVar` for the client on which the code ran is immediately udpated
- The client then communicates this variable update change to the server
- The server then broadcasts this to the other clients
- Note, unlike pre-1.0 versions of NGO when clients could write to network variables, the server can **not** write to client network variables.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about hosts? They are clients, they should be able to write to ClientNetvars too.
Any reasons we're not doing an Owner check and no client/server check? This way, a server that owns a netvar could write to the same netvar a client would if they were the owner.
With the proposed design, in boss room we'd have to have two sets of variables for each state and duplicate our gameplay state assignment everywhere :(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, good catch on host stuff, I need to double check the behavior. The intention is the client who is the host shouldn't be special.


## Private Mode

So that developers can choose to replicate the `ClientNetworkVariable` just on the server but **not** to the other clients, a `OwnerOnly` mode is avaiable.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering, couldn't this use the interest system instead of having its own system? This way, we'd be able to set custom replication rules (for example, only broadcast to team members or only broadcast to steam friends)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very interesting. The thinking behind it was, ClientNetvars are a distinct, special type. A CNV will not be snapshotted. A client can never write to a regular NV. A CNV can be private to the owning client. We could, rather than make it its own type, have attributes on a regular NV, and then those attributes could be read / processed by the interest system. I guess the spirit was to really keep CNV and NV as distinct types so as to not imply to the user that CNV's are treated by the SDK just like NV's (mainly, that they aren't snapshotted / predicted). But I'm open to more feedback

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah interesting. Yes having attributes on a regular NV, with no new CNV class, read by interest management would indeed make this a cohesive solution
Will CNVs "never" be snapshotted? I thought Jeff had plans to allow this?


For example, one might want to use the `ClientNetworkVariable` to transmit a "cast spell" input command to just the server (vs. today where an RPC is required) so that it can react to the input and then update the other clients accordingly (e.g., update the clients differently whether the user actually has manna or not). To do this, one declares the `ClientNetworkVariable` with the `OwnerOnly` permission, e.g.


```
public class NetworkVariableTest : NetworkBehaviour
{
public ClientNetworkVariable<int> SomePrivateClientVar =
new ClientNetworkVariable<int>(NetworkVariableReadPermission.OwnerOnly);

// ...

void Update()
{
if (NetworkManager.isClient)
{
// only this server and the sending client will know about this
SomePrivateClientVar.Value = 456;
}

}
}
```

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation


# Drawbacks
[drawbacks]: #drawbacks

Why might we *not* do this?
See the "Future possibilities" for a more detailed explanation, but we likely will need to change how this system works when moving on to a snapshotted model in future major releases. In the current system there is no canonical "on tick 123 these variables had values {...}", and so having the client broadcast values at some arbitrary time to be received by clients at some aribitrary time is not disriptive. As a result users used to this model may have to adapt to something different in the next major release.

# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

This feature is a building block one can use for client-authoritative features. However it intentionally leaves the "local manifestation" element to the user. That is, while a user can define the state and the SDK will sync it, it is up to the user whether to take that `ClientNetworkVariables` state and manifest it, say, in a character's position OR transmit it as a command to the server and wait for the server's updates.

In considering the feature and talking with developers the common feedback was this was the level of involvement they wanted from the SDK. Moreover, while we felt reasonably confident that we could define variable use case, we were less certain we could define a full client authority system with local manifestation given all the different use cases. Note however such additions could be added later, building on this system.

Technial advantages of this approach:
- programmer interface is just like before
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, we had the same netvar for both server and client, but with permissions. People porting to Netcode 1.x from 0.1.0 would still have to create new code to use the new ClientNetworkVariable.

- we were able to fully re-use the existing implementation

If we don't adopt this approach:
- users will have to continue to use RPC's to transmit input either as a single client -> server transmission or a combination of client -> server and then server -> other clients.

# Future possibilities
[future-possibilities]: #future-possibilities

## What happens in a Server Authoritative, Snapshot-based, Eventually-consisten world?
In the current (1.0) release, network variables are all communicated in a reliable networking mode. However, in future major releases we are likely to switch to an unreliable, server-authoritative, eventually-consistent, tick-based model for Server NetworkVariables. It is less clear what we will do with `ClientNetworkVariables` however.

- we may formalize input passing with a dedicated system
- we may bifurcate, and continue to have this mechanism, however Client and Server network variables will be "unlinked"; that is, one might use ClientNetworkVariables to transmit non-gameplay-critical, non-tick-correlated datat (e.g. transmitting players' cursors to each other)