5
\$\begingroup\$

enter image description hereI'm having trouble developing and understanding Server code. Currently, I'm trying to have each person that joins the Server be given a single digit ID that can then be used for determining turn order and a number of other things (ex: specify whose card to place where). As it stands, I tried a few versions, but the debug message indicating who should go first doesn't print and couldn't think of a way for it to run DetermineFirstPlayer() after both players join in after the server is already existing.

using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class ServerManager : NetworkBehaviour
{
    // SyncVars for game state variables
    [SyncVar]
    private List<Player> players = new List<Player>();

    [SyncVar]
    public int playerNum = 0;

    //Reference to the Player
    public Player player;

    // Reference to the TurnManager
    public TurnManager turnManager;

    // Other game state variables
    private List<Card> drawStack = new List<Card>();
    private List<Card>[] playerHands;
    private List<Card>[] playerBoard;
    private List<Card> graveyard = new List<Card>();
    private int currentPlayerIndex = 0;

    //Starts server (Host + Client)
    [Server]
    public override void OnStartServer()
    {
        base.OnStartServer();

        //Tells us that the server is running
        Debug.Log("OnStartServer was activated");
    }

    // Start is called before the first frame update
    void Start()
    {
        // Find a reference to the TurnManager
        turnManager = GameObject.Find("TurnManager").GetComponent<TurnManager>();
    }

    //Runs whens Server stops running
    [Server]
    public override void OnStopServer()
    {
        base.OnStopServer();

        //Tells us that the server is running
        Debug.Log("OnStartServer was deactivated");
    }

    //Starts when client button pressed
    public override void OnStartClient()
    {
        base.OnStartClient();
        //Message to show that client is running; appears in console tab
        Debug.Log("OnStartClient was activated");

    }

    //Runs when Client disconnects or leaves
    public override void OnStopClient()
    {
        base.OnStopClient();
        //Message to show that client is not running; appears in console tab
        Debug.Log("OnStartClient was deactivated");
    }

}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using UnityEngine.Events;

public class TurnManager : NetworkBehaviour
{
    List<NetworkIdentity> _identities = new List<NetworkIdentity>();
    private int _currentPlayerIndex = 1;
    [SyncVar(hook = nameof(NextTurnEvent))] public uint currentPlayer = 0;
    public UnityEvent<uint> nextPlayer;
    public UnityEvent<NetworkIdentity, int> playerRegisteredEvent;

    // Track the number of players that have joined
    private int playersJoined = 0;

    private void Start()
    {
        gameObject.name = "TurnManager";
        if (isServer) _identities.Add(null);
    }

    [Server]
    public void RegisterPlayer(NetworkConnectionToClient connection)
    {
        //Handle here if reconnects can happen
        Debug.LogFormat("Payer added: {0}", connection.identity);

        _identities.Add(connection.identity);
        playerRegisteredEvent?.Invoke(connection.identity, GetPlayerTurn(connection));

        // Increment the count of players that have joined
        playersJoined++;

        // If all players have joined, print the current number of players and whose turn it is
        if (playersJoined == 2)
        {
            Debug.LogFormat("Both players have joined. Current number of players: {0}", playersJoined);
            Debug.LogFormat("Current turn: {0}", currentPlayer + " player");
        }
    }

    [Server]
    public void NextPlayer()
    {
        _currentPlayerIndex++;
        if (_currentPlayerIndex >= _identities.Count) _currentPlayerIndex = 1;
        currentPlayer = _identities[_currentPlayerIndex].netId;
    }

    [Server]
    public bool IsCurrentTurn(NetworkConnectionToClient connection)
    {
        if (_identities[_currentPlayerIndex] == connection.identity) return true;
        //Sent a message to the client the action is out of turn
        //_errorManager.TargetErrorMessage(connection, "Not your turn");
        return false;
    }

    [Server]
    public int GetCurrentPlayerIndex()
    {
        return _currentPlayerIndex;
    }

    [Server]
    public uint GetPlayerByIndex(int index)
    {
        return _identities[index].netId;
    }

    public int GetPlayerTurn(NetworkConnectionToClient connection)
    {
        return _identities.IndexOf(connection.identity);
    }

    void NextTurnEvent(uint oldPlayer, uint newPlayer)
    {
        nextPlayer?.Invoke(newPlayer);
    }
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;

public class Player : NetworkBehaviour
{
    // References to game objects
    public GameObject PlayerCard;
    public TurnManager turnManager;

    public GameObject Hand;
    public GameObject Deck;

    public List<GameObject> DropZones = new List<GameObject>();

    // Variables for game state
    public int Health;
    public int CardsPlayed = 0;
    public bool IsMyTurn = false;

    // Start is called before the first frame update
    void Start()
    {
        
            // Initialize references to game objects
            Hand = GameObject.Find("AllyHand");
            Deck = GameObject.Find("Deck");

            // Connect drop zones to the list
            DropZones.Add(GameObject.Find("AllyDropZone"));
            DropZones.Add(GameObject.Find("AllyDropZone (1)"));
            DropZones.Add(GameObject.Find("AllyDropZone (2)"));
            DropZones.Add(GameObject.Find("AllyDropZone (3)"));
            DropZones.Add(GameObject.Find("AllyDropZone (4)"));
            DropZones.Add(GameObject.Find("AllyDropZone (5)"));

            // Find a reference to the TurnManager
            turnManager = GameObject.Find("TurnManager").GetComponent<TurnManager>();
    }

    public override void OnStartClient()
    {
        base.OnStartClient();

        CmdRegisterPlayer();
    }

    [Command]
    void CmdRegisterPlayer(NetworkConnectionToClient sender = null)
    {
        turnManager.RegisterPlayer(sender);
    }

}

\$\endgroup\$
1
  • \$\begingroup\$ Sorry for the lack of clarity, just getting tired of trying different things and seeing vastly different things via guides on which approach to take. \$\endgroup\$ Mar 21 at 1:06

1 Answer 1

5
\$\begingroup\$

The server needs to know who is playing. The easiest way to do that is let the players report themself. When your game gets bigger, games usually use google, facebook or some custom login for telling who they are. But there is a way easier option for small games/ prototyping. Device Authenticator uses the SystemInfo.deviceUniqueIdentifier to identify the client.

This is one of my old Turnmanagers, you can remove the events for direct methods and make your own error manager if you want to inform clients for invalid actions or out of turn commands

public class TurnManager: NetworkBehaviour {        

    List<NetworkIdentity> _identities = new List<NetworkIdentity>();
    private int _currentPlayerIndex = 1; 
    [SyncVar(hook = nameof(NextTurnEvent))] public uint currentPlayer = 0;
    public UnityEvent<uint> nextPlayer;
    public UnityEvent<NetworkIdentity, int> playerRegisteredEvent;

    private void Start() {
        gameObject.name = "TurnManager";
        if (isServer) _identities.Add(null);            
    }

    [Server]
    public void RegisterPlayer(NetworkConnectionToClient connection) {
        //Handle here if reconnects can happen
        Debug.LogFormat("Payer added: {0}", connection.identity);
        _identities.Add(connection.identity);
        playerRegisteredEvent?.Invoke(connection.identity, GetPlayerTurn(connection));
    }

    [Server]
    public void NextPlayer() {
        _currentPlayerIndex++;
        if (_currentPlayerIndex >= _identities.Count) _currentPlayerIndex = 1;
        currentPlayer = _identities[_currentPlayerIndex].netId;
    }

    [Server]
    public bool IsCurrentTurn(NetworkConnectionToClient connection) {
        if (_identities[_currentPlayerIndex] == connection.identity) return true;
        //Sent a message to the client the action is out of turn
        //_errorManager.TargetErrorMessage(connection, "Not your turn");
        return false;
    }

    [Server]
    public int GetCurrentPlayerIndex() {
        return _currentPlayerIndex;
    }

    [Server]
    public uint GetPlayerByIndex(int index) {
        return _identities[index].netId;
    }

    public int GetPlayerTurn(NetworkConnectionToClient connection) {
        return _identities.IndexOf(connection.identity);
    }
    
    void NextTurnEvent(uint oldPlayer, uint newPlayer) {
        nextPlayer?.Invoke(newPlayer);
    }
}

The idea is, each player calls something like

[Command]
void CmdRegisterPlayer(NetworkConnectionToClient sender = null) {
    _turnManager.RegisterPlayer(sender);         
}

This should happen once, maybe from the title screen on the Play Game button or search for opponent. Normally this should point to some kind of lobby which we are skipping for simple games.

An example for an user action - end turn - would look this

[Command(requiresAuthority = false)]
public void CmdEndTurn(NetworkConnectionToClient connection = null) {
    EndTurn(connection);
}

[Server]
private void EndTurn(NetworkConnectionToClient connection) {
    if (!_turnManager.IsCurrentTurn(connection)) return;
    ...
    //code that handles your turn end
}

The client can't end the turn on its own. It just sends the request to the server with his connection. The turnmanager checks, if its the players turn. Only if thats true, continue with the action.

As for your question when to actually start the game, you can use the RegisterPlayer method server side to check each time how many people are already registered and once the amount is reached for your game, call the server method of StartGame logic.

\$\endgroup\$
2
  • \$\begingroup\$ I made an TurnManager gameobject and provided it that script which may not have been what you intended, but it does work with my player script. Which executes the command. \$\endgroup\$ Mar 21 at 15:05
  • 1
    \$\begingroup\$ If it works, glad to help. TurnManager has to be its own Gameobject since it is a networkbehaviour and those are in need in network identities. \$\endgroup\$
    – Zibelas
    Mar 21 at 15:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .