-
Notifications
You must be signed in to change notification settings - Fork 5
client network variables #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. | ||
|
||
## 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
There was a problem hiding this comment.
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 :(
There was a problem hiding this comment.
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.