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();
                }
            }
        }
        internal void Start()
        {
            byte[] bytes = new byte[4];
            using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) //Why not?
            {
                rng.GetBytes(bytes);
            }

            Random random = new Random(BitConverter.ToInt32(bytes, 0));

            HashSet <string> events = new HashSet <string>();

            if (this.LevelData.Snow > random.NextDouble() * 100)
            {
                events.Add("snow");
            }

            if (this.LevelData.Wind > random.NextDouble() * 100)
            {
                events.Add("wind");
            }

            if (this.LevelData.Mode != LevelMode.KingOfTheHat)
            {
                if (this.LevelData.Sfchm > random.NextDouble() * 100)
                {
                    events.Add("sfchm");

                    foreach (MatchPlayer player in this.Players.Values)
                    {
                        this.AddHatToPlayer(player, Hat.Cowboy, player.UserData.CurrentHatColor, false);
                    }
                }
                else
                {
                    foreach (MatchPlayer player in this.Players.Values)
                    {
                        if (player.UserData.CurrentHat != Hat.None)
                        {
                            this.AddHatToPlayer(player, player.UserData.CurrentHat, player.UserData.CurrentHatColor, false);
                        }
                        else if (this.LevelData.Mode == LevelMode.HatAttack)
                        {
                            this.AddHatToPlayer(player, Hat.CardboardBox, player.UserData.CurrentHatColor, false);
                        }
                    }
                }
            }
            else
            {
                Hat   hat      = (Hat)this.LevelData.KingOfTheHat[0];
                Color hatColor = Color.FromArgb((int)this.LevelData.KingOfTheHat[1]);
                foreach (Point point in this.FinishBlocks)
                {
                    this.DropHat(hat, hatColor, (point.X * 40) + 20, (point.Y * 40) + 20);
                }
            }

            if (this.LevelData.Alien > random.NextDouble() * 100)
            {
                events.Add("aliens");
            }

            if (this.LevelData.HasPrize && this.LevelData.Prizes.Count > 0)
            {
                (string prizeType, uint prizeId) = this.LevelData.Prizes[random.Next(this.LevelData.Prizes.Count)];

                this.Prize = new MatchPrize(prizeType, prizeId, rewardsExpBonus: false);
            }
            else if (this.Players.Count >= 2)
            {
                int diffIpsCount = this.Players.Values.Select((p) => p.IPAddress).Distinct().Count();
                if (diffIpsCount >= 2)
                {
                    double change = diffIpsCount * 5;
                    if (diffIpsCount >= 4)
                    {
                        if (random.Next(40 / diffIpsCount) == 0)
                        {
                            change *= 2;
                        }
                    }

                    if (change > random.NextDouble() * 100)
                    {
                        Part specialPartId = Part.None;
                        if (random.Next(2) == 0)
                        {
                            DateTimeOffset now = DateTimeOffset.UtcNow;
                            if (now >= new DateTimeOffset(now.Year, 10, 24, 0, 0, 0, TimeSpan.Zero) && now < new DateTimeOffset(now.Year, 11, 7, 0, 0, 0, TimeSpan.Zero)) //Halloween
                            {
                                specialPartId = Part.Ghost;
                            }
                            else if (now >= new DateTimeOffset(now.Year, 11, 17, 0, 0, 0, TimeSpan.Zero) && now < new DateTimeOffset(now.Year, 12, 1, 0, 0, 0, TimeSpan.Zero)) //Thanksgiving
                            {
                                specialPartId = Part.Turkey;
                            }
                            else if (now >= new DateTimeOffset(now.Year, 12, 1, 0, 0, 0, TimeSpan.Zero) && now < new DateTimeOffset(now.Year + 1, 1, 1, 0, 0, 0, TimeSpan.Zero)) //Christmas
                            {
                                specialPartId = Part.Reindeer;
                            }
                        }

                        if (specialPartId != Part.None)
                        {
                            uint headsCount = 0;
                            uint bodysCount = 0;
                            uint feetsCount = 0;
                            foreach (MatchPlayer player in this.Players.Values)
                            {
                                if (player.UserData.HasHead(specialPartId))
                                {
                                    headsCount++;
                                }

                                if (player.UserData.HasBody(specialPartId))
                                {
                                    bodysCount++;
                                }

                                if (player.UserData.HasFeet(specialPartId))
                                {
                                    feetsCount++;
                                }
                            }

                            if (this.Players.Count != headsCount || this.Players.Count != bodysCount || this.Players.Count != feetsCount)
                            {
                                HashSet <string> possiblities = new HashSet <string>();
                                if (this.Players.Count != headsCount)
                                {
                                    possiblities.Add("head");
                                }

                                if (this.Players.Count != bodysCount)
                                {
                                    possiblities.Add("body");
                                }

                                if (this.Players.Count != feetsCount)
                                {
                                    possiblities.Add("feet");
                                }

                                this.Prize = new MatchPrize(possiblities.OrderBy((p) => random.NextDouble()).First(), (uint)specialPartId);
                            }
                        }

                        if (this.Prize == null)
                        {
                            if (random.Next(3333) == 0)
                            {
                                this.Prize = new MatchPrize("hat", (uint)new Hat[]
                                {
                                    Hat.None,
                                    Hat.BaseballCap,
                                    Hat.Cowboy,
                                    Hat.Crown
                                }.OrderBy((h) => random.NextDouble()).First());
                            }
                        }

                        if (this.Prize == null) //No special part was choosen, fallback to default
                        {
                            double prizeTypeChance = random.NextDouble() * 100;
                            if (prizeTypeChance >= 0 && prizeTypeChance <= 89) //Parts
                            {
                                Part part = new Part[]
                                {
                                    Part.Brain,
                                    Part.Cactus,
                                    Part.Cthulhu,
                                    Part.Dino,
                                    Part.Eye,
                                    Part.Hare,
                                    Part.Monster,
                                    Part.Mushroom,
                                    Part.Panda,
                                    Part.Platypus,
                                    Part.Robot,
                                    Part.Skeleton,
                                    Part.Spartan,
                                    Part.Tiki,
                                    Part.Tortoise,
                                    Part.Penguin,
                                    Part.Dragon,
                                    Part.Meteor,
                                    Part.Donkey,
                                    Part.Sword,
                                    Part.Snowman,
                                }.OrderBy((p) => random.NextDouble()).First();

                                if (prizeTypeChance > 40 && prizeTypeChance <= 67) //Body
                                {
                                    this.Prize = new MatchPrize("body", (uint)part);
                                }
                                else if (prizeTypeChance > 67 && prizeTypeChance <= 89) //Head
                                {
                                    this.Prize = new MatchPrize("head", (uint)part);
                                }
                                else //Feet
                                {
                                    this.Prize = new MatchPrize("feet", (uint)part);
                                }
                            }
                            else if (prizeTypeChance > 89 && prizeTypeChance <= 100) //Hat
                            {
                                Hat hat = new Hat[]
                                {
                                    Hat.Propeller,
                                    Hat.Santa,
                                    Hat.PedroTheSnail,
                                    Hat.Top,
                                    Hat.Party,
                                    Hat.Pirate,
                                    Hat.Nurse,
                                    Hat.Bouncy,
                                    Hat.Shark,
                                    Hat.Happy,
                                    Hat.Police,
                                    Hat.Ushanka,
                                    Hat.Toque,
                                    Hat.Fez,
                                    Hat.Witch,
                                    Hat.Halo,
                                }.OrderBy((h) => random.NextDouble()).First();

                                this.Prize = new MatchPrize("hat", (uint)hat);
                            }
                        }
                    }
                }
            }

            if (this.Prize != null)
            {
                this.Clients.SendPacket(new PrizeOutgoingMessage(this.Prize, "available"));
            }

            new Timer(this.GameBegun, null, 2664, 0);

            this.Clients.SendPackets(new EventsOutgoingMessage(events), new BeginMatchOutgoingMessage());

            LevelManager.AddPlaysAsync(this.LevelData.Id, (uint)this.Players.Count); //Lets do the most important thing now!
        }