com.unity.netcode.gameobjects icon indicating copy to clipboard operation
com.unity.netcode.gameobjects copied to clipboard

OnValueChanged is not called when Ownership changes also occur

Open zachstronaut opened this issue 2 years ago • 5 comments

When a (non-host) client calls a Server RPC that both changes Ownership and changes the value of a NetworkVariable, that non-host client will not receive a OnValueChanged event.

public class Test : NetworkBehaviour
{
    public NetworkVariable<int> test = new NetworkVariable<int>(-1, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);

    [ServerRpc(RequireOwnership = false)]
    public void InitializeAndClaimOwnership_ServerRpc(int _test, ServerRpcParams _rpcParams = default)
    {
        NetworkObject.ChangeOwnership(NetworkManager.ServerClientId);
        test.Value = _test;
        NetworkObject.ChangeOwnership(_rpcParams.Receive.SenderClientId); // Remove this line, and the non-host client will receive the OnValueChanged event for the test NetworkVariable
    }
}

public class Observer : MonoBehaviour
{
    void SomeClientMethod(Test test)
    {
        test.test.OnValueChanged += (previousValue, newValue) =>
        {
            // Will not fire on non-host client
            Debug.Log($"Previous value: {previousValue}, new value: {newValue}");
        };

        test.InitializeAndClaimOwnership_ServerRpc(0);
    }
}

zachstronaut avatar Oct 18 '23 20:10 zachstronaut

Also, the value actually doesn't ever change... which is why there's no callback event.

The server/host and the remote client actually become out of sync in terms of their value for the NetworkVariable

zachstronaut avatar Oct 18 '23 21:10 zachstronaut

@zachstronaut 😸 You are great at finding obscure issues and in almost all cases they should be addressed. This one, I first need to get some form of confirmation on the intended use case. It looks like you are switching ownership to the server to allow the server to update the value and then changing ownership back to the client?

If that is the case, then I would say: "What if we are seriously considering an update to NetworkVariable where the server always has write permissions?"

Under the above type of change, for your scenario would you still need to change ownership like that or would you be able to just send (some value) to the server via RPC and have the server apply (the same or some other value based on the value sent) to the NetworkVariable?

Side note: Within the same update to NetworkVariable the server could actually write to the NetworkVariable prior to spawning the associated NetworkObject with the possibility of providing a setting or just allowing clients to do the same (sort of like a "pre-spawn" write permissions).

NoelStephensUnity avatar Oct 19 '23 00:10 NoelStephensUnity

@NoelStephensUnity

My use case is kind of making up for the fact that Netcode doesn't have client side predictive spawn... you can't have a NetworkBehaviour on a client until you wait for the round trip to the server to spawn it. (With modern player expectations, I'm going to implement client authority instead of server authority in every case I can.)

Anyway, what I'm specifically doing is claiming client ownership over an existing NetworkBehaviour model so that I can have that client responsiveness I desire when reading and writing to NetworkVariables. I don't want to send to the server the values I want with RPC because I wouldn't be able to read the new value locally until after the round trip.

It's a round trip to the server in order to gain ownership, so while I'm there on the server requesting ownership I thought I'd kill two birds and initialize the starting values that the client wants for those NetworkVariables.

I guess my alternative choice ended up being using OnGainedOwnership and having the client assign its initialization values itself instead of having the server do it. One stone per bird, I guess.

zachstronaut avatar Oct 19 '23 00:10 zachstronaut

@zachstronaut So, you are saying that if you could spawn NetworkObjects on the client-side that your underlying issue would be resolved (obviously the client instance doing the spawning would, by default, have ownership)?

NoelStephensUnity avatar Oct 19 '23 13:10 NoelStephensUnity

@NoelStephensUnity Yes, that would be great. If I can spawn a NetworkObject on the client side, with client ownership, and immediately be able to read/write its NetworkVariables and call server RPCs (which would have to buffer until the server replicates the spawn) then my remote clients can have the snappiest of experiences with the least perception of lag.

This gets tricky -- although not impossible -- when you're spawning things that have associated NetworkTransform or NetworkRigidbody. However, I've got a lot of use cases where position (and the interpolation/extrapolation problems) are irrelevant.

Say my clients want to pop some GUI to interact with an object? I don't know which client is going to interact with it, and I want the GUI to be able to draw immediately (no round trip latency) upon client interaction (e.g. button press).

I can split the data model from the GUI. This allows me immediate local control over drawing the GUI while I then either spawn a new data model or claim ownership over the existing model.

This is definitely a nice practice regardless to get into separating your data from your presentation... but pobody's nerfect and sometimes it would certainly be easier if I could just immediately spawn and have access to a networked prefab on my client.

I'm sure there are other examples outside of this specific GUI example too where the separation of concerns wouldn't be so easily split.

zachstronaut avatar Oct 19 '23 14:10 zachstronaut