Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Third Party How to synchronize a deck of card (Using Mirror)

Discussion in 'Multiplayer' started by lebonsalaxe, Jun 9, 2021.

  1. lebonsalaxe

    lebonsalaxe

    Joined:
    Jun 8, 2021
    Posts:
    2
    Hey, I've been working on this card game lately, but i encouter a problem where I can't sync the deck of cards to the clients ( Acts as if each client has his own deck ) - So i end up, for example, with the 5 of heart on both hands. The cards that each client draws are not removed from the deck to the other player.

    My cards are all GameObjects and I can't use SyncList of gameobjects to my knowledge.

    Any help would be much appreciated.



    I can provide code if you need.
     
  2. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    Depends on how you're tracking all of it. If you're doing something on a client and it's not replicating, then you just need to go and find a way to do a [Command] and [ClientRpc] to replicate the change, or something along those lines.

    The cards should be entirely managed on the server anyway, so what the client sees should be what the server explicitly told the client to see. For example if there are a deck of cards, the client should only see them because the server sent him an array of data to convey how that stack is identified. If they draw a card, maybe they should request a card from the server and not execute until they get confirmation with the result of their draw.
     
  3. lebonsalaxe

    lebonsalaxe

    Joined:
    Jun 8, 2021
    Posts:
    2
    I am using a Command and Rpc in order to spawn the cards on the clients' screens, but the way my Deck of cards is managed, is the problem i presume. Since the cards leave the individual decks on each client. - I guess i need to create one deck for all clients ? How so ? SyncList of gameobjects is unavailable...



    Code (CSharp):
    1.  
    2. public class PlayerManager : NetworkBehaviour
    3. {
    4.  
    5.     public GameObject Card1;
    6.     public GameObject Card2;
    7.     public GameObject Card3;
    8.     public GameObject Card4;
    9.     public GameObject Card5;
    10.     public GameObject PlayerArea;
    11.     public GameObject EnemyArea;
    12.     public GameObject DropZone;
    13.  
    14.  
    15.     List<GameObject> Deck= new List<GameObject>();
    16.  
    17.  
    18.  
    19.     public override void OnStartClient()
    20.     {
    21.         base.OnStartClient();
    22.  
    23.         PlayerArea = GameObject.Find("PlayerArea");
    24.         EnemyArea = GameObject.Find("EnemyArea");
    25.         DropZone = GameObject.Find("DropZone");  
    26.     }
    27.  
    28.     [Server]
    29.     public override void OnStartServer()
    30.     {
    31.         cards.Add(Card1);
    32.         cards.Add(Card2);
    33.         cards.Add(Card3);
    34.         cards.Add(Card4);
    35.         cards.Add(Card5);    
    36.     }
    37.  
    38.     [Command]
    39.     public void CmdDealCards()
    40.     {            
    41.         for (int i = 0; i < 4; i++)
    42.         {        
    43.             GameObject card = Instantiate(cards[i], new Vector2(0, 0), Quaternion.identity);
    44.             NetworkServer.Spawn(card, connectionToClient);
    45.          
    46.             RpcShowCard(card, "Dealt");
    47.             Deck.RemoveAt(i);
    48.         }
    49.     }
    50.  
    51.     public void PlayCard(GameObject card)
    52.     {
    53.         CmdPlayCard(card);
    54.         cardsPlayed++;
    55.     }
    56.  
    57.  
    58.  
    59.     [Command]
    60.     void CmdPlayCard(GameObject card)
    61.     {
    62.         RpcShowCard(card, "Played");
    63.     }
    64.  
    65.  
    66.     [ClientRpc]
    67.     void RpcShowCard(GameObject card, string type)
    68.     {
    69.         if (type == "Dealt")
    70.         {
    71.             if (hasAuthority)
    72.             {
    73.                 card.transform.SetParent(PlayerArea.transform, false);
    74.             }
    75.             else
    76.             {
    77.                 card.transform.SetParent(EnemyArea.transform, false);
    78.                 card.GetComponent<CardFlipper>().Flip();
    79.             }
    80.         }
    81.         else if (type == "Played")
    82.         {
    83.             card.transform.SetParent(DropZone.transform, false);
    84.             if (!hasAuthority)
    85.             {
    86.                 card.GetComponent<CardFlipper>().Flip();
    87.             }
    88.         }  
    89.     }
     
    Elermond likes this.
  4. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    You may like to read the documentation from Mirror.

    [Server]
    means only the server is permitted to call this method.
    [Command]
    means only client objects with authority can call this, and it only executes on the server. It is server code.
    [ClientRpc]
    means only the server can call this method, but it only runs on clients.

    You should design for server authority, so clients should never be able to outright demand cards, or anything for that matter, from the server. Clients are filthy peasants. They do what the server tells them to do. If they make a request, they only get something back because the sever deemed them worthy.

    You should decide what all cards are on the server. Do practically everything on the server, only tell the clients what is important, and everything the client does should be a request, or information for the server to examine and act upon - maybe - if it decides the information is valid.

    Your Cards are GameObject references, which is probably not a good design pattern. Cards can literally be an integer and you simply pass the integer and the recieving end can create the appropriate GameObject by looking up the ID or Index of a List/Array. Basically, make a database and send indexes to express what cards are being referenced.

    Objects in the world space could have NetworkBehaviors on them, and that basically lets you run
    [Command]
    or
    [ClientRpc]
    on them and know exactly what card is being referenced on all clients and the server quite easily.

    Once you start following strict server authority patterns, some replication and sync issues basically just disappear. Your player manager class there won't be enough to support that. You'll need some more classes and general structure to your design.
     
    Elermond likes this.