public static UpdateConnectedPlayersMessage Send()
    {
        Logger.LogFormat("This server informing all clients of the new PlayerList state: {0}", Category.Connections,
                         string.Join(",", PlayerList.Instance.AllPlayers));
        UpdateConnectedPlayersMessage msg = new UpdateConnectedPlayersMessage();
        var  prepareConnectedPlayers      = new List <ClientConnectedPlayer>();
        bool pendingSpawn = false;

        foreach (ConnectedPlayer c in PlayerList.Instance.AllPlayers)
        {
            if (c.Connection == null)
            {
                continue;                                  //offline player
            }
            if (string.IsNullOrEmpty(c.Name))
            {
                if (c.GameObject != null)
                {
                    var joinedViewer = c.GameObject.GetComponent <JoinedViewer>();
                    if (joinedViewer != null)
                    {
                        pendingSpawn = true;
                    }
                    else
                    {
                        continue;
                    }
                }
                else
                {
                    continue;
                }
            }

            var tag = "";

            if (PlayerList.Instance.IsAdmin(c.UserId))
            {
                tag = "<color=red>[Admin]</color>";
            }
            else if (PlayerList.Instance.IsMentor(c.UserId))
            {
                tag = "<color=#6400ff>[Mentor]</color>";
            }

            prepareConnectedPlayers.Add(new ClientConnectedPlayer
            {
                UserName     = c.Username,
                Name         = c.Name,
                Job          = c.Job,
                PendingSpawn = pendingSpawn,
                Tag          = tag
            });
        }

        msg.Players = prepareConnectedPlayers.ToArray();

        msg.SendToAll();
        return(msg);
    }
Beispiel #2
0
 private static void TrySendUpdate()
 {
     if (CustomNetworkManager.Instance != null && CustomNetworkManager.Instance._isServer && PlayerList.Instance != null)
     {
         UpdateConnectedPlayersMessage.Send();
     }
 }
Beispiel #3
0
 private void TryRemove(ConnectedPlayer player)
 {
     Debug.Log($"Removed {player}");
     values.Remove(player);
     NetworkServer.Destroy(player.GameObject);
     UpdateConnectedPlayersMessage.Send();
 }
Beispiel #4
0
 private void TryRemove(ConnectedPlayer player)
 {
     Logger.Log($"Removed {player}", Category.Connections);
     values.Remove(player);
     AddPrevious(player);
     NetworkServer.Destroy(player.GameObject);
     UpdateConnectedPlayersMessage.Send();
 }
Beispiel #5
0
 private void TryRemove(ConnectedPlayer player)
 {
     Logger.Log($"Removed {player}", Category.Connections);
     loggedOff.Add(player);
     values.Remove(player);
     AddPrevious(player);
     UpdateConnectedPlayersMessage.Send();
     CheckRcon();
 }
Beispiel #6
0
    public static UpdateConnectedPlayersMessage Send()
    {
        UpdateConnectedPlayersMessage msg = new UpdateConnectedPlayersMessage();

        msg.Players = PlayerList.Instance.ClientConnectedPlayerList.ToArray();

        msg.SendToAll();
        return(msg);
    }
Beispiel #7
0
    private static void UpdatePlayerList()
    {
        Dictionary <string, GameObject> connectedPlayers = PlayerList.Instance.connectedPlayers;

        //Notify all clients that connected players list should be updated
        GameObject[] players = new GameObject[connectedPlayers.Count];
        connectedPlayers.Values.CopyTo(players, 0);
        UpdateConnectedPlayersMessage.Send(players);
    }
Beispiel #8
0
    /// <summary>
    /// Spawns a new player character and transfers the connection's control into the new body.
    /// If existingMind is null, creates the new mind and assigns it to the new body.
    ///
    /// Fires server and client side player spawn hooks.
    /// </summary>
    /// <param name="connection">connection to give control to the new player character</param>
    /// <param name="occupation">occupation of the new player character</param>
    /// <param name="characterSettings">settings of the new player character</param>
    /// <param name="existingMind">existing mind to transfer to the new player, if null new mind will be created
    /// and assigned to the new player character</param>
    /// <param name="spawnPos">world position to spawn at</param>
    /// <param name="naked">If spawning a player, should the player spawn without the defined initial equipment for their occupation?</param>
    /// <param name="willDestroyOldBody">if true, indicates the old body is going to be destroyed rather than pooled,
    /// thus we shouldn't send any network message which reference's the old body's ID since it won't exist.</param>
    ///
    /// <returns>the spawned object</returns>
    private static GameObject ServerSpawnInternal(NetworkConnection connection, Occupation occupation, CharacterSettings characterSettings,
                                                  Mind existingMind, Vector3Int?spawnPos = null, bool naked = false, bool willDestroyOldBody = false)
    {
        //determine where to spawn them
        if (spawnPos == null)
        {
            Transform spawnTransform = GetSpawnForJob(occupation.JobType);
            if (spawnTransform == null)
            {
                Logger.LogErrorFormat(
                    "Unable to determine spawn position for connection {0} occupation {1}. Cannot spawn player.",
                    Category.ItemSpawn,
                    connection.address, occupation.DisplayName);
                return(null);
            }

            spawnPos = spawnTransform.transform.position.CutToInt();
        }

        //create the player object
        var newPlayer       = ServerCreatePlayer(spawnPos.GetValueOrDefault());
        var newPlayerScript = newPlayer.GetComponent <PlayerScript>();

        //get the old body if they have one.
        var oldBody = existingMind?.GetCurrentMob();

        //transfer control to the player object
        ServerTransferPlayer(connection, newPlayer, oldBody, EVENT.PlayerSpawned, characterSettings, willDestroyOldBody);


        if (existingMind == null)
        {
            //create the mind of the player
            Mind.Create(newPlayer, occupation);
        }
        else
        {
            //transfer the mind to the new body
            existingMind.SetNewBody(newPlayerScript);
        }


        var ps = newPlayer.GetComponent <PlayerScript>();
        var connectedPlayer = PlayerList.Instance.Get(connection);

        connectedPlayer.Name = ps.playerName;
        connectedPlayer.Job  = ps.mind.occupation.JobType;
        UpdateConnectedPlayersMessage.Send();

        //fire all hooks
        var info = SpawnInfo.Player(occupation, characterSettings, CustomNetworkManager.Instance.humanPlayerPrefab,
                                    SpawnDestination.At(spawnPos), naked: naked);

        Spawn._ServerFireClientServerSpawnHooks(SpawnResult.Single(info, newPlayer));

        return(newPlayer);
    }
    public static UpdateConnectedPlayersMessage Send(GameObject[] players)
    {
        UpdateConnectedPlayersMessage msg = new UpdateConnectedPlayersMessage();

        msg.Players = players;

        msg.SendToAll();
        return(msg);
    }
        private async Task ServerSetUpPlayer(
            string unverifiedClientId,
            string unverifiedUsername,
            string unverifiedUserid,
            int unverifiedClientVersion,
            string unverifiedToken)
        {
            Logger.LogFormat("A joinedviewer called CmdServerSetupPlayer on this server, Unverified ClientId: {0} Unverified Username: {1}",
                             Category.Connections,
                             unverifiedClientId, unverifiedUsername);

            // Register player to player list (logging code exists in PlayerList so no need for extra logging here)
            var unverifiedConnPlayer = PlayerList.Instance.AddOrUpdate(new ConnectedPlayer
            {
                Connection   = connectionToClient,
                GameObject   = gameObject,
                Username     = unverifiedUsername,
                Job          = JobType.NULL,
                ClientId     = unverifiedClientId,
                UserId       = unverifiedUserid,
                ConnectionIP = connectionToClient.address
            });

            // this validates Userid and Token
            var isValidPlayer =
                await PlayerList.Instance.ValidatePlayer(unverifiedClientVersion, unverifiedConnPlayer, unverifiedToken);

            if (isValidPlayer == false)
            {
                ClearCache();
                PlayerList.Instance.Remove(unverifiedConnPlayer);
                Logger.LogWarning($"Set up new player: invalid player. For {unverifiedUsername}", Category.Connections);
                return;
            }

            //Add player to the list of current round players
            PlayerList.Instance.AddToRoundPlayers(unverifiedConnPlayer);

            //Send to client their job ban entries
            var jobBanEntries = PlayerList.Instance.ClientAskingAboutJobBans(unverifiedConnPlayer);

            PlayerList.ServerSendsJobBanDataMessage.Send(unverifiedConnPlayer.Connection, jobBanEntries);

            //Send to client the current crew job counts
            if (CrewManifestManager.Instance != null)
            {
                SetJobCountsMessage.SendToPlayer(CrewManifestManager.Instance.Jobs, unverifiedConnPlayer);
            }

            UpdateConnectedPlayersMessage.Send();

            IsValidPlayerAndWaitingOnLoad = true;
            STUnverifiedClientId          = unverifiedClientId;
            STVerifiedUserid     = unverifiedUserid;         //Is validated within  PlayerList.Instance.ValidatePlayer(
            STVerifiedConnPlayer = unverifiedConnPlayer;
        }
Beispiel #11
0
    public static void RespawnPlayer(NetworkConnection conn, short playerControllerId, JobType jobType)
    {
        GameObject player = CreatePlayer(jobType);

        NetworkServer.ReplacePlayerForConnection(conn, player, playerControllerId);

        Dictionary <string, GameObject> connectedPlayers = PlayerList.Instance.connectedPlayers;

        //Notify all clients that connected players list should be updated
        GameObject[] players = new GameObject[connectedPlayers.Count];
        connectedPlayers.Values.CopyTo(players, 0);
        UpdateConnectedPlayersMessage.Send(players);
    }
Beispiel #12
0
    private void CmdTrySetInitialName(string name)
    {
        //			Logger.Log($"TrySetName {name}");
        if (PlayerList.Instance != null)
        {
            var player = PlayerList.Instance.Get(connectionToClient);
            player.Name = name;

            playerName = player.Name;
            PlayerList.Instance.TryAddScores(player.Name);
            UpdateConnectedPlayersMessage.Send();
        }
    }
Beispiel #13
0
    private void TryMoveClientToOfflineList(ConnectedPlayer player)
    {
        if (!loggedIn.Contains(player))
        {
            Logger.Log($"Player with name {player.Name} was not found in online player list. " +
                       $"Verifying player lists for integrity...", Category.Connections);
            ValidatePlayerListRecords();
            return;
        }

        Logger.Log($"Added {player.Name} to offline player list.", Category.Connections);
        loggedOff.Add(player);
        loggedIn.Remove(player);
        UpdateConnectedPlayersMessage.Send();
        CheckRcon();
    }
Beispiel #14
0
    public override void OnServerDisconnect(NetworkConnection conn)
    {
        if (conn != null && conn.playerControllers.Count > 0)
        {
            GameObject player = conn.playerControllers[0].gameObject;
            PlayerList.Instance.RemovePlayer(player.name);

            //Notify clients that the connected players list should be updated
            GameObject[] players = new GameObject[PlayerList.Instance.connectedPlayers.Count];
            PlayerList.Instance.connectedPlayers.Values.CopyTo(players, 0);
            UpdateConnectedPlayersMessage.Send(players);

            //TODO DROP ALL HIS OBJECTS
            Debug.Log("PlayerDisconnected: " + conn.playerControllers[0].gameObject.name);
            NetworkServer.Destroy(conn.playerControllers[0].gameObject);
        }
    }
Beispiel #15
0
    private void CmdServerSetupPlayer(ulong steamId, string clientID, string username)
    {
        var connPlayer = new ConnectedPlayer
        {
            Connection = connectionToClient,
            GameObject = gameObject,
            Username   = username,
            Job        = JobType.NULL,
            SteamId    = steamId,
            ClientId   = clientID
        };

        //Add player to player list
        PlayerList.Instance.Add(connPlayer);

        // Sync all player data and the connected player count
        CustomNetworkManager.Instance.SyncPlayerData(gameObject);
        UpdateConnectedPlayersMessage.Send();

        // If they have a player to rejoin send the client the player to rejoin, otherwise send a null gameobject.
        var loggedOffPlayer = PlayerList.Instance.TakeLoggedOffPlayer(clientID);

        if (loggedOffPlayer == null)
        {
            //This is the players first time connecting to this round, assign them a Client ID;
            clientID            = System.Guid.NewGuid().ToString();
            connPlayer.ClientId = clientID;
        }

        // Only sync the pre-round countdown if it's already started
        if (GameManager.Instance.CurrentRoundState == RoundState.PreRound)
        {
            if (GameManager.Instance.waitForStart)
            {
                TargetSyncCountdown(connectionToClient, GameManager.Instance.waitForStart, GameManager.Instance.startTime);
            }
            else
            {
                GameManager.Instance.CheckPlayerCount();
            }
        }

        TargetLocalPlayerSetupPlayer(connectionToClient, loggedOffPlayer, clientID, GameManager.Instance.CurrentRoundState);
    }
    public static UpdateConnectedPlayersMessage Send()
    {
        UpdateConnectedPlayersMessage msg = new UpdateConnectedPlayersMessage();
        var prepareConnectedPlayers       = new List <ClientConnectedPlayer>();

        foreach (ConnectedPlayer c in PlayerList.Instance.AllPlayers)
        {
            prepareConnectedPlayers.Add(new ClientConnectedPlayer
            {
                Name = c.Name,
                Job  = c.Job
            });
        }

        msg.Players = prepareConnectedPlayers.ToArray();

        msg.SendToAll();
        return(msg);
    }
Beispiel #17
0
    public static void RespawnPlayer(NetworkConnection conn, short playerControllerId, JobType jobType, CharacterSettings characterSettings, GameObject oldBody)
    {
        GameObject player = CreatePlayer(jobType);

        TransferPlayer(conn, playerControllerId, player, oldBody, EVENT.PlayerSpawned, characterSettings);
        new Mind(player, jobType);
        var equipment = player.GetComponent <Equipment>();

        var playerScript    = player.GetComponent <PlayerScript>();
        var connectedPlayer = PlayerList.Instance.Get(conn);

        connectedPlayer.Name = playerScript.playerName;
        UpdateConnectedPlayersMessage.Send();
        PlayerList.Instance.TryAddScores(playerScript.playerName);

        equipment.SetPlayerLoadOuts();
        if (jobType != JobType.SYNDICATE && jobType != JobType.AI)
        {
            SecurityRecordsManager.Instance.AddRecord(playerScript, jobType);
        }
    }
Beispiel #18
0
    public static UpdateConnectedPlayersMessage Send()
    {
        Logger.LogFormat("This server informing all clients of the new PlayerList state: {0}", Category.Connections,
                         string.Join(",", PlayerList.Instance.AllPlayers));
        UpdateConnectedPlayersMessage msg = new UpdateConnectedPlayersMessage();
        var prepareConnectedPlayers       = new List <ClientConnectedPlayer>();

        foreach (ConnectedPlayer c in PlayerList.Instance.AllPlayers)
        {
            prepareConnectedPlayers.Add(new ClientConnectedPlayer
            {
                Name = c.Name,
                Job  = c.Job
            });
        }

        msg.Players = prepareConnectedPlayers.ToArray();

        msg.SendToAll();
        return(msg);
    }
    private async void ServerSetUpPlayer(
        string unverifiedClientId,
        string unverifiedUsername,
        string unverifiedUserid,
        int unverifiedClientVersion,
        string unverifiedToken)
    {
        Logger.LogFormat("A joinedviewer called CmdServerSetupPlayer on this server, Unverified ClientId: {0} Unverified Username: {1}",
                         Category.Connections,
                         unverifiedClientId, unverifiedUsername);

        //Register player to player list (logging code exists in PlayerList so no need for extra logging here)
        var unverifiedConnPlayer = PlayerList.Instance.AddOrUpdate(new ConnectedPlayer
        {
            Connection = connectionToClient,
            GameObject = gameObject,
            Username   = unverifiedUsername,
            Job        = JobType.NULL,
            ClientId   = unverifiedClientId,
            UserId     = unverifiedUserid
        });

        //this validates Userid and Token
        var isValidPlayer = await PlayerList.Instance.ValidatePlayer(unverifiedClientId, unverifiedUsername,
                                                                     unverifiedUserid, unverifiedClientVersion, unverifiedConnPlayer, unverifiedToken);

        if (isValidPlayer == false)
        {
            Logger.LogWarning("Set up new player: invalid player.");
            return;
        }

        // Check if they have a player to rejoin. If not, assign them a new client ID.
        GameObject loggedOffPlayer = PlayerList.Instance.TakeLoggedOffPlayerbyClientId(unverifiedClientId);

        // If the player does not yet have an in-game object to control, they'll probably have a
        // JoinedViewer assigned as they were only in the lobby. If so, destroy it and use the new one.
        if (loggedOffPlayer != null)
        {
            var checkForViewer = loggedOffPlayer.GetComponent <JoinedViewer>();
            if (checkForViewer)
            {
                Destroy(loggedOffPlayer);
                loggedOffPlayer = null;
            }
        }

        UpdateConnectedPlayersMessage.Send();

        // Only sync the pre-round countdown if it's already started.
        if (GameManager.Instance.CurrentRoundState == RoundState.PreRound)
        {
            if (GameManager.Instance.waitForStart)
            {
                TargetSyncCountdown(connectionToClient, GameManager.Instance.waitForStart, GameManager.Instance.CountdownEndTime);
            }
            else
            {
                GameManager.Instance.CheckPlayerCount();
            }
        }

        // If there's a logged off player, we will force them to rejoin. Previous logic allowed client to re-enter
        // their body or not, which should not be up to the client!
        if (loggedOffPlayer != null)
        {
            StartCoroutine(WaitForLoggedOffObserver(loggedOffPlayer));
        }
        else
        {
            TargetLocalPlayerSetupNewPlayer(connectionToClient, GameManager.Instance.CurrentRoundState);
        }

        PlayerList.Instance.CheckAdminState(unverifiedConnPlayer, unverifiedUserid);
        PlayerList.Instance.CheckMentorState(unverifiedConnPlayer, unverifiedUserid);

        PlayerList.ClientJobBanDataMessage.Send(unverifiedUserid);
    }
Beispiel #20
0
    private void CmdServerSetupPlayer(string clientID, string username, string userid, int clientVersion)
    {
        Logger.LogFormat("A joinedviewer called CmdServerSetupPlayer on this server, clientID: {0} username: {1}", Category.Connections,
                         clientID, username);

        //Register player to player list (logging code exists in PlayerList so no need for extra logging here)
        var connPlayer = PlayerList.Instance.AddOrUpdate(new ConnectedPlayer
        {
            Connection = connectionToClient,
            GameObject = gameObject,
            Username   = username,
            Job        = JobType.NULL,
            ClientId   = clientID
        });

        if (!PlayerList.Instance.ValidatePlayer(clientID, username, userid, clientVersion, connPlayer))
        {
            return;
        }

        // Check if they have a player to rejoin. If not, assign them a new client ID
        var loggedOffPlayer = PlayerList.Instance.TakeLoggedOffPlayer(clientID);

        if (loggedOffPlayer == null)
        {
            //This is the players first time connecting to this round, assign them a Client ID;
            var oldID = clientID;
            clientID = System.Guid.NewGuid().ToString();
            Logger.LogFormat("This server did not find a logged off player with clientID {0}, assigning" +
                             " joined viewer a new ID {1}", Category.Connections, oldID, clientID);
        }

        // Sync all player data and the connected player count
        CustomNetworkManager.Instance.SyncPlayerData(gameObject);
        UpdateConnectedPlayersMessage.Send();



        // Only sync the pre-round countdown if it's already started
        if (GameManager.Instance.CurrentRoundState == RoundState.PreRound)
        {
            if (GameManager.Instance.waitForStart)
            {
                TargetSyncCountdown(connectionToClient, GameManager.Instance.waitForStart, GameManager.Instance.startTime);
            }
            else
            {
                GameManager.Instance.CheckPlayerCount();
            }
        }

        //if there's a logged off player, we will force them to rejoin. Previous logic allowed client to reenter
        //their body or not, which should not be up to the client!
        if (loggedOffPlayer != null)
        {
            PlayerSpawn.ServerRejoinPlayer(this, loggedOffPlayer);
        }
        else
        {
            TargetLocalPlayerSetupNewPlayer(connectionToClient, connPlayer.ClientId, GameManager.Instance.CurrentRoundState);
        }
    }
Beispiel #21
0
    /// <summary>
    /// Spawns a new player character and transfers the connection's control into the new body.
    /// If existingMind is null, creates the new mind and assigns it to the new body.
    ///
    /// Fires server and client side player spawn hooks.
    /// </summary>
    /// <param name="connection">connection to give control to the new player character</param>
    /// <param name="occupation">occupation of the new player character</param>
    /// <param name="characterSettings">settings of the new player character</param>
    /// <param name="existingMind">existing mind to transfer to the new player, if null new mind will be created
    /// and assigned to the new player character</param>
    /// <param name="spawnPos">world position to spawn at</param>
    /// <param name="spawnItems">If spawning a player, should the player spawn without the defined initial equipment for their occupation?</param>
    /// <param name="willDestroyOldBody">if true, indicates the old body is going to be destroyed rather than pooled,
    /// thus we shouldn't send any network message which reference's the old body's ID since it won't exist.</param>
    ///
    /// <returns>the spawned object</returns>
    private static GameObject ServerSpawnInternal(NetworkConnection connection, Occupation occupation, CharacterSettings characterSettings,
                                                  Mind existingMind, Vector3Int?spawnPos = null, bool spawnItems = true, bool willDestroyOldBody = false)
    {
        //determine where to spawn them
        if (spawnPos == null)
        {
            Transform spawnTransform;
            //Spawn normal location for special jobs or if less than 2 minutes passed
            if (GameManager.Instance.stationTime < ARRIVALS_SPAWN_TIME || occupation.LateSpawnIsArrivals == false)
            {
                spawnTransform = GetSpawnForJob(occupation.JobType);
            }
            else
            {
                spawnTransform = GetSpawnForLateJoin(occupation.JobType);
                //Fallback to assistant spawn location if none found for late join
                if (spawnTransform == null && occupation.JobType != JobType.NULL)
                {
                    spawnTransform = GetSpawnForJob(JobType.ASSISTANT);
                }
            }

            if (spawnTransform == null)
            {
                Logger.LogErrorFormat(
                    "Unable to determine spawn position for connection {0} occupation {1}. Cannot spawn player.",
                    Category.ItemSpawn,
                    connection.address, occupation.DisplayName);
                return(null);
            }

            spawnPos = spawnTransform.transform.position.CutToInt();
        }

        //create the player object
        var newPlayer       = ServerCreatePlayer(spawnPos.GetValueOrDefault());
        var newPlayerScript = newPlayer.GetComponent <PlayerScript>();

        //get the old body if they have one.
        var oldBody = existingMind?.GetCurrentMob();

        //transfer control to the player object
        ServerTransferPlayer(connection, newPlayer, oldBody, EVENT.PlayerSpawned, characterSettings, willDestroyOldBody);


        if (existingMind == null)
        {
            //create the mind of the player
            Mind.Create(newPlayer, occupation);
        }
        else
        {
            //transfer the mind to the new body
            existingMind.SetNewBody(newPlayerScript);
        }


        var ps = newPlayer.GetComponent <PlayerScript>();
        var connectedPlayer = PlayerList.Instance.Get(connection);

        connectedPlayer.Name = ps.playerName;
        connectedPlayer.Job  = ps.mind.occupation.JobType;
        UpdateConnectedPlayersMessage.Send();

        //fire all hooks
        var info = SpawnInfo.Player(occupation, characterSettings, CustomNetworkManager.Instance.humanPlayerPrefab,
                                    SpawnDestination.At(spawnPos), spawnItems: spawnItems);

        Spawn._ServerFireClientServerSpawnHooks(SpawnResult.Single(info, newPlayer));

        return(newPlayer);
    }
    private async void ServerSetUpPlayer(
        string unverifiedClientId,
        string unverifiedUsername,
        string unverifiedUserid,
        int unverifiedClientVersion,
        string unverifiedToken)
    {
        Logger.LogFormat("A joinedviewer called CmdServerSetupPlayer on this server, Unverified ClientId: {0} Unverified Username: {1}",
                         Category.Connections,
                         unverifiedClientId, unverifiedUsername);

        //Register player to player list (logging code exists in PlayerList so no need for extra logging here)
        var unverifiedConnPlayer = PlayerList.Instance.AddOrUpdate(new ConnectedPlayer
        {
            Connection = connectionToClient,
            GameObject = gameObject,
            Username   = unverifiedUsername,
            Job        = JobType.NULL,
            ClientId   = unverifiedClientId,
            UserId     = unverifiedUserid
        });

        var isValidPlayer = await PlayerList.Instance.ValidatePlayer(unverifiedClientId, unverifiedUsername,
                                                                     unverifiedUserid, unverifiedClientVersion, unverifiedConnPlayer, unverifiedToken);

        if (!isValidPlayer)
        {
            return;                         //this validates Userid and Token
        }
        // Check if they have a player to rejoin. If not, assign them a new client ID
        var loggedOffPlayer = PlayerList.Instance.TakeLoggedOffPlayerbyClientId(unverifiedClientId);

        if (loggedOffPlayer != null)
        {
            var checkForViewer = loggedOffPlayer.GetComponent <JoinedViewer>();
            if (checkForViewer)
            {
                Destroy(loggedOffPlayer);
                loggedOffPlayer = null;
            }
        }

        // Sync all player data and the connected player count
        CustomNetworkManager.Instance.SyncPlayerData(gameObject);
        UpdateConnectedPlayersMessage.Send();

        // Only sync the pre-round countdown if it's already started
        if (GameManager.Instance.CurrentRoundState == RoundState.PreRound)
        {
            if (GameManager.Instance.waitForStart)
            {
                // Calculate when the countdown will end relative to the NetworkTime
                double endTime = NetworkTime.time + GameManager.Instance.CountdownTime;
                TargetSyncCountdown(connectionToClient, GameManager.Instance.waitForStart, endTime);
            }
            else
            {
                GameManager.Instance.CheckPlayerCount();
            }
        }

        //if there's a logged off player, we will force them to rejoin. Previous logic allowed client to reenter
        //their body or not, which should not be up to the client!
        if (loggedOffPlayer != null)
        {
            PlayerSpawn.ServerRejoinPlayer(this, loggedOffPlayer);
        }
        else
        {
            TargetLocalPlayerSetupNewPlayer(connectionToClient,
                                            GameManager.Instance.CurrentRoundState);
        }

        PlayerList.Instance.CheckAdminState(unverifiedConnPlayer, unverifiedUserid);
    }
    private async void ServerSetUpPlayer(
        string unverifiedClientId,
        string unverifiedUsername,
        string unverifiedUserid,
        int unverifiedClientVersion,
        string unverifiedToken)
    {
        Logger.LogFormat("A joinedviewer called CmdServerSetupPlayer on this server, Unverified ClientId: {0} Unverified Username: {1}",
                         Category.Connections,
                         unverifiedClientId, unverifiedUsername);

        // Register player to player list (logging code exists in PlayerList so no need for extra logging here)
        var unverifiedConnPlayer = PlayerList.Instance.AddOrUpdate(new ConnectedPlayer
        {
            Connection = connectionToClient,
            GameObject = gameObject,
            Username   = unverifiedUsername,
            Job        = JobType.NULL,
            ClientId   = unverifiedClientId,
            UserId     = unverifiedUserid
        });

        // this validates Userid and Token
        var isValidPlayer = await PlayerList.Instance.ValidatePlayer(unverifiedClientId, unverifiedUsername,
                                                                     unverifiedUserid, unverifiedClientVersion, unverifiedConnPlayer, unverifiedToken);

        if (isValidPlayer == false)
        {
            Logger.LogWarning($"Set up new player: invalid player. For {unverifiedUsername}", Category.Connections);
            return;
        }

        //Send to client their job ban entries
        var jobBanEntries = PlayerList.Instance.ClientAskingAboutJobBans(unverifiedConnPlayer);

        PlayerList.ServerSendsJobBanDataMessage.Send(unverifiedConnPlayer.Connection, jobBanEntries);

        //Send to client the current crew job counts
        if (CrewManifestManager.Instance != null)
        {
            SetJobCountsMessage.SendToPlayer(CrewManifestManager.Instance.Jobs, unverifiedConnPlayer);
        }

        UpdateConnectedPlayersMessage.Send();

        // Only sync the pre-round countdown if it's already started.
        if (GameManager.Instance.CurrentRoundState == RoundState.PreRound)
        {
            if (GameManager.Instance.waitForStart)
            {
                TargetSyncCountdown(connectionToClient, GameManager.Instance.waitForStart, GameManager.Instance.CountdownEndTime);
            }
            else
            {
                GameManager.Instance.CheckPlayerCount();
            }
        }

        // Check if they have a player to rejoin before creating a new ConnectedPlayer
        var loggedOffPlayer = PlayerList.Instance.RemovePlayerbyClientId(unverifiedClientId, unverifiedUserid, unverifiedConnPlayer);
        var checkForViewer  = loggedOffPlayer?.GameObject.GetComponent <JoinedViewer>();

        if (checkForViewer)
        {
            Destroy(loggedOffPlayer.GameObject);
            loggedOffPlayer = null;
        }

        // If there's a logged off player, we will force them to rejoin their body
        if (loggedOffPlayer == null)
        {
            TargetLocalPlayerSetupNewPlayer(connectionToClient, GameManager.Instance.CurrentRoundState);
        }
        else
        {
            StartCoroutine(WaitForLoggedOffObserver(loggedOffPlayer.GameObject));
        }

        PlayerList.Instance.CheckAdminState(unverifiedConnPlayer, unverifiedUserid);
        PlayerList.Instance.CheckMentorState(unverifiedConnPlayer, unverifiedUserid);
    }