static void SpecPlayerOnCondition(PlayerTeam player, Account account, string userMessage) { player.IsSpectator = true; AuthServiceClient.SendLobbyMessage(account, userMessage); }
public static void AutoClosePolls() { var db = new ZkDataContext(); foreach (var p in db.Polls.Where(x => x.IsHeadline && x.ExpireBy != null && x.ExpireBy < DateTime.UtcNow && x.RoleTypeID != null).ToList()) { var yes = p.PollVotes.Count(x => x.PollOption.OptionText == "Yes"); var no = p.PollVotes.Count(x => x.PollOption.OptionText == "No"); var acc = p.AccountByRoleTargetAccountID; if (yes > no) { if (p.RoleIsRemoval) { var toDelete = db.AccountRoles.Where(x => x.AccountID == acc.AccountID && x.RoleTypeID == p.RoleTypeID); db.AccountRoles.DeleteAllOnSubmit(toDelete); db.Events.InsertOnSubmit(Global.CreateEvent("{0} was removed from the {1} role of {2} by a vote - {3} for, {4} against", acc, (object)p.Clan ?? p.Faction, p.RoleType, yes, no)); db.SubmitAndMergeChanges(); AuthServiceClient.SendLobbyMessage(acc, string.Format("You were recalled from the function of {0} by a vote", p.RoleType.Name)); } else { if (!acc.AccountRolesByAccountID.Any(x => x.RoleTypeID == p.RoleTypeID)) { Account previous = null; if (p.RoleType.IsOnePersonOnly) { var entries = db.AccountRoles.Where(x => x.RoleTypeID == p.RoleTypeID && (p.RoleType.IsClanOnly ? x.ClanID == p.RestrictClanID : x.FactionID == p.RestrictFactionID)).ToList(); if (entries.Any()) { previous = entries.First().AccountByAccountID; db.AccountRoles.DeleteAllOnSubmit(entries); db.SubmitAndMergeChanges(); } } var entry = new AccountRole() { AccountByAccountID = acc, Inauguration = DateTime.UtcNow, Clan = p.Clan, Faction = p.Faction, RoleType = p.RoleType }; acc.AccountRolesByAccountID.Add(entry); if (previous == null) { db.Events.InsertOnSubmit(Global.CreateEvent("{0} was elected for the {1} role of {2} by a vote - {3} for, {4} against", acc, (object)p.Clan ?? p.Faction, p.RoleType, yes, no)); } else { db.Events.InsertOnSubmit(Global.CreateEvent("{0} was elected for the {1} role of {2} by a vote, replacing {3} - {4} for, {5} against", acc, (object)p.Clan ?? p.Faction, p.RoleType, previous, yes, no)); } AuthServiceClient.SendLobbyMessage(acc, string.Format("Congratulations!! You were elected into a function of {0} by a vote", p.RoleType.Name)); } } } p.IsHeadline = false; db.Polls.DeleteOnSubmit(p); } db.SubmitAndMergeChanges(); }
public static BalanceTeamsResult BalanceTeams(string autoHost, string map, string mod, List <AccountTeam> currentTeams, List <BotTeam> currentBots) { var mode = ContentService.GetModeFromHost(autoHost); if (currentTeams.Count < 1) { return(new BalanceTeamsResult()); } using (var db = new ZkDataContext()) { var res = new BalanceTeamsResult(); res.Message = ""; var idList = currentTeams.Select(x => x.AccountID).ToList(); var players = new List <Account>(); foreach (var p in idList.Select(x => db.Accounts.First(y => y.LobbyID == x))) { /*if (p.ClanID == null) * { * //res.Message += string.Format("{0} cannot play, must join a clan first http://zero-k.info/Planetwars/ClanList\n", p.Name); * //AuthServiceClient.SendLobbyMessage(p, "To play here, join a clan first http://zero-k.info/Planetwars/ClanList"); * }*/ /*if (p.Clan != null && !p.Name.Contains(p.Clan.Shortcut)) * { * res.Message += string.Format("{0} cannot play, name must contain clan tag {1}\n", p.Name, p.Clan.Shortcut); * AuthServiceClient.SendLobbyMessage(p, * string.Format( * "Your name must contain clan tag {0}, rename for example by saying: /rename [{0}]{1}", * p.Clan.Shortcut, * p.Name)); * }*/ if (p.Level < GlobalConst.MinPlanetWarsLevel) { res.Message += string.Format("{0} cannot play, his level is {1}, minimum level is {2}\n", p.Name, p.Level, GlobalConst.MinPlanetWarsLevel); AuthServiceClient.SendLobbyMessage(p, string.Format( "Sorry, PlanetWars is competive online campaign for experienced players. You need to be at least level 5 to play here. To increase your level, play more games on other hosts or open multiplayer game and play against computer AI bots. You can observe this game however.")); } else { players.Add(p); } } var clans = players.Where(x => x.Clan != null).Select(x => x.Clan).ToList(); var treaties = new Dictionary <Tuple <Clan, Clan>, EffectiveTreaty>(); var planet = db.Galaxies.Single(x => x.IsDefault).Planets.Single(x => x.Resource.InternalName == map); // bots game if (planet.PlanetStructures.Any(x => !string.IsNullOrEmpty(x.StructureType.EffectBots))) { var teamID = 0; for (var i = 0; i < players.Count; i++) { res.BalancedTeams.Add(new AccountTeam() { AccountID = players[i].LobbyID ?? 0, Name = players[i].Name, AllyID = 0, TeamID = teamID++ }); } int cnt = 1; foreach (var b in planet.PlanetStructures.Select(x => x.StructureType).Where(x => !string.IsNullOrEmpty(x.EffectBots))) { res.Bots.Add(new BotTeam() { AllyID = 1, BotAI = b.EffectBots, TeamID = teamID++, BotName = "Aliens" + cnt++ }); } res.Message += string.Format("This planet is infested by aliens, fight for your survival"); return(res); } var planetFactionId = planet.Account != null ? planet.Account.FactionID ?? 0 : 0; var attackerFactions = planet.AccountPlanets.Where(x => x.DropshipCount > 0 && x.Account.FactionID != null).Select(x => (x.Account.FactionID ?? 0)). Distinct().ToList(); if (currentTeams.Count < 2) { return new BalanceTeamsResult() { Message = "Not enough players" } } ; for (var i = 1; i < clans.Count; i++) { for (var j = 0; j < i; j++) { var treaty = clans[i].GetEffectiveTreaty(clans[j]); treaties[Tuple.Create(clans[i], clans[j])] = treaty; treaties[Tuple.Create(clans[j], clans[i])] = treaty; // if treaty is neutral but they send ships - mark as "war" if (planet.OwnerAccountID != null && treaty.AllyStatus == AllyStatus.Neutral) { if (clans[i].ClanID == planet.Account.ClanID && planet.AccountPlanets.Any(x => x.Account.ClanID == clans[j].ClanID && x.DropshipCount > 0)) { treaty.AllyStatus = AllyStatus.War; } else if (clans[j].ClanID == planet.Account.ClanID && planet.AccountPlanets.Any(x => x.Account.ClanID == clans[i].ClanID && x.DropshipCount > 0)) { treaty.AllyStatus = AllyStatus.War; } } } } var sameTeamScore = new double[players.Count, players.Count]; for (var i = 1; i < players.Count; i++) { for (var j = 0; j < i; j++) { var c1 = players[i].Clan; var c2 = players[j].Clan; var f1 = players[i].FactionID ?? -1; var f2 = players[i].FactionID ?? -1; var points = 0.0; if (players[i].FactionID != null && players[i].FactionID == players[j].FactionID) { points = 3; // same faction weight 1 } if (c1 != null && c2 != null) { if (c1 == c2) { points = 4; } else { var treaty = treaties[Tuple.Create(players[i].Clan, players[j].Clan)]; if (treaty.AllyStatus == AllyStatus.Alliance) { points = 2; } else if (treaty.AllyStatus == AllyStatus.Ceasefire) { points = 1; } else if (treaty.AllyStatus == AllyStatus.War) { points = -3; } if (treaty.AllyStatus == AllyStatus.Neutral && f1 != f2) { if ((planetFactionId == f1 && attackerFactions.Contains(f2)) || (planetFactionId == f2 && attackerFactions.Contains(f1))) { points = -3; } } } } else if (f1 != f2) { if ((planetFactionId == f1 && attackerFactions.Contains(f2)) || (planetFactionId == f2 && attackerFactions.Contains(f1))) { points = -3; } } sameTeamScore[i, j] = points; sameTeamScore[j, i] = points; //res.Message += string.Format("{0} + {1} = {2} \n", players[i].Name, players[j].Name, points); } } var playerScoreMultiplier = new double[players.Count]; for (var i = 0; i < players.Count; i++) { var mult = 1.0; var player = players[i]; if (planet.OwnerAccountID == player.AccountID) { mult += 1; // owner } else if (planet.Account != null && planet.Account.ClanID == player.AccountID) { mult += 0.5; // owner's clan } if (planet.AccountPlanets.Any(x => x.AccountID == player.AccountID && x.DropshipCount > 0)) { mult += 1; // own dropship } else if (planet.AccountPlanets.Any(x => x.DropshipCount > 0 && x.Account.ClanID == player.ClanID)) { mult += 0.5; // clan's dropship } playerScoreMultiplier[i] = mult; //res.Message += string.Format("{0} mult = {1} \n", players[i].Name, mult); } var limit = 1 << (players.Count); var bestCombination = -1; var bestScore = double.MinValue; double bestCompo = 0; double absCompo = 0; double bestElo = 0; double bestTeamDiffs = 0; var playerAssignments = new int[players.Count]; for (var combinator = 0; combinator < limit; combinator++) { //double team0Weight = 0; double team0Elo = 0; //double team1Weight = 0; double team1Elo = 0; var team0count = 0; var team1count = 0; // determine where each player is amd dp some adding for (var i = 0; i < players.Count; i++) { var player = players[i]; var team = (combinator & (1 << i)) > 0 ? 1 : 0; playerAssignments[i] = team; if (team == 0) { team0Elo += player.EffectiveElo; //team0Weight += player.EloWeight; team0count++; } else { team1Elo += player.EffectiveElo; // *player.EloWeight; //team1Weight += player.EloWeight; team1count++; } } if (team0count == 0 || team1count == 0) { continue; // skip combination, empty team } // calculate score for team difference var teamDiffScore = -(20.0 * Math.Abs(team0count - team1count) / (double)(team0count + team1count)) - Math.Abs(team0count - team1count); if (teamDiffScore < -10) { continue; // max imabalance } double balanceModifier = 0; // count elo vs balance modifier /* * if (team0count < team1count) balanceModifier = -teamDiffScore; * else balanceModifier = teamDiffScore;*/ // calculate score for elo difference team0Elo = team0Elo / team0count; team1Elo = team1Elo / team1count; //team0Elo = team0Elo/team0Weight; //team1Elo = team1Elo/team1Weight; var eloScore = -Math.Abs(team0Elo - team1Elo) / 14; if (eloScore < -17) { continue; } if (team0Elo < team1Elo) { balanceModifier += -eloScore; } else { balanceModifier += eloScore; } // verify if ther eis sense in playing (no zero sum game ip abuse) var majorityFactions = (from teamData in Enumerable.Range(0, players.Count).GroupBy(x => playerAssignments[x]) let majorityCount = Math.Ceiling(teamData.Count() / 2.0) select teamData.GroupBy(x => players[x].FactionID).Where(x => x.Key != null && x.Count() >= majorityCount). Select(x => x.Key ?? 0)).ToList(); if (majorityFactions.Count == 2 && majorityFactions[0].Intersect(majorityFactions[1]).Any()) { continue; // winning either side would be benefitial for some majority faction } // calculate score for meaningfull teams var compoScore = 0.0; for (var i = 0; i < players.Count; i++) // for every player calculate his score as average of relations to other plaeyrs { double sum = 0; var cnt = 0; for (var j = 0; j < players.Count; j++) { if (i != j) { var sts = sameTeamScore[i, j]; if (sts != 0.0) // we only consider no-neutral people { if (playerAssignments[i] == playerAssignments[j]) { sum += sts; cnt++; } /*else sum -= sts; // different teams - score is equal to negation of same team score * cnt++;*/ } } } if (cnt > 0) // player can be meaningfully ranked, he had at least one non zero relation { compoScore += playerScoreMultiplier[i] * sum / cnt; } } if (compoScore < 0) { continue; // get meaningfull teams only || compoScore < 0.5*absCompo } if (compoScore > absCompo) { absCompo = compoScore; // todo lame - abs compo not known at this point,should be 2 pass } var score = -Math.Abs(balanceModifier) + teamDiffScore + compoScore; if (score > bestScore) { bestCombination = combinator; bestScore = score; bestElo = eloScore; bestCompo = compoScore; bestTeamDiffs = teamDiffScore; } } if (bestCombination == -1) { res.BalancedTeams = null; res.Message += "Cannot be balanced well at this point"; } /*else if (bestCompo < absCompo*0.5) * { * res.BalancedTeams = null; * res.Message += string.Format("Cannot be balanced well at this point - best composition: {0}, available: {1}", absCompo, bestCompo); * }*/ else { var differs = false; for (var i = 0; i < players.Count; i++) { var allyID = ((bestCombination & (1 << i)) > 0) ? 1 : 0; if (!differs && allyID != currentTeams.First(x => x.AccountID == players[i].LobbyID).AllyID) { differs = true; } res.BalancedTeams.Add(new AccountTeam() { AccountID = players[i].LobbyID.Value, Name = players[i].Name, AllyID = allyID, TeamID = i }); } if (differs) { res.Message += string.Format( "Winning combination score: {0:0.##} team difference, {1:0.##} elo, {2:0.##} composition. Win chance {3}%", bestTeamDiffs, bestElo, bestCompo, Utils.GetWinChancePercent(bestElo * 20)); } } res.DeleteBots = true; return(res); } } }
public static string SubmitSpringBattleResult(BattleContext context, string password, BattleResult result, List <BattlePlayerResult> players, List <string> extraData) { try { Account acc = AuthServiceClient.VerifyAccountPlain(context.AutohostName, password); if (acc == null) { throw new Exception("Account name or password not valid"); } AutohostMode mode = context.GetMode(); var db = new ZkDataContext(); if (extraData == null) { extraData = new List <string>(); } var sb = new SpringBattle { HostAccountID = acc.AccountID, Duration = result.Duration, EngineGameID = result.EngineBattleID, MapResourceID = db.Resources.Single(x => x.InternalName == result.Map).ResourceID, ModResourceID = db.Resources.Single(x => x.InternalName == result.Mod).ResourceID, HasBots = result.IsBots, IsMission = result.IsMission, PlayerCount = players.Count(x => !x.IsSpectator), StartTime = result.StartTime, Title = result.Title, ReplayFileName = result.ReplayName, EngineVersion = result.EngineVersion, }; db.SpringBattles.InsertOnSubmit(sb); foreach (BattlePlayerResult p in players) { sb.SpringBattlePlayers.Add(new SpringBattlePlayer { AccountID = db.Accounts.First(x => x.AccountID == p.LobbyID).AccountID, AllyNumber = p.AllyNumber, CommanderType = p.CommanderType, IsInVictoryTeam = p.IsVictoryTeam, IsSpectator = p.IsSpectator, LoseTime = p.LoseTime }); } db.SubmitChanges(); // awards foreach (string line in extraData.Where(x => x.StartsWith("award"))) { string[] partsSpace = line.Substring(6).Split(new[] { ' ' }, 3); string name = partsSpace[0]; string awardType = partsSpace[1]; string awardText = partsSpace[2]; SpringBattlePlayer player = sb.SpringBattlePlayers.FirstOrDefault(x => x.Account.Name == name); if (player != null) { db.AccountBattleAwards.InsertOnSubmit(new AccountBattleAward { AccountID = player.AccountID, SpringBattleID = sb.SpringBattleID, AwardKey = awardType, AwardDescription = awardText }); } } var text = new StringBuilder(); bool isPlanetwars = false; if (mode == AutohostMode.Planetwars && sb.SpringBattlePlayers.Count(x => !x.IsSpectator) >= 2 && sb.Duration >= GlobalConst.MinDurationForPlanetwars) { // test that factions are not intermingled (each faction only has one ally number) - if they are it wasnt actually PW balanced if ( sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.Account.Faction != null) .GroupBy(x => x.Account.Faction) .All(grp => grp.Select(x => x.AllyNumber).Distinct().Count() < 2)) { isPlanetwars = true; List <int> winnerTeams = sb.SpringBattlePlayers.Where(x => x.IsInVictoryTeam && !x.IsSpectator).Select(x => x.AllyNumber).Distinct().ToList(); int?winNum = null; if (winnerTeams.Count == 1) { winNum = winnerTeams[0]; if (winNum > 1) { winNum = null; text.AppendLine("ERROR: Invalid winner"); } } PlanetWarsTurnHandler.EndTurn(result.Map, extraData, db, winNum, sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account).ToList(), text, sb, sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.AllyNumber == 0).Select(x => x.Account).ToList()); Global.PlanetWarsMatchMaker.RemoveFromRunningBattles(context.AutohostName); } else { text.AppendLine("Battle wasn't PlanetWars balanced, it counts as a normal team game only"); } } bool noElo = (extraData.FirstOrDefault(x => x.StartsWith("noElo")) != null); try { db.SubmitChanges(); } catch (System.Data.Linq.DuplicateKeyException ex) { Trace.TraceError(ex.ToString()); } Dictionary <int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level); sb.CalculateAllElo(noElo, isPlanetwars); foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) { u.Account.CheckLevelUp(); } db.SubmitAndMergeChanges(); try { foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account)) { Global.Nightwatch.Tas.Extensions.PublishAccountData(a); } } catch (Exception ex) { Trace.TraceError("error updating extension data: {0}", ex); } foreach (Account account in sb.SpringBattlePlayers.Select(x => x.Account)) { if (account.Level > orgLevels[account.AccountID]) { try { string message = string.Format("Congratulations {0}! You just leveled up to level {1}. {3}/Users/Detail/{2}", account.Name, account.Level, account.AccountID, GlobalConst.BaseSiteUrl); //text.AppendLine(message); AuthServiceClient.SendLobbyMessage(account, message); } catch (Exception ex) { Trace.TraceError("Error sending level up lobby message: {0}", ex); } } } text.AppendLine(string.Format("BATTLE DETAILS AND REPLAY ----> {1}/Battles/Detail/{0} <-----", sb.SpringBattleID, GlobalConst.BaseSiteUrl)); // create debriefing room, join players there and output message string channelName = "B" + sb.SpringBattleID; var joinplayers = new List <string>(); joinplayers.AddRange(context.Players.Select(x => x.Name)); // add those who were there at start joinplayers.AddRange(sb.SpringBattlePlayers.Select(x => x.Account.Name)); // add those who played TasClient tas = Global.Nightwatch.Tas; Battle bat = tas.ExistingBattles.Values.FirstOrDefault(x => x.Founder.Name == context.AutohostName); // add those in lobby atm var conf = context.GetConfig(); if (bat != null && (conf == null || conf.MinToJuggle == null)) // if not qm room do not join those who are in battle { List <string> inbatPlayers = bat.Users.Keys.ToList(); joinplayers.RemoveAll(x => inbatPlayers.Contains(x)); } foreach (string jp in joinplayers.Distinct().Where(x => x != context.AutohostName)) { tas.ForceJoinChannel(jp, channelName); } tas.JoinChannel(channelName); // join nightwatch and say it tas.Say(SayPlace.Channel, channelName, text.ToString(), true); tas.LeaveChannel(channelName); //text.Append(string.Format("Debriefing in #{0} - zk://chat/channel/{0} ", channelName)); return(text.ToString()); } catch (Exception ex) { Trace.TraceError(ex.ToString()); return(ex.ToString()); } }