public static (uint rank, ulong exp) GetRankAndExpFromTotalExp(ulong totalExp)
    {
        uint currentRank = 0;

        while (totalExp > 0)
        {
            ulong expNeeded = ExpUtils.GetNextRankExpRequirement(currentRank);

            try
            {
                checked
                {
                    totalExp -= expNeeded;
                    currentRank++;

                    if (totalExp == 0)
                    {
                        break;
                    }
                }
            }
            catch (OverflowException)
            {
                break;
            }
        }

        return(currentRank, totalExp);
    }
        internal void FinishMatch(ClientSession session)
        {
            if (this.StartedOn != null)
            {
                double now = this.StartedOn.Elapsed.TotalSeconds;
                if (this.LevelData.Seconds > 0 && this.LevelData.Mode != LevelMode.KingOfTheHat && now > (this.LevelData.Seconds + 5)) //Small threashold
                {
                    return;
                }

                if (this.Players.TryGetValue(session.SocketId, out MatchPlayer player) && !player.Forfiet && player.FinishTime == null)
                {
                    player.FinishTime = now;

                    this.Clients.SendPacket(new PlayerFinishedOutgoingMessage(session.SocketId, (IReadOnlyCollection <MatchPlayer>) this.Players.Values));

                    ulong expEarned = ExpUtils.GetExpEarnedForFinishing(now);

                    List <object[]> expArray = new List <object[]>();
                    if (this.LevelData.Mode == LevelMode.Race)
                    {
                        expArray.Add(new object[] { "Level completed", expEarned });
                    }
                    else if (this.LevelData.Mode == LevelMode.Deathmatch)
                    {
                        expArray.Add(new object[] { "Fighting spirit", expEarned });
                    }
                    else if (this.LevelData.Mode == LevelMode.HatAttack)
                    {
                        expArray.Add(new object[] { "Hat owner", expEarned });
                    }
                    else if (this.LevelData.Mode == LevelMode.CoinFiend)
                    {
                        expArray.Add(new object[] { "Coin meizer", expEarned });
                    }
                    else if (this.LevelData.Mode == LevelMode.DamageDash)
                    {
                        expArray.Add(new object[] { "Damage dealer", expEarned });
                    }
                    else if (this.LevelData.Mode == LevelMode.KingOfTheHat)
                    {
                        expArray.Add(new object[] { "Hat holder", expEarned });
                    }

                    bool firstPlace = true;
                    foreach (MatchPlayer other in this.Players.Values)
                    {
                        if (other != player)
                        {
                            bool defeated = false;
                            switch (this.LevelData.Mode)
                            {
                            case LevelMode.Race:
                            case LevelMode.HatAttack:
                            case LevelMode.KingOfTheHat:
                                defeated = other.Forfiet || other.FinishTime == null;
                                break;

                            case LevelMode.Deathmatch:
                                defeated = other.Forfiet || other.FinishTime != null;
                                break;

                            case LevelMode.CoinFiend:
                                defeated = other.Forfiet || player.Coins > other.Coins;
                                break;

                            case LevelMode.DamageDash:
                                defeated = other.Forfiet || player.Dash > other.Dash;
                                break;
                            }

                            if (defeated)
                            {
                                ulong playerExp = (ulong)Math.Round(ExpUtils.GetExpForDefeatingPlayer(other.UserData.Rank) * ExpUtils.GetPlaytimeMultiplayer(other.FinishTime ?? now) * ExpUtils.GetKeyPressMultiplayer(other.KeyPresses));

                                if (other.IPAddress == player.IPAddress)
                                {
                                    playerExp /= 2;
                                }

                                expEarned += playerExp;
                                expArray.Add(new object[] { "Defeated " + other.UserData.Username, playerExp });
                            }
                            else
                            {
                                firstPlace = false;
                            }
                        }
                    }

                    ulong baseExp = expEarned;
                    if (firstPlace)
                    {
                        MatchPrize prize = Interlocked.Exchange(ref this.Prize, null);
                        if (prize != null)
                        {
                            bool partExp = false;

                            if (prize.Category == "hat")
                            {
                                if (player.UserData.HasHat((Hat)prize.Id))
                                {
                                    partExp = true;
                                }
                                else
                                {
                                    player.UserData.GiveHat((Hat)prize.Id);
                                }
                            }
                            else if (prize.Category == "head")
                            {
                                if (player.UserData.HasHead((Part)prize.Id))
                                {
                                    partExp = true;
                                }
                                else
                                {
                                    player.UserData.GiveHead((Part)prize.Id);
                                }
                            }
                            else if (prize.Category == "body")
                            {
                                if (player.UserData.HasBody((Part)prize.Id))
                                {
                                    partExp = true;
                                }
                                else
                                {
                                    player.UserData.GiveBody((Part)prize.Id);
                                }
                            }
                            else if (prize.Category == "feet")
                            {
                                if (player.UserData.HasFeet((Part)prize.Id))
                                {
                                    partExp = true;
                                }
                                else
                                {
                                    player.UserData.GiveFeet((Part)prize.Id);
                                }
                            }

                            if (!prize.RewardsExpBonus)
                            {
                                partExp = false;
                            }

                            if (partExp)
                            {
                                expEarned += (ulong)Math.Round(baseExp * 0.5);
                                expArray.Add(new object[] { "Prize bonus", "EXP X 1.5" });
                            }

                            session.SendPacket(new PrizeOutgoingMessage(prize, partExp ? "exp" : "got"));
                        }
                    }

                    float expHatBonus = 0;
                    foreach (MatchPlayerHat hat in player.Hats)
                    {
                        if (!hat.Spawned && hat.Hat == Hat.BaseballCap)
                        {
                            expHatBonus += expHatBonus == 0 ? 1f : 0.1f;
                        }
                    }

                    if (expHatBonus > 0)
                    {
                        expEarned += (ulong)Math.Round(baseExp * expHatBonus);
                        expArray.Add(new object[] { "Exp hat", $"EXP X {expHatBonus + 1}" });
                    }

                    ulong bonusExpDrained = 0;
                    if (player.UserData.BonusExp > 0)
                    {
                        bonusExpDrained = Math.Min(player.UserData.BonusExp, baseExp);
                        if (bonusExpDrained > 0)
                        {
                            expEarned += bonusExpDrained;
                            expArray.Add(new object[] { "Bonus exp", $"EXP X {(bonusExpDrained / baseExp) + 1}" });
                            expArray.Add(new object[] { "Bonus exp left", player.UserData.BonusExp - bonusExpDrained });

                            player.UserData.DrainBonusExp(bonusExpDrained);
                        }
                    }

                    session.SendPackets(new YouFinishedOutgoingMessage(session.UserData.Rank, session.UserData.Exp, ExpUtils.GetNextRankExpRequirement(session.UserData.Rank), expEarned, expArray));

                    player.UserData.AddExp(expEarned);

                    this.CheckGameState();
                }
            }
        }