Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We are updating our Terms of Service for all Unity subscription plans, effective October 13, 2022, to create a more streamlined, user-friendly set of terms. Please review them here: unity.com/legal/terms-of-service.
    Dismiss Notice
  3. Have a look at our Games Focus blog post series which will show what Unity is doing for all game developers – now, next year, and in the future.
    Dismiss Notice
  4. Join us on Thursday, September 29, for a day with Unity's SRP teams here on the forum or on Reddit, and discuss topics around URP, HDRP, and the Scriptable Render Pipeline in general.
    Dismiss Notice

Question How to use Actions correctly in multiplayer games ?

Discussion in 'Netcode for GameObjects' started by edin97, Sep 17, 2022.

  1. edin97

    edin97

    Joined:
    Aug 9, 2021
    Posts:
    29
    Hello,
    I'm having trouble using Actions for my multiplayer game.

    I have a shop, on item purchased I want to invoke a unity Action so my other classes can do their job on item purchased. Before confirming the purchase, I ask the server if the player has enough money, since Rpc's always return void, I'm using a bool NetworkVariable (HasPlayerEnoughMoney) , which the server sets to true if the player has enough money. If the player has enough money, I send an Action OnItemPurchased. Issue is : I subscribe to this HasPlayerEnoughMoney.OnValueChanged and OnItemPurchased with every player. So for some reason when the host buys an item, it deducts money from other players too. (clients buying doesn't take money from server) BUT, replacing the OnItemPurchased Action invoke by simple functions calls, works. no issues.

    Here are the simple methods : Both scripts are on a player -> if we have 2 players, each script will be read 4 times here (owner+copy on both sides.)
    Code (CSharp):
    1.    
    2.     private void OnEnable()
    3.     {
    4. // HasPlayerEnoughMoney is the bool NetworkVariable which the server sets to true if player has enough money
    5.         playerMoneyHandler.HasPlayerEnoughMoney.OnValueChanged += AllowPurchase;
    6.     }
    7.     private void OnDisable()
    8.     {
    9.         playerMoneyHandler.HasPlayerEnoughMoney.OnValueChanged -= AllowPurchase;
    10.     }
    11.     private void AllowPurchase(bool oldValue, bool newValue)
    12.     {
    13.         if (!IsOwner) { return; }
    14.         // IF ENOUGH MONEY TO BUY
    15.         if (newValue)
    16.         {
    17.             // THIS IS THE ISSUE : all players access this section ?? and call action on their side ?
    18.             ActionsPlayer.OnItemPurchased?.Invoke(lastCollectibleConsidered); // ISSUE
    19.  
    20.             // SIMPLE FUNCTION CALLS : SOLUTION
    21.             //playerMoneyHandler.AddMoney(-lastCollectibleConsidered.CollectibleValue);
    22.             //playerMagickHandling.AddMojosRecievedOnCollecting(lastCollectibleConsidered);
    23.             //playerInventoryManager.AddCollectibleInInventory(lastCollectibleConsidered);
    24.             //playerAbilitiesHandler.AddCollectedScroll(lastCollectibleConsidered);
    25.         }
    26.         else
    27.         {
    28.             if (Debug.isDebugBuild) { print("NOT ENOUGH MONEY : "); }
    29.         }
    30.     }
    31.  
    32. // INPUT : left mouse click -> confirm buy  
    33. private void PlayerWantsToConfirmBuy(InputAction.CallbackContext callbackContext)
    34.     {
    35. // SETS NETWORKVARIABLE VALUE TO TRUE IF PLAYER HAS ENOUGH MONEY
    36.         if (IsOwner) playerMoneyHandler.HasPlayerEnoughMoneyToBuy(lastCollectibleConsidered.CollectibleValue);
    37.     }
    38.  
    this is for example one of the methods that subscribes to OnItemPurchased :
    Code (CSharp):
    1.  
    2.     private void OnEnable()
    3.     {
    4.         Money.OnValueChanged += OnMoneyChanged;
    5.         ActionsPlayer.OnItemPurchased+= PayCollectibleValue;
    6.     }
    7.  
    8.     private void OnDisable()
    9.     {
    10.         Money.OnValueChanged -= OnMoneyChanged;
    11.         ActionsPlayer.OnItemPurchased-= PayCollectibleValue;
    12.  
    13.     private void AddMoney_Network(float amount)
    14.     {
    15.         if (Money.Value + amount <= 0.0f) { Money.Value = 0.0f; }
    16.         else { Money.Value += amount; }
    17.     }
    18.  
    19.     public void AddMoney(float amount)
    20.     {
    21.         if (IsServer)
    22.         {
    23.             AddMoney_Network(amount);
    24.         }
    25.         else
    26.         {
    27.            if (IsOwner) ManageMoneyOnServerRpc(amount, 1);
    28.         }
    29.     }
    30.     private void PayCollectibleValue(CollectibleScriptableObject collectible)
    31.     {
    32.         AddMoney(-collectible.CollectibleValue);
    33.     }
    34.     }
    Can someone tell me what I'm doing wrong here please ? I would prefer using actions so I don't have to make my methods public. Also I don't understand how the action is been called from 2 different players since I use an input to ask the server if the player has enough money. Input should be read only on the owner player since the copy doesn't recieve inputs ? Thanks in advance.
     
  2. SamuelBellomoUnity

    SamuelBellomoUnity

    Unity Technologies

    Joined:
    Sep 24, 2020
    Posts:
    3
    Is this a client hosted game or a dedicated game server? Cause you usually don't want to have monetization controlled by a client, that'd be exposed to cheating super easily. You'd want a service for this or use a DGS.
    If on DGS, netvars are replicated by default to all clients. so a netvar change server side will get replicated to all clients. you'd either want to change your read permissions on the netvar or use a client RPC. For a transitory event like this, I'd use a client RPC (see this page https://docs-multiplayer.unity3d.com/netcode/current/learn/rpcvnetvar) you don't need state tracking over multiple frames for this.
     
    REDACT3D_ and CreativeChris like this.
unityunity