void tas_BattleUserJoined(object sender, BattleUserEventArgs e1) { if (e1.BattleID != tas.MyBattleID) { return; } string name = e1.UserName; string welc = config.Welcome; if (!string.IsNullOrEmpty(welc)) { welc = welc.Replace("%1", name); welc = welc.Replace("%2", GetUserLevel(name).ToString()); welc = welc.Replace("%3", MainConfig.SpringieVersion); SayBattlePrivate(name, welc); } if (spring.IsRunning) { spring.AddUser(e1.UserName, e1.ScriptPassword); TimeSpan started = DateTime.Now.Subtract(spring.GameStarted); started = new TimeSpan((int)started.TotalHours, started.Minutes, started.Seconds); SayBattlePrivate(name, String.Format("THIS GAME IS CURRENTLY IN PROGRESS, PLEASE WAIT UNTIL IT ENDS! Running for {0}", started)); SayBattlePrivate(name, "If you say !notify, I will message you when the current game ends."); } if (SpawnConfig == null) { try { var serv = GlobalConst.GetSpringieService(); PlayerJoinResult ret = serv.AutohostPlayerJoined(tas.MyBattle.GetContext(), tas.ExistingUsers[name].AccountID); if (ret != null) { if (!string.IsNullOrEmpty(ret.PrivateMessage)) { tas.Say(SayPlace.User, name, ret.PrivateMessage, false); } if (!string.IsNullOrEmpty(ret.PublicMessage)) { tas.Say(SayPlace.Battle, "", ret.PublicMessage, true); } if (ret.ForceSpec) { tas.ForceSpectator(name); } if (ret.Kick) { tas.Kick(name); } } } catch (Exception ex) { SayBattle("ServerManage error: " + ex, false); } } if (SpawnConfig != null && SpawnConfig.Owner == name) // owner joins, set him boss { ComBoss(TasSayEventArgs.Default, new[] { name }); } }
void tas_MyBattleMapChanged(object sender, OldNewPair <Battle> oldNewPair) { lastMapChange = DateTime.Now; Battle b = tas.MyBattle; if (b != null) { string mapName = b.MapName.ToLower(); if (SpawnConfig == null) { ComResetOptions(TasSayEventArgs.Default, new string[] { }); ComClearBox(TasSayEventArgs.Default, new string[] { }); } try { var serv = GlobalConst.GetSpringieService(); string commands = serv.GetMapCommands(mapName); if (!string.IsNullOrEmpty(commands)) { foreach (string c in commands.Split('\n').Where(x => !string.IsNullOrEmpty(x))) { RunCommand(c); } } } catch (Exception ex) { Trace.TraceError("Error procesing map commands: {0}", ex); } } }
protected override void SuccessAction() { try { bool val; var moves = tas.MyBattle.Users.Values.Where(x => x.Name != tas.MyBattle.Founder.Name) .Where(x => userVotes.TryGetValue(x.Name, out val) && val) .Select(x => new MovePlayerEntry() { BattleHost = host, PlayerName = x.Name }) .ToList(); // move those that voted yes var serv = GlobalConst.GetSpringieService(); serv.MovePlayers(tas.UserName, tas.UserPassword, moves); } catch (Exception ex) { ah.SayBattle(ex.ToString()); } }
static void SlaveStartSpring(AutoHost ah, List <UserBattleStatus> team) { var serv = GlobalConst.GetSpringieService(); var context = ah.tas.MyBattle.GetContext(); context.Players = team.Select(x => new PlayerTeam() { AllyID = x.AllyNumber, Name = x.Name, LobbyID = x.LobbyUser.AccountID, TeamID = x.TeamNumber, IsSpectator = false }).ToList(); var balance = serv.BalanceTeams(context, true, null, null); ah.ApplyBalanceResults(balance); context.Players = balance.Players; context.Bots = balance.Bots; ah.SayBattle("please wait, game is about to start"); ah.StopVote(); ah.slaveContextOverride = context; ah.tas.StartGame(); }
public void UpdateAll() { try { var serv = GlobalConst.GetSpringieService(); var configs = serv.GetClusterConfigs(Config.ClusterNode); var copy = new List <AutoHost>(); lock (autoHosts) { copy = autoHosts.ToList(); } foreach (var conf in configs) { if (!copy.Any(x => x.config.Login == conf.Login)) { SpawnAutoHost(conf, null).Start(); } else { foreach (var ah in copy.Where(x => x.config.Login == conf.Login && x.SpawnConfig == null)) { ah.config = conf; } } } var todel = copy.Where(x => !configs.Any(y => y.Login == x.config.Login)).ToList(); foreach (var ah in todel) { StopAutohost(ah); } } catch (Exception ex) { Trace.TraceError("Error in periodic updates: {0}", ex); } }
public override void End() { bool val; try { var moves = tas.MyBattle.Users.Values.Where(x => x.Name != tas.MyBattle.Founder.Name) .Where(x => userVotes.TryGetValue(x.Name, out val) && val) .Select(x => new MovePlayerEntry() { BattleHost = host, PlayerName = x.Name }) .ToList(); // move those that voted yes if there are at least 2 if (moves.Count > 1) { var serv = GlobalConst.GetSpringieService(); serv.MovePlayers(tas.UserName, tas.UserPassword, moves); } } catch (Exception ex) { ah.SayBattle(ex.ToString()); } base.End(); }
private void ParseInfolog(string text, bool isCrash) { if (string.IsNullOrEmpty(text)) { Trace.TraceWarning("Infolog is empty"); return; } try { var hasError = false; var modName = battleResult.Mod; var mapName = battleResult.Map; var isCheating = false; int? score = null; int scoreFrame = 0; string gameId = null; string demoFileName = null; string missionVars = ""; foreach (var cycleline in text.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)) { var line = cycleline; var gameframe = 0; if (line.StartsWith("[DedicatedServer]")) { line = line.Replace("[DedicatedServer] ", ""); } if (line.StartsWith("[")) { var idx = line.IndexOf("] "); if (idx > 0) { int.TryParse(line.Substring(1, idx - 1), out gameframe); if (idx >= 0) { line = line.Substring(idx + 2); } } } // FIXME: why are these even null in the first place? if (mapName == null && line.StartsWith("Using map")) { mapName = line.Substring(10).Trim(); } if (modName == null && line.StartsWith("Using game")) { modName = line.Substring(11).Trim(); } if (line.StartsWith("recording demo")) { demoFileName = Path.GetFileName(line.Substring(15).Trim()); // 91.0 } else if (line.StartsWith("[DedicatedServer] recording demo")) { demoFileName = Path.GetFileName(line.Substring(33).Trim()); // 95.0 and later } if (line.StartsWith("Using demofile")) { return; // do nothing if its demo } if (line.StartsWith("GameID: ") && gameId == null) { gameId = line.Substring(8).Trim(); } if (line.StartsWith("STATS:")) { statsData.Add(line.Substring(6)); } if (line.Contains("SCORE: ") && !isCheating && battleResult.IsMission) { var match = Regex.Match(line, "SCORE: ([^ ]+)"); if (match.Success) { // game score var data = match.Groups[1].Value; //Trace.TraceInformation("Score data (raw) : " + data); data = Encoding.ASCII.GetString(Convert.FromBase64String(match.Groups[1].Value)); //Trace.TraceInformation("Score data (decoded) : " + data); var parts = data.Split('/'); score = 0; if (parts.Length > 1) { score = Convert.ToInt32(parts[1]); gameframe = Convert.ToInt32(parts[0]); } else { score = Convert.ToInt32(data); } scoreFrame = gameframe; } } if (line.Contains("MISSIONVARS:") && battleResult.IsMission) { var match = Regex.Match(line, "MISSIONVARS: ([^ ]+)"); missionVars = match.Groups[1].Value.Trim(); Trace.TraceInformation(string.Format("Mission variables: {0} (original line: {1})", missionVars, line)); } // obsolete, hanlded by pm messages //if (line.StartsWith("STATS:")) statsData.Add(line.Substring(6)); if (line.StartsWith("Cheating!") || line.StartsWith("Cheating is enabled!")) { isCheating = true; } if (line.StartsWith("Error") || line.StartsWith("LuaRules") || line.StartsWith("Internal error") || line.StartsWith("LuaCOB") || (line.StartsWith("Failed to load") && !line.Contains("duplicate name"))) { hasError = true; } } if (score != null || !String.IsNullOrEmpty(missionVars)) { Trace.TraceInformation("Submitting score for mission " + modName); try { var service = GlobalConst.GetContentService(); Task.Factory.StartNew(() => { try { service.SubmitMissionScore(lobbyUserName, Utils.HashLobbyPassword(lobbyPassword), modName, score ?? 0, scoreFrame / 30, missionVars); } catch (Exception ex) { Trace.TraceError("Error sending score: {0}", ex); } }); } catch (Exception ex) { Trace.TraceError(string.Format("Error sending mission score: {0}", ex)); } } var modOk = GlobalConst.IsZkMod(modName); // submit main stats if (!isCheating && !isCrash && modOk && gameEndedOk) { if (isHosting) { var service = GlobalConst.GetSpringieService(); try { battleResult.EngineBattleID = gameId; battleResult.ReplayName = demoFileName; // set victory team for all allied with currently alive foreach (var p in statsPlayers.Values.Where(x => !x.IsSpectator && x.LoseTime == null)) { foreach (var q in statsPlayers.Values.Where(x => !x.IsSpectator && x.AllyNumber == p.AllyNumber)) { q.IsVictoryTeam = true; } } if (StartContext != null) { var result = service.SubmitSpringBattleResult(StartContext, lobbyPassword, battleResult, statsPlayers.Values.ToList(), statsData); if (result != null) { foreach (var line in result.Split('\n')) { client.Say(SayPlace.Battle, "", line, true); } } } } catch (Exception ex) { Trace.TraceError("Error sending game result: {0}", ex); } } if (statsData.Count > 1) { // must be more than 1 line - 1 is player list var statsService = new StatsCollector { Proxy = null }; try { statsService.SubmitGameEx(gameId, modName, mapName, statsData.ToArray()); } catch (Exception ex) { Trace.TraceError("Error sending game stats: {0}", ex); } } } } catch (Exception ex) { Trace.TraceError("Error processing spring log: {0}", ex); } }
/// <summary> /// Starts spring game /// </summary> /// <param name="client">tasclient to get current battle from</param> /// <param name="priority">spring process priority</param> /// <param name="affinity">spring process cpu affinity</param> /// <param name="scriptOverride">if set, overrides generated script with supplied one</param> /// <param name="userName">lobby user name - used to submit score</param> /// <param name="passwordHash">lobby password hash - used to submit score</param> /// <returns>generates script</returns> public string StartGame(TasClient client, ProcessPriorityClass?priority, int?affinity, string scriptOverride, bool useSafeMode = false, bool useMultithreaded = false, BattleContext contextOverride = null, Battle battleOverride = null) { if (!File.Exists(paths.Executable) && !File.Exists(paths.DedicatedServer)) { throw new ApplicationException(string.Format("Spring or dedicated server executable not found: {0}, {1}", paths.Executable, paths.DedicatedServer)); } this.client = client; wasKilled = false; if (!IsRunning) { gameEndedOk = false; IsBattleOver = false; lobbyUserName = client.UserName; lobbyPassword = client.UserPassword; battleResult = new BattleResult(); talker = new Talker(); talker.SpringEvent += talker_SpringEvent; var battle = battleOverride ?? client.MyBattle; isHosting = client != null && battle != null && battle.Founder.Name == client.MyUser.Name; if (isHosting) { scriptPath = Utils.MakePath(paths.WritableDirectory, "script_" + battle.Founder + ".txt").Replace('\\', '/'); } else { scriptPath = Utils.MakePath(paths.WritableDirectory, "script.txt").Replace('\\', '/'); } statsPlayers.Clear(); statsData.Clear(); StartContext = null; string script; if (!string.IsNullOrEmpty(scriptOverride)) { battleResult.IsMission = true; isHosting = false; script = scriptOverride; } else { List <UserBattleStatus> players; battleGuid = Guid.NewGuid(); var service = GlobalConst.GetSpringieService(); SpringBattleStartSetup startSetup = null; if (isHosting && GlobalConst.IsZkMod(battle.ModName)) { try { StartContext = contextOverride ?? battle.GetContext(); startSetup = service.GetSpringBattleStartSetup(StartContext); if (startSetup.BalanceTeamsResult != null) { StartContext.Players = startSetup.BalanceTeamsResult.Players; StartContext.Bots = startSetup.BalanceTeamsResult.Bots; } connectedPlayers.Clear(); foreach (var p in StartContext.Players) { p.IsIngame = true; } } catch (Exception ex) { Trace.TraceError("Error getting start setup: {0}", ex); } } script = battle.GenerateScript(out players, client.MyUser, talker.LoopbackPort, battleGuid.ToString(), startSetup); battleResult.IsMission = battle.IsMission; battleResult.IsBots = battle.Bots.Any(); battleResult.Title = battle.Title; battleResult.Mod = battle.ModName; battleResult.Map = battle.MapName; battleResult.EngineVersion = paths.SpringVersion; talker.SetPlayers(players); statsPlayers = players.ToDictionary(x => x.Name, x => new BattlePlayerResult { LobbyID = x.LobbyUser.AccountID, AllyNumber = x.AllyNumber, CommanderType = null, // todo commandertype IsSpectator = x.IsSpectator, IsVictoryTeam = false, }); } if (isHosting) { timer.Start(); } File.WriteAllText(scriptPath, script); LogLines = new StringBuilder(); var optirun = Environment.GetEnvironmentVariable("OPTIRUN"); process = new Process(); process.StartInfo.CreateNoWindow = true; List <string> arg = new List <string>(); if (string.IsNullOrEmpty(optirun)) { if (UseDedicatedServer) { process.StartInfo.FileName = paths.DedicatedServer; process.StartInfo.WorkingDirectory = Path.GetDirectoryName(paths.DedicatedServer); } else { process.StartInfo.FileName = useMultithreaded ? paths.MtExecutable : paths.Executable; process.StartInfo.WorkingDirectory = Path.GetDirectoryName(paths.Executable); } } else { Trace.TraceInformation("Using optirun {0} to start the game (OPTIRUN env var defined)", optirun); process.StartInfo.FileName = optirun; arg.Add(string.Format("\"{0}\"", (useMultithreaded ? paths.MtExecutable : paths.Executable))); } arg.Add(string.Format("--config \"{0}\"", paths.GetSpringConfigPath())); process.StartInfo.EnvironmentVariables["OMP_WAIT_POLICY"] = "ACTIVE"; if (useSafeMode) { arg.Add("--safemode"); } arg.Add(string.Format("\"{0}\"", scriptPath)); //Trace.TraceInformation("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments); process.StartInfo.Arguments = string.Join(" ", arg); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.Exited += springProcess_Exited; process.ErrorDataReceived += process_ErrorDataReceived; process.OutputDataReceived += process_OutputDataReceived; process.EnableRaisingEvents = true; gamePrivateMessages = new Dictionary <string, int>(); battleResult.StartTime = DateTime.UtcNow; process.Start(); process.BeginOutputReadLine(); process.BeginErrorReadLine(); if (IsRunning && SpringStarted != null) { SpringStarted(this, EventArgs.Empty); } Utils.StartAsync(() => { Thread.Sleep(1000); try { if (priority != null) { process.PriorityClass = priority.Value; } if (affinity != null) { process.ProcessorAffinity = (IntPtr)affinity.Value; } } catch (Exception ex) { Trace.TraceWarning("Error setting spring process affinity: {0}", ex); } }); return(script); } else { Trace.TraceError("Spring already running"); } return(null); }
protected override bool PerformInit(TasSayEventArgs e, string[] words, out string question, out int winCount) { winCount = 0; question = null; if (spring.IsRunning) { AutoHost.Respond(tas, spring, e, "Cannot change map while the game is running"); return(false); } else { string[] vals; int[] indexes; if (words.Length > 0) { ah.FilterMaps(words, out vals, out indexes); if (vals.Length > 0) { map = vals[0]; var resource = ah.cache.FindResourceData(new string[] { map }, ResourceType.Map); question = string.Format("Change map to {0} {2}/Maps/Detail/{1} ?", map, resource[0].ResourceID, GlobalConst.BaseSiteUrl); return(true); } else { AutoHost.Respond(tas, spring, e, "Cannot find such map"); return(false); } } else { try { if (tas.MyBattle != null && !spring.IsRunning) { var serv = GlobalConst.GetSpringieService(); Task.Factory.StartNew(() => { RecommendedMapResult foundMap; try { foundMap = serv.GetRecommendedMap(tas.MyBattle.GetContext(), true); } catch (Exception ex) { Trace.TraceError(ex.ToString()); return; } if (foundMap != null && foundMap.MapName != null && tas.MyBattle != null) { if (tas.MyBattle.MapName != foundMap.MapName) { map = foundMap.MapName; } } }); // I have no idea why it can't just work like the above way var resourceList = ah.cache.FindResourceData(new string[] { map }, ResourceType.Map); var resource = resourceList.Find(x => x.InternalName == map); question = string.Format("Change map to {0} {2}/Maps/Detail/{1} ?", map, resource.ResourceID, GlobalConst.BaseSiteUrl); return(true); } } catch (System.Exception ex) { AutoHost.Respond(tas, spring, e, ex.ToString()); //System.Diagnostics.Trace.TraceError(ex.ToString()); } return(false); } } }