Search Unity

Third Party Random damage in turn-based prototype using Mirror

Discussion in 'Multiplayer' started by CharlieFleed, Oct 21, 2021.

  1. CharlieFleed

    CharlieFleed

    Joined:
    Jul 7, 2018
    Posts:
    5
    In my prototype, which is this one btw

    when a local unit picks an action (move, shoot, etc), I call a Command which in turn calls an ClientRpc method on all clients, for example:
    Code (CSharp):
    1.  
    2.     override public void HandleConfirmClick()
    3.     {
    4.         CmdHunker();
    5.     }
    6.  
    7.     [Command]
    8.     void CmdHunker()
    9.     {
    10.         RpcHunker();
    11.     }
    12.  
    13.     [ClientRpc]
    14.     void RpcHunker()
    15.     {
    16.         _gridAgent.Hunkering = true;
    17.         InvokeActionConfirmed(this);
    18.         Deactivate();
    19.         InvokeActionComplete(this);
    20.     }
    Nothing out of the ordinary. Cmd and Rpc prefixes are for clarity.
    Since the game is turn-based, after the action is decided, each client proceeds with its execution independently, and they will all do the same thing, except when there is randomness involved (hit/miss, random damage, crits, etc.).
    The need for a random value may occur down the line at the end of a series of events involving several objects interacting, projectiles, explosions, timers etc, so I don't think in such a scenario one would synchronize/centralize everything, it sounds like a nightmare.

    I found several posts, here and elsewhere, on similar issues, some suggesting to use a custom random generator initialized on all clients with the same seed. I followed this approach before in other platforms (XNA), but I don't really like this solution, and it also appears to have issues with random generator implementations depending on CPU architecture etc, so I am looking for alternatives.

    Any suggestions?
     
    Last edited: Oct 22, 2021
  2. CharlieFleed

    CharlieFleed

    Joined:
    Jul 7, 2018
    Posts:
    5
    So, in case anyone is interested, I ended up implementing my random generator as a spawned game object.
    The idea is that the one on the server will generate numbers when requested and will add them to a SyncList. The ones running on clients will return a number only when there are new unused numbers in the SyncList, otherwise the requestor will try again the next frame.
    It is key that the sequence of numbers requested by all clients is deterministic (let's say hit, crit, damage), so I removed any source of randomness like the order in which colliders are returned for explosions. It is also important that when there are multiple requests at the same frame their order is fixed, so for example they cannot be calls in Update methods as there is no guarantee on the order. Luckily I was already doing everything with an event system where I have full control of the order in which "simultaneous" events are processed.

    Here is my code, quite simple after all.

    Code (CSharp):
    1. public class NetworkRandomGenerator : NetworkBehaviour
    2. {
    3.     readonly SyncList<int> _ints = new SyncList<int>();
    4.  
    5.     int _next;
    6.  
    7.     private void Awake()
    8.     {
    9.         _instance = this;
    10.     }
    11.  
    12.     public int RandomRange(int min, int max)
    13.     {
    14.         if (isServer)
    15.         {
    16.             int value = Random.Range(min, max);
    17.             _ints.Add(value);
    18.             return value;
    19.         }
    20.         else
    21.         {
    22.             int value = _ints[_next];
    23.             _next++;
    24.             return value;
    25.         }
    26.     }
    27.  
    28.     public bool Ready()
    29.     {
    30.         if (isServer)
    31.         {
    32.             return true;
    33.         }
    34.         else
    35.         {
    36.             return _next < _ints.Count;
    37.         }
    38.     }
    39.  
    40.     #region Singleton
    41.  
    42.     private static NetworkRandomGenerator _instance;
    43.  
    44.     public static NetworkRandomGenerator Instance { get { return _instance; } }
    45.  
    46.     #endregion
    47. }