[Netcode] Changing NetworkVariable in one NetworkeBehavior changesit inside ALL NetworkBehaviours

So I’m making a multiplayer card game, based in Triple Triad using Netcode. I have a Card NetworkBehaviour with an attribute called IsBlue, which determines the color of the background for the card (and player currently owning the card)

public class Card : Interactible //Interacbile is a NetworkBehaviour
{
    ...
    public NetworkVariable<bool> IsBlue = new NetworkVariable<bool>(false);
    ...
   
    void Update()
    {
        if (IsChangingSides.Value)
        {
            ChangeSides();
        }
    }
    ....
    public override void OnNetworkSpawn()
    {
        CardName.OnValueChanged += (FixedString64Bytes previousValue, FixedString64Bytes newValue) => {              ChangeCardMaterial(); };
        IsBlue.OnValueChanged += (bool previousValue, bool newValue) =>
        {
            Debug.Log("Changed Color!");
            ChangeCardMaterial();
        };
    ...   
    private void ChangeSides()
    {
        ....
        ChangeColorClientRpc(!IsBlue.Value);
        IsChangingSides.Value = false;
        RevealCardClientRpc();
    }
    ...
    [ClientRpc]
    public void ChangeColorClientRpc(bool isBlue)
    {
        Debug.Log(name);
        IsBlue.Value = isBlue;
        ChangeCardMaterial();
    }
    ....
}

When I first start the game, I randomly decide which player will be the first one and change the value of their IsBlue variable individually inside the server, then I register a listener for the OnValueChanged event, which updates the material for each card. This is working like a charm

public class GameController : NetworkBehaviour
{
    ...
    public Hand[] PlayerHands;
    ...
    private void SetupCards()
    {
        foreach (Hand playerHand in PlayerHands)
        {
            foreach (Card card in playerHand.cards)
            {
                ...
                card.IsBlue = card.OwningPlayer.IsBlue;
                ...
            }
        }
    }
...
}

Now, when playing, after a player makes a move and takes the enemy’s card, the card is supposed to change colors, but when I change the IsBlue value for the card, it changes on all of the other cards too! wht’s worse, the OnValueChanged listener for the variable is never called!

public class Player : NetworkBehaviour
{
    ...
    //This is the entry point for players using their card, this triggers ther est of the process
    [ServerRpc(RequireOwnership = false)]
    public void SelectInteractibleServerRpc(NetworkObjectReference interactibleNetworkReference,
        ServerRpcParams serverRpcParams = default)
    {
        NetworkObject rawInteractible;
        bool couldFetchIteractible = interactibleNetworkReference.TryGet(out rawInteractible);

        if (couldFetchIteractible)
        {
            Interactible interactible = rawInteractible.gameObject.GetComponent<Interactible>();
            if (IsPlaying.Value && (rawInteractible.OwnerClientId == serverRpcParams.Receive.SenderClientId || interactible is BoardSlot))
            {
                ...
                interactible.ToggleSelect();
               ....
           }
        ...
        }
    ...
    }
....
}
public class BoardSlot : Interactible //Interacbile is a NetworkBehaviour
{
    private Card OccupyingCard;

    public BoardSlot TopSlot;
    ...
    private void CalculateMove()
    {
        if (TopSlot != null && TopSlot.OccupyingCard != null && TopSlot.OccupyingCard.BottomValue.Value < OccupyingCard.TopValue.Value && TopSlot.OccupyingCard.IsBlue.Value != OccupyingCard.IsBlue.Value)
        {
            TopSlot.OccupyingCard.IsChangingSides.Value = true;
            Debug.Log("Brute Force Taken!");
        }
...
}

As a footnote, I am now changing the value inside an RPC because, as mentioned above, when I tried changing it inside the server itself, it was just changing the value for the variable for all Card NetworkObjects in scene, never calling the listener so the materials where never updating. I decided to create a ClientRpc and manually chagne the material, but the issue of all cards having their NetworkVariable changed at the same time persisted when I change the variable inside the ClientRpc

Am I doing something wrong? Or am I just the unluckiest person in the world to stumble upon two bugs at the same time? Help please :frowning:

When you exchange cards do you change the ownership of the card(s)?
Without all of your scripts, I just whipped together a quick example of how I would go about handling changing color based on ownership and using NetworkVariables:

/// <summary>
/// Add this to your player prefab
/// The player color selector assigns a color to a spawned player (you can adjust the colors however you want, but the general idea)
/// </summary>
public class PlayerColorSelector : NetworkBehaviour
{
    private static Color[] s_Colors = { Color.red, Color.yellow, Color.green, Color.blue, Color.cyan, Color.magenta, Color.white };
    public NetworkVariable<Color> PlayerColor = new NetworkVariable<Color>();
    public override void OnNetworkSpawn()
    {
        PlayerColor.Value = s_Colors[OwnerClientId % Convert.ToUInt64(s_Colors.Length)];
        base.OnNetworkSpawn();
    }
}
/// <summary>
/// Add this to your card(s) prefab(s)
/// The owner always sets the CardColor which will be synchronized with everyone when it changes
/// </summary>
public class PlayingCardColor : NetworkBehaviour
{
    public NetworkVariable<Color> CardColor = new NetworkVariable<Color>(default, NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Owner);
    private PlayerColorSelector m_PlayerColorSelector;
    private MeshRenderer m_MeshRenderer;
    private void Awake()
    {
        m_MeshRenderer = GetComponent<MeshRenderer>();
    }
    public override void OnNetworkSpawn()
    {
        // Everyone subscribes to when the color of the card changes
        CardColor.OnValueChanged += OnCardColorChanged;
        // All instances assign their own relative player color selector to each *local* card instance.
        // This assures when ownership changes the owner will apply its color to the card (which will then get synchronized with everyone)
        m_PlayerColorSelector = NetworkManager.LocalClient.PlayerObject.GetComponent<PlayerColorSelector>();
        // When the card is first spawned, it is assigned the current owner's color
        if (IsOwner)
        {
            SetCardPlayerColor();
        }
        // Everyone applies the current card color when the card is spawned
        ApplyCurrentOwnerColor();
        base.OnNetworkSpawn();
    }
    private void ApplyCurrentOwnerColor()
    {
        // Since the script to change the color wasn't provided, I just am applying the color to the
        // MeshRenderer's material's color
        m_MeshRenderer.material.color = CardColor.Value;
    }
    private void OnCardColorChanged(Color previous, Color current)
    {
        ApplyCurrentOwnerColor();
    }
    /// <summary>
    /// Requires NGO v1.7.0 or higher
    /// When ownership of the card changes, the owner will apply its assigned color
    /// to the card
    /// </summary>
    protected override void OnOwnershipChanged(ulong previous, ulong current)
    {
        if (current == NetworkManager.LocalClientId)
        {
            SetCardPlayerColor();
        }
        base.OnOwnershipChanged(previous, current);
    }
    /// <summary>
    /// Uncomment this for anything below NGO v1.7.0
    /// When ownership of the card changes, the owner will apply its assigned color
    /// to the card
    /// (also comment out the above OnOwnershipChanged)
    /// </summary>
    //public override void OnGainedOwnership()
    //{
    //    // We do this check because this is triggered on the server-host side too when a client gains ownership
    //    if (IsOwner)
    //    {
    //        SetCardPlayerColor();
    //    }
    //    base.OnGainedOwnership();
    //}
    /// <summary>
    /// Just made this a method in the event you have
    /// </summary>
    private void SetCardPlayerColor()
    {
        // Just a safety check in the event, later, you invoke this as a non-owner
        if (!IsOwner)
        {
            Debug.LogWarning($"Client-{NetworkManager.LocalClientId} is trying to set the card color when the card is owned by Client-{OwnerClientId}!");
            return;
        }
        // Owner applies its player assigned color to the CardColor NetworkVariable
        CardColor.Value = m_PlayerColorSelector.PlayerColor.Value;
    }
}

Let me know if this helps you resolve your issue?

I do! i forgot to flag this as solved.
The problem was on my code, I was setting the network variable IsBlue of the card as the IsBlue variable of the played, which obvioously misconfigured everything haha so yeah, my bad xD

1 Like