Change listeners and input authority - implementing weapon switching

This is driving me crazy. I'm basing my project architecture on the Fusion Outer Loop demo, where a Player object is instantiated for each joined client, and can then spawn a Character. I've implemented movement/death/respawning/character switching just fine. Where I'm struggling is weapon switching. I've tried a few things, but my latest attempt looks like this:

using Fusion;
using UnityEngine;

public class Character : NetworkBehaviour
{
    [Networked(OnChanged = nameof(OnWeaponChanged))]
    public int currentWeapon { get; set; }
    //other variables, components here

    void Update()
    {
        firstWeaponKeyDown = firstWeaponKeyDown || Input.GetKeyDown(KeyCode.Alpha1);
        secondWeaponKeyDown = secondWeaponKeyDown || Input.GetKeyDown(KeyCode.Alpha2);
        switchWeaponKeyDown = switchWeaponKeyDown || Input.GetKeyDown(KeyCode.Q);
        App.Instance.Session.Map.SetPlayerDebugText($"Current Weapon: {currentWeapon}");    
    }

    public override void FixedUpdateNetwork()
    {
        if (GetInput(out NetworkInputData networkInputData))
        {
            //movement etc
            //Switching weapon
            int switchToWeapon = currentWeapon;
            if (firstWeaponKeyDown) switchToWeapon = 0;
            if (secondWeaponKeyDown) switchToWeapon = 1;
            if (switchWeaponKeyDown) switchToWeapon = currentWeapon == 0 ? 1 : 0;
            if (Object.HasInputAuthority) currentWeapon = switchToWeapon;

            firstWeaponKeyDown = false;
            secondWeaponKeyDown = false;
            switchWeaponKeyDown = false;
        }
    }

    public static void OnWeaponChanged(Changed<Character> changed)
    {
        changed.Behaviour.nicknameText.text = changed.Behaviour.currentWeapon.ToString(); //simple debug to show the currently selected weapon above the player's head, so other clients can see.
    }
}

I also tried using an RPC, rather than a change listener, but I figured this was a better approach to avoid missed RPCs.

Right now only the host can change their weapon. If other players try, sometimes the debug text briefly flickers as though an attempt has been made. I'm sure I've made a mistake with input or state authority somewhere, but I've tried a few things with no luck.

Would hugely appreciate any help - this is driving me nuts.

Comments

  • you cannot use local variables like firstWeaponKeyDown for this. All your button presses should be stored inside the NetworkInput and only accessed through that. And you should submit them using the input callback. Take a look at the Fusion 100 tutorial or one of our samples.

    uYou cannot firstWeaponKeyDown

  • Thank you, that appears to have worked.

    I'm still a bit unclear about how input actually works in Fusion, since you can still sample input normally (ie outside of the NetworkInput system) for local functionality like showing a scoreboard. Do NetworkBehaviours override normal input function or something?

    I guess what I was thinking originally was that it was better to handle weapon switching input on the client side, and just have one variable that other clients can observe, rather than passing multiple button presses through NetworkInput, and I'm not sure I get why that doesn't actually work.

  • The way network input works is that it is applied both locally on your client during the predicted FixedUpdateNetwork but also sent to the server to be applied during the server's FixedUpdateNetwork. That way the server and the client will end up using the same inputs and get a similar result.

    In your case the weapon switch was just based on storing input locally in a regular variable. This only happens on the client so the weapon switch is only executed on the client and then corrected by the server.

  • So the local client makes some change (current weapon variable) outside of NetworkInput, which is then corrected by the server because it doesn't match the current state of the object (ie the server overrides the weapon to the previous value). Right?

    In what scenario can a client locally change some variable, and have that update to the server, outside of the NetworkInput struct? I'm thinking for example of equipping some cosmetic item, which should then update for non-local clients. The Fusion Outer Loop does this with a colour picker and a field for changing the username, both of which make RPC calls to the local Player object. Is it just that a NetworkBehaviour can't call its own RPC?

  • Yes that's correct you can also use RPCs for this. The workflow is the same it's either

    • NetworkInput is used to change Networked properties which then get replicated to each client.
    • An RPC from the server is used to change Networked properties which then get replicated to each client.

    The RPC approach is a bit slower but much more flexible since you can have any number of RPCs but only one network input. So for something like switching a cosmetic item you use an RPC. For the core gameplay where you need immediate feedback you use input.

  • I see. Thanks for answering my questions, I really appreciate it.