示例#1
0
        BalanceTeamsResult LegacyBalance(int teamCount, BalanceMode mode, BattleContext b, params List <Account>[] unmovablePlayers)
        {
            var ret = new BalanceTeamsResult();

            try {
                ret.CanStart = true;
                ret.Players  = b.Players.ToList();

                var db          = new ZkDataContext();
                var nonSpecList = b.Players.Where(y => !y.IsSpectator).Select(y => (int?)y.LobbyID).ToList();
                var accs        =
                    db.Accounts.Where(x => nonSpecList.Contains(x.AccountID)).ToList();
                if (accs.Count < 1)
                {
                    ret.CanStart = false;
                    return(ret);
                }
                if (teamCount < 1)
                {
                    teamCount = 1;
                }
                if (teamCount > accs.Count)
                {
                    teamCount = accs.Count;
                }
                if (teamCount == 1)
                {
                    foreach (var p in ret.Players)
                    {
                        p.AllyID = 0;
                    }
                    return(ret);
                }

                maxTeamSize = (int)Math.Ceiling(accs.Count / (double)teamCount);

                teams.Clear();
                for (var i = 0; i < teamCount; i++)
                {
                    var team = new BalanceTeam();
                    teams.Add(team);
                    if (unmovablePlayers != null && unmovablePlayers.Length > i)
                    {
                        var unmovables = unmovablePlayers[i];
                        team.AddItem(new BalanceItem(unmovablePlayers[i].ToArray())
                        {
                            CanBeMoved = false
                        });
                        accs.RemoveAll(x => unmovables.Any(y => y.AccountID == x.AccountID));
                    }
                }

                balanceItems = new List <BalanceItem>();
                if (mode == BalanceMode.ClanWise)
                {
                    var clanGroups = accs.GroupBy(x => x.ClanID ?? x.AccountID).ToList();
                    if (teamCount > clanGroups.Count() || clanGroups.Any(x => x.Count() > maxTeamSize))
                    {
                        mode = BalanceMode.Normal;
                    }
                    else
                    {
                        balanceItems.AddRange(clanGroups.Select(x => new BalanceItem(x.ToArray())));
                    }
                }
                if (mode == BalanceMode.FactionWise)
                {
                    balanceItems.Clear();
                    var factionGroups = accs.GroupBy(x => x.FactionID ?? x.AccountID).ToList();
                    balanceItems.AddRange(factionGroups.Select(x => new BalanceItem(x.ToArray())));
                }

                if (mode == BalanceMode.Normal)
                {
                    balanceItems.Clear();
                    balanceItems.AddRange(accs.Select(x => new BalanceItem(x)));
                }

                var sw = new Stopwatch();
                sw.Start();
                RecursiveBalance(0);
                sw.Stop();

                if (bestTeams == null)
                {
                    var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                    fallback.Message += "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                    return(fallback);
                }

                var minSize    = bestTeams.Min(x => x.Count);
                var maxSize    = bestTeams.Max(x => x.Count);
                var sizesWrong = maxSize / (double)minSize > MaxTeamSizeDifferenceRatio;

                // cbalance failed, rebalance using normal
                if (mode == BalanceMode.ClanWise && (bestTeams == null || GetTeamsDifference(bestTeams) > MaxCbalanceDifference || sizesWrong))
                {
                    return(new Balancer().LegacyBalance(teamCount, BalanceMode.Normal, b, unmovablePlayers));
                }
                // cbalance failed, rebalance using normal

                if (sizesWrong && mode == BalanceMode.FactionWise)
                {
                    var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                    fallback.Message += "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                    return(fallback); // fallback standard balance if PW balance fails

                    /*ret.CanStart = false;
                     * ret.Message = string.Format("Failed to balance - too many people from same faction");
                     * return ret;*/
                }

                if (unmovablePlayers != null && unmovablePlayers.Length > 0)
                {
                    var minElo = bestTeams.Min(x => x.AvgElo);
                    var maxElo = bestTeams.Max(x => x.AvgElo);
                    if (maxElo - minElo > GlobalConst.MaxPwEloDifference)
                    {
                        var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                        fallback.Message += "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                        return(fallback); // fallback standard balance if PW balance fails

                        /*
                         * ret.CanStart = false;
                         * ret.Message = string.Format("Team difference is too big - win chance {0}% - spectate some or wait for more people",
                         *                          Utils.GetWinChancePercent(maxElo - minElo));
                         * return ret;*/
                    }
                }

                if (bestTeams == null)
                {
                    ret.CanStart = false;
                    ret.Message  =
                        string.Format(
                            "Failed to balance {0} - too many people from same clan or faction (in teams game you can try !random and !forcestart)");
                    return(ret);
                }
                else
                {
                    if (unmovablePlayers == null || unmovablePlayers.Length == 0)
                    {
                        bestTeams = bestTeams.Shuffle();                                                           // permute when not unmovable players present
                    }
                    var text = "( ";

                    var lastTeamElo = 0.0;
                    var allyNum     = 0;
                    foreach (var team in bestTeams)
                    {
                        if (allyNum > 0)
                        {
                            text += " : ";
                        }
                        text += string.Format("{0}", (allyNum + 1));
                        if (allyNum > 0)
                        {
                            text += string.Format("={0}%)", Utils.GetWinChancePercent(lastTeamElo - team.AvgElo));
                        }
                        lastTeamElo = team.AvgElo;

                        foreach (var u in team.Items.SelectMany(x => x.LobbyId))
                        {
                            ret.Players.Single(x => x.LobbyID == u).AllyID = allyNum;
                        }
                        allyNum++;
                    }
                    text += ")";

                    ret.Message = String.Format("{0} players balanced {2} to {1} teams {3}. {4} combinations checked, spent {5}ms of CPU time",
                                                bestTeams.Sum(x => x.Count),
                                                teamCount,
                                                mode,
                                                text,
                                                iterationsChecked,
                                                sw.ElapsedMilliseconds);
                }
            } catch (Exception ex) {
                Trace.TraceError(ex.ToString());
                ret.Message  = ex.ToString();
                ret.CanStart = false;
            }
            return(ret);
        }
示例#2
0
        static BalanceTeamsResult PerformBalance(BattleContext context,
                                                 bool isGameStart,
                                                 int?allyCount,
                                                 bool?clanWise,
                                                 AutohostConfig config,
                                                 int playerCount)
        {
            var res  = new BalanceTeamsResult();
            var mode = context.GetMode();

            using (var db = new ZkDataContext()) {
                if (!CheckPlayersMinimumConditions(context, db, config, ref res.Message))
                {
                    res.CanStart = false;
                    return(res);
                }

                switch (mode)
                {
                case AutohostMode.None:
                {
                    if (!isGameStart)
                    {
                        res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == true ? BalanceMode.ClanWise : BalanceMode.Normal, context);
                    }
                }
                break;

                case AutohostMode.Generic:
                {
                    if (allyCount == null && res.Bots != null && res.Bots.Any())
                    {
                        res.Players = context.Players.ToList();
                        res.Bots    = context.Bots.Where(x => x.Owner != context.AutohostName).ToList();
                        foreach (var p in res.Players)
                        {
                            p.AllyID = 0;
                        }
                        foreach (var b in res.Bots)
                        {
                            b.AllyID = 1;
                        }
                    }
                    else
                    {
                        var map = db.Resources.Single(x => x.InternalName == context.Map);
                        res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 2,
                                                           clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                                           context);
                        res.DeleteBots = mode == AutohostMode.Teams;
                    }
                    return(res);
                }

                case AutohostMode.Teams:
                {
                    var map = db.Resources.Single(x => x.InternalName == context.Map);
                    res            = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                    res.DeleteBots = mode == AutohostMode.Teams;
                    return(res);
                }

                case AutohostMode.Game1v1:
                {
                    res            = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                    res.DeleteBots = true;
                }
                break;

                case AutohostMode.GameChickens:
                {
                    res.Players = context.Players.ToList();
                    res.Bots    = context.Bots.Where(x => x.Owner != context.AutohostName).ToList();
                    foreach (var p in res.Players)
                    {
                        p.AllyID = 0;
                    }
                    foreach (var b in res.Bots)
                    {
                        b.AllyID = 1;
                    }

                    if (!res.Bots.Any() && res.Players.Count > 0)
                    {
                        res.Message  = "Add some bot (computer player) as your enemy. Use button on bottom left. Chicken or CAI is recommended.";
                        res.CanStart = false;

                        /*else
                         *      {
                         *          res.Bots.Add(new BotTeam() { AllyID = 1, TeamID = 16, BotName = "default_Chicken", BotAI = "Chicken: Normal", });
                         *          res.Message = "Adding a normal chickens bot for you";
                         *      }*/
                    }
                }
                break;

                case AutohostMode.GameFFA:
                {
                    var map = db.Resources.Single(x => x.InternalName == context.Map);
                    if (map.MapFFAMaxTeams != null)
                    {
                        res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams.Value,
                                                           clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                                           context);
                    }
                    else
                    {
                        res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 8,
                                                           clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                                           context);
                    }
                    return(res);
                }

                case AutohostMode.Planetwars:

                    return(new Balancer().PlanetwarsBalance(context));
                }
                return(res);
            }
        }
示例#3
0
        /// <summary>
        ///     Calls <see cref="LegacyBalance" /> with the appropriate parameters depending on game settings and conditions
        /// </summary>
        /// <param name="isGameStart">
        ///     If true and <see cref="AutohostMode" /> is none, do nothing (i.e. don't autobalance custom
        ///     rooms at start)
        /// </param>
        /// <param name="allyCount"></param>
        /// <param name="clanWise"></param>
        /// <param name="config"></param>
        /// <remarks>Also removes bots from team games, and tells people to add bots to a chicken game if absent</remarks>
        static BalanceTeamsResult PerformBalance(
            LobbyHostingContext context,
            bool isGameStart,
            int?allyCount,
            bool?clanWise)
        {
            var res  = new BalanceTeamsResult();
            var mode = context.Mode;

            using (var db = new ZkDataContext())
            {
                switch (mode)
                {
                case AutohostMode.None:
                {
                    if (!isGameStart)
                    {
                        if (allyCount == null || allyCount == 2)
                        {
                            res = PartitionBalance.BalanceInterface(2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                        }
                        else
                        {
                            res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == true ? BalanceMode.ClanWise : BalanceMode.Normal, context);
                        }
                    }
                }
                break;

                case AutohostMode.Teams:
                case AutohostMode.Game1v1:
                {
                    res            = PartitionBalance.BalanceInterface(2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                    res.DeleteBots = true;
                }
                break;

                case AutohostMode.GameChickens:
                {
                    res.Players = context.Players.ToList();
                    res.Bots    = context.Bots.ToList();
                    foreach (var p in res.Players)
                    {
                        p.AllyID = 0;
                    }
                    foreach (var b in res.Bots)
                    {
                        b.AllyID = 1;
                    }

                    // add chickens via modoptions hackish thingie
                    string chickBot = null;
                    if (context.ModOptions?.TryGetValue("chickenailevel", out chickBot) == true && !string.IsNullOrEmpty(chickBot) && chickBot != "none")
                    {
                        res.Bots.RemoveAll(x => x.BotAI.StartsWith("Chicken:"));
                        res.Bots.Add(new BotTeam()
                            {
                                AllyID = 1, BotName = "default_Chicken", BotAI = chickBot
                            });
                    }

                    if (!res.Bots.Any() && res.Players.Count > 0)
                    {
                        //res.Message = "Add some bot (computer player) as your enemy. Use button on bottom left. Chicken or CAI is recommended.";
                        var map = db.Resources.FirstOrDefault(x => x.InternalName == context.Map);
                        if (map?.MapIsChickens == true)
                        {
                            res.Bots.Add(new BotTeam()
                                {
                                    AllyID = 1, BotName = "default_Chicken", BotAI = "Chicken: Normal",
                                });
                        }
                        else
                        {
                            for (int i = 1; i <= res.Players.Where(x => !x.IsSpectator).Count(); i++)
                            {
                                res.Bots.Add(new BotTeam()
                                    {
                                        AllyID = 1, BotName = "cai" + i, BotAI = "CAI",
                                    });
                            }
                        }
                        res.Message = "Adding computer AI player for you";
                    }
                }
                break;

                case AutohostMode.GameFFA:
                {
                    res.DeleteBots = true;
                    var map = db.Resources.Single(x => x.InternalName == context.Map);
                    if (map.MapFFAMaxTeams != null)
                    {
                        res = new Balancer().LegacyBalance(
                            allyCount ?? map.MapFFAMaxTeams.Value,
                            clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                            context);
                    }
                    else
                    {
                        res = new Balancer().LegacyBalance(
                            allyCount ?? map.MapFFAMaxTeams ?? 8,
                            clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                            context);
                    }
                    return(res);
                }

                case AutohostMode.Planetwars:

                    return(new Balancer().PlanetwarsBalance(context));
                }
                return(res);
            }
        }
        /// <summary>
        ///     The function that actually moves players arounds
        /// </summary>
        /// <param name="teamCount"></param>
        /// <param name="mode"></param>
        /// <param name="b"></param>
        /// <param name="unmovablePlayers"></param>
        /// <returns></returns>
        BalanceTeamsResult LegacyBalance(int teamCount, BalanceMode mode, LobbyHostingContext b, params List<Account>[] unmovablePlayers)
        {
            var ret = new BalanceTeamsResult();

            if (b.IsMatchMakerGame) mode = BalanceMode.Party; // override, for matchmaker mode is always party
            
            try
            {
                ret.CanStart = true;
                ret.Players = b.Players.ToList();

                var db = new ZkDataContext();
                var nonSpecList = b.Players.Where(y => !y.IsSpectator).Select(y => (int?)y.LobbyID).ToList();
                var accs = db.Accounts.Where(x => nonSpecList.Contains(x.AccountID)).ToList();
                if (accs.Count < 1)
                {
                    ret.CanStart = false;
                    return ret;
                }
                if (teamCount < 1) teamCount = 1;
                if (teamCount > accs.Count) teamCount = accs.Count;
                if (teamCount == 1)
                {
                    foreach (var p in ret.Players) p.AllyID = 0;
                    return ret;
                }

                maxTeamSize = (int)Math.Ceiling(accs.Count / (double)teamCount);

                teams.Clear();
                for (var i = 0; i < teamCount; i++)
                {
                    var team = new BalanceTeam();
                    teams.Add(team);
                    if (unmovablePlayers != null && unmovablePlayers.Length > i)
                    {
                        var unmovables = unmovablePlayers[i];
                        team.AddItem(new BalanceItem(b.IsMatchMakerGame, unmovablePlayers[i].ToArray()) { CanBeMoved = false });
                        accs.RemoveAll(x => unmovables.Any(y => y.AccountID == x.AccountID));
                    }
                }

                balanceItems = new List<BalanceItem>();
                if (mode == BalanceMode.Party)
                {
                    var clanGroups = accs.GroupBy(x => b.Players.First(p => p.Name == x.Name).PartyID ?? x.AccountID).ToList();
                    if (teamCount > clanGroups.Count() || clanGroups.Any(x => x.Count() > maxTeamSize)) mode = BalanceMode.Normal;
                    else balanceItems.AddRange(clanGroups.Select(x => new BalanceItem(b.IsMatchMakerGame, x.ToArray())));
                }

                if (mode == BalanceMode.ClanWise)
                {
                    var clanGroups = accs.GroupBy(x => b.Players.First(p => p.Name == x.Name).PartyID ?? x.ClanID ?? x.AccountID).ToList();
                    if (teamCount > clanGroups.Count() || clanGroups.Any(x => x.Count() > maxTeamSize)) mode = BalanceMode.Normal;
                    else balanceItems.AddRange(clanGroups.Select(x => new BalanceItem(b.IsMatchMakerGame, x.ToArray())));
                }
                if (mode == BalanceMode.FactionWise)
                {
                    balanceItems.Clear();
                    var factionGroups = accs.GroupBy(x => x.FactionID ?? x.AccountID).ToList();
                    balanceItems.AddRange(factionGroups.Select(x => new BalanceItem(b.IsMatchMakerGame, x.ToArray())));
                }

                if (mode == BalanceMode.Normal)
                {
                    balanceItems.Clear();
                    balanceItems.AddRange(accs.Select(x => new BalanceItem(b.IsMatchMakerGame, x)));
                }

                var sw = new Stopwatch();
                sw.Start();
                RecursiveBalance(0);
                sw.Stop();

                if (bestTeams == null)
                {
                    var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                    fallback.Message += "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                    return fallback;
                }

                var minSize = bestTeams.Min(x => x.Count);
                var maxSize = bestTeams.Max(x => x.Count);
                var sizesWrong = maxSize / (double)minSize > MaxTeamSizeDifferenceRatio;

                // cbalance failed, rebalance using normal
                if (mode == BalanceMode.ClanWise && (bestTeams == null || GetTeamsDifference(bestTeams) > MaxCbalanceDifference || sizesWrong)) return new Balancer().LegacyBalance(teamCount, BalanceMode.Normal, b, unmovablePlayers);
                // cbalance failed, rebalance using normal

                if (sizesWrong && mode == BalanceMode.FactionWise)
                {
                    var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                    fallback.Message += "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                    return fallback; // fallback standard balance if PW balance fails
                    /*ret.CanStart = false;
                    ret.Message = string.Format("Failed to balance - too many people from same faction");
                    return ret;*/
                }

                if (unmovablePlayers != null && unmovablePlayers.Length > 0)
                {
                    var minElo = bestTeams.Min(x => x.AvgElo);
                    var maxElo = bestTeams.Max(x => x.AvgElo);
                    if (maxElo - minElo > GlobalConst.MaxPwEloDifference)
                    {
                        var fallback = new Balancer().LegacyBalance(teamCount, BalanceMode.ClanWise, b, null);
                        fallback.Message +=
                            "\nWarning: STANDARD TEAM BALANCE USED, PlanetWars not possible with those teams, too many from one faction";
                        return fallback; // fallback standard balance if PW balance fails
                        /*
                        ret.CanStart = false;
                        ret.Message = string.Format("Team difference is too big - win chance {0}% - spectate some or wait for more people",
                                                    Utils.GetWinChancePercent(maxElo - minElo));
                        return ret;*/
                    }
                }

                if (bestTeams == null)
                {
                    ret.CanStart = false;
                    ret.Message =
                        string.Format(
                            "Failed to balance {0} - too many people from same clan or faction (in teams game you can try !random and !forcestart)");
                    return ret;
                }
                if (unmovablePlayers == null || unmovablePlayers.Length == 0) bestTeams = bestTeams.Shuffle(); // permute when not unmovable players present

                var text = "( ";

                var lastTeamElo = 0.0;
                var allyNum = 0;
                foreach (var team in bestTeams)
                {
                    if (allyNum > 0) text += " : ";
                    text += string.Format("{0}", (allyNum + 1));
                    if (allyNum > 0) text += string.Format("={0}%)", Utils.GetWinChancePercent(lastTeamElo - team.AvgElo));
                    lastTeamElo = team.AvgElo;

                    foreach (var u in team.Items.SelectMany(x => x.LobbyId)) ret.Players.Single(x => x.LobbyID == u).AllyID = allyNum;
                    allyNum++;
                }
                text += ")";

                ret.Message = string.Format(
                    "{0} players balanced {2} to {1} teams {3}. {4} combinations checked, spent {5}ms of CPU time",
                    bestTeams.Sum(x => x.Count),
                    teamCount,
                    mode,
                    text,
                    iterationsChecked,
                    sw.ElapsedMilliseconds);
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
                ret.Message = ex.ToString();
                ret.CanStart = false;
            }
            return ret;
        }
        /// <summary>
        ///     Calls <see cref="LegacyBalance" /> with the appropriate parameters depending on game settings and conditions
        /// </summary>
        /// <param name="isGameStart">
        ///     If true and <see cref="AutohostMode" /> is none, do nothing (i.e. don't autobalance custom
        ///     rooms at start)
        /// </param>
        /// <param name="allyCount"></param>
        /// <param name="clanWise"></param>
        /// <param name="config"></param>
        /// <remarks>Also removes bots from team games, and tells people to add bots to a chicken game if absent</remarks>
        static BalanceTeamsResult PerformBalance(
            LobbyHostingContext context,
            bool isGameStart,
            int? allyCount,
            bool? clanWise)
        {
            var res = new BalanceTeamsResult();
            var mode = context.Mode;

            using (var db = new ZkDataContext())
            {
                switch (mode)
                {
                    case AutohostMode.None:
                        {
                            if (!isGameStart) res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == true ? BalanceMode.ClanWise : BalanceMode.Normal, context);
                        }
                        break;
                    case AutohostMode.Teams:
                    case AutohostMode.Game1v1:
                        {
                            res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                            res.DeleteBots = true;
                        }
                        break;
                    
                    case AutohostMode.GameChickens:
                        {
                            res.Players = context.Players.ToList();
                            res.Bots = context.Bots.ToList();
                            foreach (var p in res.Players) p.AllyID = 0;
                            foreach (var b in res.Bots) b.AllyID = 1;

                            // add chickens via modoptions hackish thingie
                            string chickBot = null;
                            if (context.ModOptions?.TryGetValue("chickenailevel", out chickBot) == true && !string.IsNullOrEmpty(chickBot) && chickBot != "none")
                            {
                                res.Bots.Add(new BotTeam() { AllyID = 1, BotName = "default_Chicken", BotAI = chickBot });
                            }

                            if (!res.Bots.Any() && res.Players.Count > 0)
                            {
                                //res.Message = "Add some bot (computer player) as your enemy. Use button on bottom left. Chicken or CAI is recommended.";
                                var map = db.Resources.FirstOrDefault(x => x.InternalName == context.Map);
                                if (map?.MapIsChickens == true) res.Bots.Add(new BotTeam() { AllyID = 1, BotName = "default_Chicken", BotAI = "Chicken: Normal", });
                                else res.Bots.Add(new BotTeam() { AllyID = 1, BotName = "cai", BotAI = "CAI", });
                                res.Message = "Adding computer AI player for you";
                            }
                        }
                        break;
                    case AutohostMode.GameFFA:
                        {
                            res.DeleteBots = true;
                            var map = db.Resources.Single(x => x.InternalName == context.Map);
                            if (map.MapFFAMaxTeams != null)
                            {
                                res = new Balancer().LegacyBalance(
                                    allyCount ?? map.MapFFAMaxTeams.Value,
                                    clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                    context);
                            }
                            else
                            {
                                res = new Balancer().LegacyBalance(
                                    allyCount ?? map.MapFFAMaxTeams ?? 8,
                                    clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                    context);
                            }
                            return res;
                        }
                    case AutohostMode.Planetwars:

                        return new Balancer().PlanetwarsBalance(context);
                }
                return res;
            }
        }
        static BalanceTeamsResult PerformBalance(BattleContext context,
                                                 bool isGameStart,
                                                 int? allyCount,
                                                 bool? clanWise,
                                                 AutohostConfig config,
                                                 int playerCount) {
            var res = new BalanceTeamsResult();
            var mode = context.GetMode();

            using (var db = new ZkDataContext()) {
                if (!CheckPlayersMinimumConditions(context, db, config, ref res.Message)) {
                    res.CanStart = false;
                    return res;
                }

                switch (mode) {
                    case AutohostMode.None:
                    {
                        if (!isGameStart) res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == true ? BalanceMode.ClanWise : BalanceMode.Normal, context);
                    }
                        break;
                    case AutohostMode.Generic:
                    {
                        if (allyCount == null && res.Bots != null && res.Bots.Any())
                        {
                            res.Players = context.Players.ToList();
                            res.Bots = context.Bots.Where(x => x.Owner != context.AutohostName).ToList();
                            foreach (var p in res.Players) p.AllyID = 0;
                            foreach (var b in res.Bots) b.AllyID = 1;
                        }
                        else
                        {
                            var map = db.Resources.Single(x => x.InternalName == context.Map);
                            res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 2,
                                clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                context);
                            res.DeleteBots = mode == AutohostMode.Teams;
                        }
                        return res;
                    }
                    
                    case AutohostMode.Teams:
                    {
                        var map = db.Resources.Single(x => x.InternalName == context.Map);
                        res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                        res.DeleteBots = mode == AutohostMode.Teams;
                        return res;
                    }
                    case AutohostMode.Game1v1:
                    {
                        res = new Balancer().LegacyBalance(allyCount ?? 2, clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise, context);
                        res.DeleteBots = true;
                    }
                        break;

                    case AutohostMode.GameChickens:
                    {
                        res.Players = context.Players.ToList();
                        res.Bots = context.Bots.Where(x => x.Owner != context.AutohostName).ToList();
                        foreach (var p in res.Players) p.AllyID = 0;
                        foreach (var b in res.Bots) b.AllyID = 1;

                        if (!res.Bots.Any() && res.Players.Count > 0) {
                            res.Message = "Add some bot (computer player) as your enemy. Use button on bottom left. Chicken or CAI is recommended.";
                            res.CanStart = false;
                            /*else
                                    {
                                        res.Bots.Add(new BotTeam() { AllyID = 1, TeamID = 16, BotName = "default_Chicken", BotAI = "Chicken: Normal", });
                                        res.Message = "Adding a normal chickens bot for you";
                                    }*/
                        }
                    }
                        break;
                    case AutohostMode.GameFFA:
                    {
                        var map = db.Resources.Single(x => x.InternalName == context.Map);
                        if (map.MapFFAMaxTeams != null)
                            res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams.Value,
                                                               clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                                               context);
                        else
                            res = new Balancer().LegacyBalance(allyCount ?? map.MapFFAMaxTeams ?? 8,
                                                               clanWise == false ? BalanceMode.Normal : BalanceMode.ClanWise,
                                                               context);
                        return res;
                    }
                    case AutohostMode.Planetwars:

                        return new Balancer().PlanetwarsBalance(context);
                }
                return res;
            }
        }
        static bool listOnlyThatLevelsModules = false;  // may cause bugs

        /// <summary>
        /// Sets up all the things that Springie needs to know for the battle: how to balance, who to get extra commanders, what PlanetWars structures to create, etc.
        /// </summary>
        public static SpringBattleStartSetup GetSpringBattleStartSetup(BattleContext context)
        {
            try {
                AutohostMode mode = context.GetMode();
                var          ret  = new SpringBattleStartSetup();

                if (mode == AutohostMode.Planetwars)
                {
                    ret.BalanceTeamsResult = Balancer.BalanceTeams(context, true, null, null);
                    context.Players        = ret.BalanceTeamsResult.Players;
                }

                var commanderTypes = new LuaTable();
                var db             = new ZkDataContext();

                // calculate to whom to send extra comms
                var accountIDsWithExtraComms = new List <int>();
                if (mode == AutohostMode.Planetwars || mode == AutohostMode.Generic || mode == AutohostMode.GameFFA ||
                    mode == AutohostMode.Teams)
                {
                    IOrderedEnumerable <IGrouping <int, PlayerTeam> > groupedByTeam =
                        context.Players.Where(x => !x.IsSpectator).GroupBy(x => x.AllyID).OrderByDescending(x => x.Count());
                    IGrouping <int, PlayerTeam> biggest = groupedByTeam.FirstOrDefault();
                    if (biggest != null)
                    {
                        foreach (var other in groupedByTeam.Skip(1))
                        {
                            int cnt = biggest.Count() - other.Count();
                            if (cnt > 0)
                            {
                                foreach (Account a in
                                         other.Select(x => db.Accounts.First(y => y.AccountID == x.LobbyID)).OrderByDescending(x => x.Elo * x.EloWeight).Take(
                                             cnt))
                                {
                                    accountIDsWithExtraComms.Add(a.AccountID);
                                }
                            }
                        }
                    }
                }

                bool is1v1 = context.Players.Where(x => !x.IsSpectator).ToList().Count == 2 && context.Bots.Count == 0;

                // write Planetwars details to modoptions (for widget)
                Faction attacker = null;
                Faction defender = null;
                Planet  planet   = null;
                if (mode == AutohostMode.Planetwars)
                {
                    planet   = db.Galaxies.First(x => x.IsDefault).Planets.First(x => x.Resource.InternalName == context.Map);
                    attacker =
                        context.Players.Where(x => x.AllyID == 0 && !x.IsSpectator)
                        .Select(x => db.Accounts.First(y => y.AccountID == x.LobbyID))
                        .Where(x => x.Faction != null)
                        .Select(x => x.Faction)
                        .First();

                    defender = planet.Faction;

                    if (attacker == defender)
                    {
                        defender = null;
                    }

                    ret.ModOptions.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                        Key = "attackingFaction", Value = attacker.Shortcut
                    });
                    if (defender != null)
                    {
                        ret.ModOptions.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                            Key = "defendingFaction", Value = defender.Shortcut
                        });
                    }
                    ret.ModOptions.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                        Key = "planet", Value = planet.Name
                    });
                }

                // write player custom keys (level, elo, is muted, etc.)
                foreach (PlayerTeam p in context.Players)
                {
                    Account user = db.Accounts.Find(p.LobbyID);
                    if (user != null)
                    {
                        var userParams = new List <SpringBattleStartSetup.ScriptKeyValuePair>();
                        ret.UserParameters.Add(new SpringBattleStartSetup.UserCustomParameters {
                            LobbyID = p.LobbyID, Parameters = userParams
                        });

                        bool userBanMuted = user.PunishmentsByAccountID.Any(x => !x.IsExpired && x.BanMute);
                        if (userBanMuted)
                        {
                            userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                Key = "muted", Value = "1"
                            });
                        }
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair
                        {
                            Key = "faction", Value = user.Faction != null ? user.Faction.Shortcut : ""
                        });
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair
                        {
                            Key = "clan", Value = user.Clan != null ? user.Clan.Shortcut : ""
                        });
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                            Key = "level", Value = user.Level.ToString()
                        });
                        double elo = mode == AutohostMode.Planetwars ? user.EffectivePwElo : (is1v1 ? user.Effective1v1Elo : user.EffectiveElo);
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                            Key = "elo", Value = Math.Round(elo).ToString()
                        });                                                                                                                // elo for ingame is just ordering for auto /take
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                            Key = "avatar", Value = user.Avatar
                        });
                        userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                            Key = "admin", Value = (user.IsZeroKAdmin ? "1" : "0")
                        });

                        if (!p.IsSpectator)
                        {
                            // set valid PW structure attackers
                            if (mode == AutohostMode.Planetwars)
                            {
                                bool allied = user.Faction != null && defender != null && user.Faction != defender &&
                                              defender.HasTreatyRight(user.Faction, x => x.EffectPreventIngamePwStructureDestruction == true, planet);

                                if (!allied && user.Faction != null && (user.Faction == attacker || user.Faction == defender))
                                {
                                    userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                        Key = "canAttackPwStructures", Value = "1"
                                    });
                                }
                            }

                            var  pu = new LuaTable();
                            bool userUnlocksBanned    = user.PunishmentsByAccountID.Any(x => !x.IsExpired && x.BanUnlocks);
                            bool userCommandersBanned = user.PunishmentsByAccountID.Any(x => !x.IsExpired && x.BanCommanders);

                            if (!userUnlocksBanned)
                            {
                                if (mode != AutohostMode.Planetwars || user.Faction == null)
                                {
                                    foreach (Unlock unlock in user.AccountUnlocks.Select(x => x.Unlock))
                                    {
                                        pu.Add(unlock.Code);
                                    }
                                }
                                else
                                {
                                    foreach (Unlock unlock in
                                             user.AccountUnlocks.Select(x => x.Unlock).Union(user.Faction.GetFactionUnlocks().Select(x => x.Unlock)).Where(x => x.UnlockType == UnlockTypes.Unit))
                                    {
                                        pu.Add(unlock.Code);
                                    }
                                }
                            }

                            userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                Key = "unlocks", Value = pu.ToBase64String()
                            });

                            if (accountIDsWithExtraComms.Contains(user.AccountID))
                            {
                                userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                    Key = "extracomm", Value = "1"
                                });
                            }

                            var pc = new LuaTable();

                            if (!userCommandersBanned)
                            {
                                // set up commander data
                                foreach (Commander c in user.Commanders.Where(x => x.Unlock != null && x.ProfileNumber <= GlobalConst.CommanderProfileCount))
                                {
                                    try {
                                        if (string.IsNullOrEmpty(c.Name) || c.Name.Any(x => x == '"'))
                                        {
                                            c.Name = c.CommanderID.ToString();
                                        }
                                        LuaTable morphTable = new LuaTable();
                                        pc["[\"" + c.Name + "\"]"] = morphTable;

                                        // process decoration icons
                                        LuaTable decorations = new LuaTable();
                                        foreach (Unlock d in
                                                 c.CommanderDecorations.Where(x => x.Unlock != null).OrderBy(
                                                     x => x.SlotID).Select(x => x.Unlock))
                                        {
                                            CommanderDecorationIcon iconData = db.CommanderDecorationIcons.FirstOrDefault(x => x.DecorationUnlockID == d.UnlockID);
                                            if (iconData != null)
                                            {
                                                string iconName = null, iconPosition = null;
                                                // FIXME: handle avatars and preset/custom icons
                                                if (iconData.IconType == (int)DecorationIconTypes.Faction)
                                                {
                                                    iconName = user.Faction != null ? user.Faction.Shortcut : null;
                                                }
                                                else if (iconData.IconType == (int)DecorationIconTypes.Clan)
                                                {
                                                    iconName = user.Clan != null ? user.Clan.Shortcut : null;
                                                }

                                                if (iconName != null)
                                                {
                                                    iconPosition = CommanderDecoration.GetIconPosition(d);
                                                    LuaTable entry = new LuaTable();
                                                    entry.Add("image", iconName);
                                                    decorations.Add("icon_" + iconPosition.ToLower(), entry);
                                                }
                                            }
                                            else
                                            {
                                                decorations.Add(d.Code);
                                            }
                                        }


                                        string prevKey = null;
                                        for (int i = 0; i <= GlobalConst.NumCommanderLevels; i++)
                                        {
                                            string key = string.Format("c{0}_{1}_{2}", user.AccountID, c.ProfileNumber, i);
                                            morphTable.Add(key);    // TODO: maybe don't specify morph series in player data, only starting unit

                                            var comdef = new LuaTable();
                                            commanderTypes[key] = comdef;

                                            comdef["chassis"] = c.Unlock.Code + i;

                                            var modules = new LuaTable();
                                            comdef["modules"] = modules;

                                            comdef["decorations"] = decorations;

                                            comdef["name"] = c.Name.Substring(0, Math.Min(25, c.Name.Length)) + " level " + i;

                                            //if (i < GlobalConst.NumCommanderLevels)
                                            //{
                                            //    comdef["next"] = string.Format("c{0}_{1}_{2}", user.AccountID, c.ProfileNumber, i+1);
                                            //}
                                            //comdef["owner"] = user.Name;

                                            if (i > 0)
                                            {
                                                comdef["cost"] = c.GetTotalMorphLevelCost(i);

                                                if (listOnlyThatLevelsModules)
                                                {
                                                    if (prevKey != null)
                                                    {
                                                        comdef["prev"] = prevKey;
                                                    }
                                                    prevKey = key;
                                                    foreach (Unlock m in
                                                             c.CommanderModules.Where(x => x.CommanderSlot.MorphLevel == i && x.Unlock != null).OrderBy(
                                                                 x => x.Unlock.UnlockType).ThenBy(x => x.SlotID).Select(x => x.Unlock))
                                                    {
                                                        modules.Add(m.Code);
                                                    }
                                                }
                                                else
                                                {
                                                    foreach (Unlock m in
                                                             c.CommanderModules.Where(x => x.CommanderSlot.MorphLevel <= i && x.Unlock != null).OrderBy(
                                                                 x => x.Unlock.UnlockType).ThenBy(x => x.SlotID).Select(x => x.Unlock))
                                                    {
                                                        modules.Add(m.Code);
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Exception ex) {
                                        Trace.TraceError(ex.ToString());
                                        throw new ApplicationException(
                                                  string.Format("Error processing commander: {0} - {1} of player {2} - {3}",
                                                                c.CommanderID,
                                                                c.Name,
                                                                user.AccountID,
                                                                user.Name),
                                                  ex);
                                    }
                                }
                            }
                            else
                            {
                                userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                    Key = "jokecomm", Value = "1"
                                });
                            }

                            userParams.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                                Key = "commanders", Value = pc.ToBase64String()
                            });
                        }
                    }
                }

                ret.ModOptions.Add(new SpringBattleStartSetup.ScriptKeyValuePair {
                    Key = "commanderTypes", Value = commanderTypes.ToBase64String()
                });

                // set PW structures
                if (mode == AutohostMode.Planetwars)
                {
                    string owner = planet.Faction != null ? planet.Faction.Shortcut : "";

                    var pwStructures = new LuaTable();
                    foreach (PlanetStructure s in planet.PlanetStructures.Where(x => x.StructureType != null && !string.IsNullOrEmpty(x.StructureType.IngameUnitName)))
                    {
                        pwStructures.Add("s" + s.StructureTypeID,
                                         new LuaTable
                        {
                            { "unitname", s.StructureType.IngameUnitName },
                            //{ "isDestroyed", s.IsDestroyed ? true : false },
                            { "name", string.Format("{0} {1} ({2})", owner, s.StructureType.Name, s.Account != null ? s.Account.Name:"unowned") },
                            { "description", s.StructureType.Description }
                        });
                    }
                    ret.ModOptions.Add(new SpringBattleStartSetup.ScriptKeyValuePair
                    {
                        Key = "planetwarsStructures", Value = pwStructures.ToBase64String()
                    });
                }

                return(ret);
            } catch (Exception ex) {
                Trace.TraceError(ex.ToString());
                throw;
            }
        }