private static void FillApplicableRatings(SpringBattle battle, SpringBattleContext result) { battle.ApplicableRatings = 0; if (battle.HasBots) { return; } if (battle.IsMission) { return; } if (battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).Select(x => x.AllyNumber).Distinct().Count() < 2) { return; } if (battle.ResourceByMapResourceID?.MapIsSpecial == true) { return; } //only count balanced custom matches for elo if (battle.Mode == AutohostMode.None && battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).GroupBy(x => x.AllyNumber).Select(x => x.Count()).Distinct().Count() > 1) { return; } if (battle.Duration < GlobalConst.MinDurationForElo) { return; } battle.ApplicableRatings |= (RatingCategoryFlags)result.LobbyStartContext.ApplicableRating; //battle.ApplicableRatings |= RatingCategoryFlags.Casual; }
private static void ProcessElos(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, ZkDataContext db, SpringBattle sb) { bool noElo = result.OutputExtras.Any(x => x?.StartsWith("noElo", true, System.Globalization.CultureInfo.CurrentCulture) == true); sb.CalculateAllElo(noElo); if (!noElo) { RatingSystems.ProcessResult(sb); } foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) { u.Account.CheckLevelUp(); } db.SaveChanges(); try { foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account)) { server.PublishAccountUpdate(a); server.PublishUserProfileUpdate(a); } } catch (Exception ex) { Trace.TraceError("error updating extension data: {0}", ex); } }
protected virtual async Task OnDedicatedExited(SpringBattleContext springBattleContext) { StopVote(); IsInGame = false; RunningSince = null; var debriefingMessage = BattleResultHandler.SubmitSpringBattleResult(springBattleContext, server); await server.Broadcast(Users.Keys, debriefingMessage); await server.Broadcast(server.ConnectedUsers.Keys, new BattleUpdate() { Header = GetHeader() }); foreach (var s in toNotify) { await server.GhostSay(new Say() { User = GlobalConst.NightwatchName, Text = $"** {FounderName} 's {Title} just ended, join me! **", Target = s, IsEmote = true, Place = SayPlace.User, Ring = true, AllowRelay = false }); } toNotify.Clear(); }
void spring_SpringExited(object sender, SpringBattleContext springBattleContext) { if (speechSynthesizer != null) { speechSynthesizer.SpeakAsyncCancelAll(); } }
protected override async Task OnDedicatedExited(SpringBattleContext springBattleContext) { try { StopVote(); RunningSince = null; IsInGame = false; isZombie = true; var playerNames = spring.Context.ActualPlayers.Where(x => x.Name != null).Select(x => x.Name).ToList(); bool result = BattleResultHandler.SubmitSpringBattleResult(springBattleContext, server, debriefingMessage => { debriefingMessage.ChatChannel = "debriefing_" + debriefingMessage.ServerBattleID; // join people to channel Task.WhenAll( playerNames.Select(x => server.ConnectedUsers.Get(x)) .Where(x => x != null) .Select(x => x.Process(new JoinChannel() { ChannelName = debriefingMessage.ChatChannel }))); server.Broadcast(playerNames, debriefingMessage); }); await server.RemoveBattle(this); } catch (Exception ex) { Trace.TraceError("Error processing battle exited: {0}", ex); } }
private static void FillApplicableRatings(SpringBattle battle, SpringBattleContext result) { battle.ApplicableRatings = 0; if (battle.HasBots) { return; } if (battle.IsMission) { return; } if (battle.SpringBattlePlayers?.Select(x => x.AllyNumber).Distinct().Count() < 2) { return; } if (battle.ResourceByMapResourceID?.MapIsSpecial == true) { return; } if (battle.Duration < GlobalConst.MinDurationForElo) { return; } battle.ApplicableRatings |= (RatingCategoryFlags)result.LobbyStartContext.ApplicableRating; //battle.ApplicableRatings |= RatingCategoryFlags.Casual; }
private static void ProcessXP(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, ZkDataContext db, SpringBattle sb) { sb.DispenseXP(); foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) { u.Account.CheckLevelUp(); } db.SaveChanges(); if (sb.ApplicableRatings == 0) { try { foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account)) { server.PublishAccountUpdate(a); server.PublishUserProfileUpdate(a); } } catch (Exception ex) { Trace.TraceError("error updating extension data: {0}", ex); } } }
private static void ProcessPlanetWars(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, SpringBattle sb, ZkDataContext db, StringBuilder text) { if (result.LobbyStartContext.Mode != AutohostMode.Planetwars || sb.PlayerCount < 2 || sb.Duration < GlobalConst.MinDurationForPlanetwars) { return; } 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; } } PlanetWarsTurnHandler.EndTurn(result.LobbyStartContext.Map, result.OutputExtras, 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(), server.PlanetWarsEventCreator, server); server.PlanetWarsMatchMaker.RemoveFromRunningBattles(result.LobbyStartContext.BattleID); }
//Processes new SpringBattle and determines applicable ratings public static void ProcessResult(SpringBattle battle, SpringBattleContext result, PendingDebriefing partialDebriefing) { if (!Initialized) { return; } FillApplicableRatings(battle, result); ProcessBattle(battle, debriefing: partialDebriefing); }
//Processes new SpringBattle and determines applicable ratings public static void ProcessResult(SpringBattle battle, SpringBattleContext result) { if (!Initialized) { return; } FillApplicableRatings(battle, result); ProcessBattle(battle); }
protected virtual async Task OnDedicatedExited(SpringBattleContext springBattleContext) { StopVote(); IsInGame = false; RunningSince = null; EndedSince = DateTime.UtcNow; var debriefingMessage = BattleResultHandler.SubmitSpringBattleResult(springBattleContext, server); Debriefings.Add(debriefingMessage); await server.Broadcast(Users.Keys, debriefingMessage); await server.Broadcast(server.ConnectedUsers.Keys, new BattleUpdate() { Header = GetHeader() }); foreach (var s in toNotify) { await server.GhostSay(new Say() { User = GlobalConst.NightwatchName, Text = $"** {FounderName} 's {Title} just ended, join me! **", Target = s, IsEmote = true, Place = SayPlace.User, Ring = true, AllowRelay = false }); } toNotify.Clear(); var playingEligibleUsers = server.MatchMaker.GetEligibleQuickJoinPlayers(Users.Values.Where(x => !x.LobbyUser.IsAway && !x.IsSpectator).Select(x => server.ConnectedUsers[x.Name]).ToList()); if (playingEligibleUsers.Count() >= InviteMMPlayers) //Make sure there are enough eligible users for a battle to be likely to happen //put all users into MM queue to suggest battles { var teamsQueues = server.MatchMaker.PossibleQueues.Where(x => x.Mode == AutohostMode.Teams).ToList(); var availableUsers = Users.Values.Where(x => !x.LobbyUser.IsAway).Select(x => server.ConnectedUsers[x.Name]).ToList(); await server.MatchMaker.MassJoin(availableUsers, teamsQueues); } if (IsAutohost) { await RunCommandDirectly <CmdMap>(null); discussionTimer.Interval = (DiscussionTime + 1) * 1000; discussionTimer.Start(); } await CheckCloseBattle(); }
private async void DedicatedServerExited(object sender, SpringBattleContext springBattleContext) { try { await OnDedicatedExited(springBattleContext); } catch (Exception ex) { Trace.TraceError("Error processing dedi server exited: {0}", ex); } }
private static void FillApplicableRatings(SpringBattle battle, SpringBattleContext result) { battle.ApplicableRatings = 0; if (battle.HasBots) { return; } if (battle.IsMission) { return; } if (battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).Select(x => x.AllyNumber).Distinct().Count() < 2) { return; } if (battle.ResourceByMapResourceID?.MapIsSpecial == true) { return; } //only count balanced custom matches for elo if (battle.Mode == AutohostMode.None && battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).GroupBy(x => x.AllyNumber).Select(x => x.Count()).Distinct().Count() > 1) { return; } if (battle.Duration < GlobalConst.MinDurationForElo) { return; } //don't mark battles for ratings if they can't be rated ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList(); ICollection <int> losers = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList(); if (winners.Count == 0 || losers.Count == 0 || winners.Intersect(losers).Count() != 0) { return; } battle.ApplicableRatings |= (RatingCategoryFlags)result.LobbyStartContext.ApplicableRating; //Optionally add other flags here, like a casual or overall rating }
private void spring_BattleStarted(object sender, SpringBattleContext e) { try { StopVote(); if (IsMatchMakerBattle && e.PlayersUnreadyOnStart.Count > 0 && e.IsTimeoutForceStarted) { string message = string.Format("Players {0} did not choose a start position. Game will be aborted.", e.PlayersUnreadyOnStart.StringJoin()); spring.SayGame(message); Trace.TraceInformation(string.Format("Matchmaker Game {0} aborted because {1}", BattleID, message)); RunCommandDirectly <CmdExit>(null); server.UserLogSay($"Battle aborted because {e.PlayersUnreadyOnStart.Count} players didn't join their MM game: {e.PlayersUnreadyOnStart.StringJoin()}."); e.PlayersUnreadyOnStart.ForEach(x => server.MatchMaker.BanPlayer(x)); } } catch (Exception ex) { Trace.TraceError("Error processing spring_BattleStarted started: {0}", ex); } }
private static SpringBattle SaveSpringBattle(SpringBattleContext result, ZkDataContext db) { var sb = new SpringBattle { HostAccountID = Account.AccountByName(db, result.LobbyStartContext.FounderName)?.AccountID, Mode = result.LobbyStartContext.Mode, Duration = result.Duration, EngineGameID = result.EngineBattleID, MapResourceID = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Map).ResourceID, ModResourceID = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Mod).ResourceID, HasBots = result.LobbyStartContext.Bots.Any(), IsMission = result.LobbyStartContext.IsMission, PlayerCount = result.ActualPlayers.Count(x => !x.IsSpectator), StartTime = result.StartTime, Title = result.LobbyStartContext.Title, ReplayFileName = Path.GetFileName(result.ReplayName), EngineVersion = result.LobbyStartContext.EngineVersion, IsMatchMaker = result.LobbyStartContext.IsMatchMakerGame, ApplicableRatings = 0, }; db.SpringBattles.InsertOnSubmit(sb); // store players foreach (BattlePlayerResult p in result.ActualPlayers) { var account = Account.AccountByName(db, p.Name); if (account != null) { sb.SpringBattlePlayers.Add(new SpringBattlePlayer { Account = account, AccountID = account.AccountID, AllyNumber = p.AllyNumber, IsInVictoryTeam = p.IsVictoryTeam, IsSpectator = p.IsSpectator, LoseTime = p.LoseTime }); } } // store bots var victoryAllyID = result.ActualPlayers.Where(x => x.IsVictoryTeam).Select(x => (int?)x.AllyNumber).FirstOrDefault() ?? -1; if (victoryAllyID == -1) { victoryAllyID = (result.ActualPlayers.Min(x => (int?)x.AllyNumber) ?? -1) + 1; // no player won, its likely to be next lowes team (stupid hack needed) } foreach (var bot in result.LobbyStartContext.Bots) { sb.SpringBattleBots.Add(new SpringBattleBot() { AllyNumber = bot.AllyID, BotAI = bot.BotAI, BotName = bot.BotName, IsInVictoryTeam = bot.AllyID == victoryAllyID }); } db.SaveChanges(); return(db.SpringBattles.FirstOrDefault(x => x.SpringBattleID == sb.SpringBattleID)); // reselect from db to get proper lazy proxies }
//stores replay, springbattle, processes rating and xp, returns whether battle ended normally public static bool SubmitSpringBattleResult(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, Action <BattleDebriefing> consumer) { var ret = new BattleDebriefing(); try { bool isValidGame = true; if (!result.GameEndedOk) { ret.Message = "Game didn't end properly"; isValidGame = false; } if (result.IsCheating) { ret.Message = "Cheats were enabled during this game"; isValidGame = false; } var db = new ZkDataContext(); var text = new StringBuilder(); var sb = SaveSpringBattle(result, db); if (isValidGame) { StoreAwards(result.OutputExtras, sb, db); } StoreLogs(result.OutputExtras, sb, db); if (result.LobbyStartContext.Mode == AutohostMode.Planetwars) { ProcessPlanetWars(result, server, sb, db, text); } Dictionary <int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level); //fill in applicable ratings bool noElo = !isValidGame || result.LobbyStartContext.ModOptions.Any(x => x.Key.ToLower() == "noelo" && x.Value != "0" && x.Value != "false"); if (!noElo) { RatingSystems.FillApplicableRatings(sb, result); } if (isValidGame) { ProcessXP(result, server, db, sb); } ret.Url = string.Format("{1}/Battles/Detail/{0}", sb.SpringBattleID, GlobalConst.BaseSiteUrl); ret.ServerBattleID = sb.SpringBattleID; server.GhostSay( new Say() { Text = string.Format("BATTLE DETAILS AND REPLAY ----> {0} <-----", ret.Url), IsEmote = true, Place = SayPlace.Battle, User = GlobalConst.NightwatchName }, result.LobbyStartContext.BattleID); foreach (var p in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) { ret.DebriefingUsers[p.Account.Name] = new BattleDebriefing.DebriefingUser() { AccountID = p.AccountID, LoseTime = p.LoseTime ?? -1, AllyNumber = p.AllyNumber, IsInVictoryTeam = p.IsInVictoryTeam, EloChange = 0, IsRankdown = false, IsRankup = false, NewElo = -1, NextRankElo = -1, PrevRankElo = -1, NewRank = p.Account.Rank, XpChange = p.XpChange ?? 0, NewXp = p.Account.Xp, NextLevelXp = Account.GetXpForLevel(p.Account.Level + 1), PrevLevelXp = Account.GetXpForLevel(p.Account.Level), IsLevelUp = orgLevels[p.AccountID] < p.Account.Level, Awards = sb.AccountBattleAwards.Where(x => x.AccountID == p.AccountID).Select(x => new BattleDebriefing.DebriefingAward() { Value = x.Value, Key = x.AwardKey, Description = x.AwardDescription }).ToList() }; } //send to rating if (!noElo) { RatingSystems.ProcessResult(sb, result, new PendingDebriefing() { debriefingConsumer = consumer, partialDebriefing = ret, battle = sb, }); } Trace.TraceInformation("Battle ended: Server exited for B" + sb.SpringBattleID); db.SaveChanges(); return(true); } catch (Exception ex) { var data = JsonConvert.SerializeObject(result); Trace.TraceError($"{ex}\nData:\n{data}"); ret.Message = "Error processing game result: " + ex.Message; return(false); } }
protected virtual async Task OnDedicatedExited(SpringBattleContext springBattleContext) { StopVote(); IsInGame = false; RunningSince = null; BlockPollsUntil = DateTime.UtcNow.AddSeconds(DiscussionSeconds); bool result = BattleResultHandler.SubmitSpringBattleResult(springBattleContext, server, (debriefing) => { Debriefings.Add(debriefing); server.Broadcast(springBattleContext.ActualPlayers.Select(x => x.Name), debriefing); Trace.TraceInformation("Battle ended: Sent out debriefings for B" + debriefing.ServerBattleID); }); await server.Broadcast(server.ConnectedUsers.Keys, new BattleUpdate() { Header = GetHeader() }); foreach (var s in toNotify) { await server.GhostSay(new Say() { User = GlobalConst.NightwatchName, Text = $"** {FounderName} 's {Title} just ended, join me! **", Target = s, IsEmote = true, Place = SayPlace.User, Ring = true, AllowRelay = false }); } toNotify.Clear(); var playingEligibleUsers = server.MatchMaker.GetEligibleQuickJoinPlayers(Users.Values.Where(x => !x.LobbyUser.IsAway && !x.IsSpectator && x.Name != null).Select(x => server.ConnectedUsers[x.Name]).ToList()); if (playingEligibleUsers.Count() >= InviteMMPlayers) { //Make sure there are enough eligible users for a battle to be likely to happen //put all users into MM queue to suggest battles var teamsQueues = server.MatchMaker.PossibleQueues.Where(x => x.Mode == AutohostMode.Teams).ToList(); var availableUsers = Users.Values.Where(x => !x.LobbyUser.IsAway && x.Name != null).Select(x => server.ConnectedUsers[x.Name]).ToList(); await server.MatchMaker.MassJoin(availableUsers, teamsQueues); DiscussionSeconds = MatchMaker.TimerSeconds + 2; } else { DiscussionSeconds = 5; } BlockPollsUntil = DateTime.UtcNow.AddSeconds(DiscussionSeconds); if (IsAutohost || (!Users.ContainsKey(FounderName) || Users[FounderName].LobbyUser?.IsAway == true) && Mode != AutohostMode.None && Mode != AutohostMode.Planetwars && !IsPassworded) { if (!result) { //Game was aborted/exited/invalid, allow manual commands BlockPollsUntil = DateTime.UtcNow; } else { //Initiate discussion time, then map vote, then start vote discussionTimer.Interval = (DiscussionSeconds - 1) * 1000; discussionTimer.Start(); } } await CheckCloseBattle(); }
public static BattleDebriefing SubmitSpringBattleResult(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server) { var ret = new BattleDebriefing(); try { if (!result.GameEndedOk) { ret.Message = "Game didn't end properly"; return(ret); } if (result.IsCheating) { ret.Message = "Cheats were enabled during this game"; return(ret); } var db = new ZkDataContext(); var text = new StringBuilder(); var sb = SaveSpringBattle(result, db); ProcessExtras(result.OutputExtras, sb, db); if (result.LobbyStartContext.Mode == AutohostMode.Planetwars) { ProcessPlanetWars(result, server, sb, db, text); } Dictionary <int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level); ProcessElos(result, server, db, sb); ret.Url = string.Format("{1}/Battles/Detail/{0}", sb.SpringBattleID, GlobalConst.BaseSiteUrl); ret.ServerBattleID = sb.SpringBattleID; server.GhostSay( new Say() { Text = string.Format("BATTLE DETAILS AND REPLAY ----> {0} <-----", ret.Url), IsEmote = true, Place = SayPlace.Battle, User = GlobalConst.NightwatchName }, result.LobbyStartContext.BattleID); foreach (var p in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) { ret.DebriefingUsers[p.Account.Name] = new BattleDebriefing.DebriefingUser() { LoseTime = p.LoseTime, AllyNumber = p.AllyNumber, IsInVictoryTeam = p.IsInVictoryTeam, EloChange = p.EloChange?.ToString("F2"), XpChange = p.XpChange, IsLevelUp = orgLevels[p.AccountID] < p.Account.Level, Awards = sb.AccountBattleAwards.Where(x => x.AccountID == p.AccountID).Select(x => new BattleDebriefing.DebriefingAward() { Value = x.Value, Key = x.AwardKey, Description = x.AwardDescription }).ToList() }; } return(ret); } catch (Exception ex) { var data = JsonConvert.SerializeObject(result); Trace.TraceError($"{ex}\nData:\n{data}"); ret.Message = "Error processing game result: " + ex.Message; return(ret); } }