public CurrentLobbyStats GetCurrentStats() { var ret = new CurrentLobbyStats(); foreach (var u in client.ExistingUsers.Values) { if (!u.IsBot && !u.IsInGame && !u.IsInBattleRoom) { ret.UsersIdle++; } } foreach (var b in client.ExistingBattles.Values) { if (!GlobalConst.IsZkMod(b.ModName)) { continue; } foreach (var u in b.Users.Select(x => x.LobbyUser)) { if (u.IsBot) { continue; } if (u.IsInGame) { ret.UsersFighting++; } else if (u.IsInBattleRoom) { ret.UsersWaiting++; } } if (b.IsInGame) { ret.BattlesRunning++; } else { ret.BattlesWaiting++; } } return(ret); }
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 { using (var service = new ContentService { Proxy = null }) { service.SubmitMissionScoreCompleted += (s, e) => { if (e.Error != null) { if (e.Error is WebException) { Trace.TraceWarning("Error sending score: {0}", e.Error); } else { Trace.TraceError("Error sending score: {0}", e.Error); } } }; service.SubmitMissionScoreAsync(lobbyUserName, Utils.HashLobbyPassword(lobbyPassword), modName, score ?? 0, scoreFrame / 30, missionVars); } } 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 = new SpringieService() { Proxy = null }; var mis = new ContentService() { Proxy = null }; 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, Enumerable.ToArray(statsPlayers.Values), statsData.ToArray()); if (result != null) { foreach (var line in result.Split('\n')) { client.Say(TasClient.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) { 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; isHosting = client != null && client.MyBattle != null && client.MyBattle.Founder.Name == client.MyUser.Name; if (isHosting) { scriptPath = Utils.MakePath(paths.WritableDirectory, "script_" + client.MyBattle.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 = new SpringieService() { Proxy = null }; SpringBattleStartSetup startSetup = null; if (isHosting && GlobalConst.IsZkMod(client.MyBattle.ModName)) { try { StartContext = client.MyBattle.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 = client.MyBattle.GenerateScript(out players, client.MyUser, talker.LoopbackPort, battleGuid.ToString(), startSetup); battleResult.IsMission = client.MyBattle.IsMission; battleResult.IsBots = client.MyBattle.Bots.Any(); battleResult.Title = client.MyBattle.Title; battleResult.Mod = client.MyBattle.ModName; battleResult.Map = client.MyBattle.MapName; battleResult.EngineVersion = paths.SpringVersion; talker.SetPlayers(players); statsPlayers = players.ToDictionary(x => x.Name, x => new BattlePlayerResult { LobbyID = x.LobbyUser.LobbyID, AllyNumber = x.AllyNumber, CommanderType = null, // todo commandertype IsSpectator = x.IsSpectator, IsVictoryTeam = false, Rank = x.LobbyUser.Rank, }); } 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); }