From 1a50391bfeb969740590096bd95b7cd56598495a Mon Sep 17 00:00:00 2001 From: RaidMax Date: Tue, 10 Apr 2018 19:25:44 -0500 Subject: [PATCH] exit works correctly again changes to rcon for T6M hopefully fixed some stat issues (spm and database errors) --- Application/Main.cs | 8 +- Application/Server.cs | 4 +- Plugins/Stats/Helpers/StatManager.cs | 39 ++++++---- SharedLibraryCore/RCon/Connection.cs | 10 ++- SharedLibraryCore/Utilities.cs | 112 +++++++++++++++++++++------ 5 files changed, 127 insertions(+), 46 deletions(-) diff --git a/Application/Main.cs b/Application/Main.cs index 1d2b3a3da..4f899673b 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -37,7 +37,6 @@ namespace IW4MAdmin.Application ServerManager = ApplicationManager.GetInstance(); ServerManager.Init().Wait(); - Task.Run(() => ServerManager.Start()); Task.Run(() => { @@ -60,11 +59,12 @@ namespace IW4MAdmin.Application Console.Write('>'); } while (ServerManager.Running); - - Console.WriteLine("Shutdown complete"); }); - WebfrontCore.Program.Init(ServerManager); + Task.Run(() => WebfrontCore.Program.Init(ServerManager)); + ServerManager.Start(); + ServerManager.Logger.WriteVerbose("Shutdown complete"); + } catch (Exception e) diff --git a/Application/Server.cs b/Application/Server.cs index 70240abcb..eaf49d8df 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -662,7 +662,7 @@ namespace IW4MAdmin // patch for T5M:V2 log path mainPath = (GameName == Game.T5M) ? "rzodemo" : mainPath; // patch for T6M:PLUTONIUM - mainPath = (GameName == Game.T6M) ? "t6r/data" : mainPath; + mainPath = (GameName == Game.T6M) ? $"t6r{Path.DirectorySeparatorChar}data" : mainPath; string logPath = (game.Value == "" || onelog?.Value == 1) ? $"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" : @@ -682,7 +682,7 @@ namespace IW4MAdmin Logger.WriteInfo($"Log file is {logPath}"); #if DEBUG - //LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php"); + LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php"); #else await Broadcast("IW4M Admin is now ^2ONLINE"); #endif diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 1c68d387b..f32b968c3 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -156,6 +156,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(Log, clientStats)); + // todo: look at this more + statsSvc.ClientStatSvc.Update(clientStats); + await statsSvc.ClientStatSvc.SaveChangesAsync(); + return clientStats; } @@ -192,6 +196,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers UpdateStats(clientStats); // todo: should this be saved every disconnect? + statsSvc.ClientStatSvc.Update(clientStats); await statsSvc.ClientStatSvc.SaveChangesAsync(); // increment the total play time serverStats.TotalPlayTime += (int)(DateTime.UtcNow - pl.LastConnection).TotalSeconds; @@ -260,15 +265,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers return; } - var playerDetection = Servers[serverId].PlayerDetections[attacker.ClientId]; - var playerStats = Servers[serverId].PlayerStats[attacker.ClientId]; + var clientDetection = Servers[serverId].PlayerDetections[attacker.ClientId]; + var clientStats = Servers[serverId].PlayerStats[attacker.ClientId]; // increment their hit count if (kill.DeathType == IW4Info.MeansOfDeath.MOD_PISTOL_BULLET || kill.DeathType == IW4Info.MeansOfDeath.MOD_RIFLE_BULLET || kill.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT) { - playerStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1; + clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1; + + statsSvc.ClientStatSvc.Update(clientStats); await statsSvc.ClientStatSvc.SaveChangesAsync(); } @@ -299,8 +306,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } } - await executePenalty(playerDetection.ProcessKill(kill)); - await executePenalty(playerDetection.ProcessTotalRatio(playerStats)); + await executePenalty(clientDetection.ProcessKill(kill)); + await executePenalty(clientDetection.ProcessTotalRatio(clientStats)); } } @@ -347,10 +354,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers if (streakMessage != string.Empty) await attacker.Tell(streakMessage); - + // todo: do we want to save this immediately? var statsSvc = ContextThreads[serverId]; - statsSvc.ClientStatSvc.SaveChanges(); + statsSvc.ClientStatSvc.Update(attackerStats); + statsSvc.ClientStatSvc.Update(victimStats); + await statsSvc.ClientStatSvc.SaveChangesAsync(); } /// @@ -393,16 +402,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers /// private EFClientStatistics UpdateStats(EFClientStatistics clientStats) { + // prevent NaN or inactive time lowering SPM + if ((DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0 < 0.1 || + (DateTime.UtcNow - clientStats.LastActive).TotalSeconds / 60.0 > 3 || + clientStats.SessionScore < 1) + return clientStats; + double timeSinceLastCalc = (DateTime.UtcNow - clientStats.LastStatCalculation).TotalSeconds / 60.0; double timeSinceLastActive = (DateTime.UtcNow - clientStats.LastActive).TotalSeconds / 60.0; - // prevent NaN or inactive time lowering SPM - if (timeSinceLastCalc == 0 || timeSinceLastActive > 3 || clientStats.SPM < 1) - return clientStats; - // calculate the players Score Per Minute for the current session - int currentScore = clientStats.SessionScore; - double killSPM = currentScore / (timeSinceLastCalc * 60.0); + int scoreDifference = clientStats.LastScore == 0 ? 0 : clientStats.SessionScore - clientStats.LastScore; + double killSPM = scoreDifference / timeSinceLastCalc; // calculate how much the KDR should weigh // 1.637 is a Eddie-Generated number that weights the KDR nicely @@ -427,7 +438,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers clientStats.Skill = Math.Round((clientStats.SPM * KDRWeight), 3); clientStats.LastStatCalculation = DateTime.UtcNow; - clientStats.LastScore = currentScore; + clientStats.LastScore = clientStats.SessionScore; return clientStats; } diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs index f24b18482..946d1fd19 100644 --- a/SharedLibraryCore/RCon/Connection.cs +++ b/SharedLibraryCore/RCon/Connection.cs @@ -177,10 +177,10 @@ namespace SharedLibraryCore.RCon { case StaticHelpers.QueryType.DVAR: case StaticHelpers.QueryType.COMMAND: - queryString = $"ÿÿÿÿ\x02rcon {RConPassword} {parameters}"; + queryString = $"ÿÿÿÿrcon {RConPassword} {parameters}"; break; case StaticHelpers.QueryType.GET_STATUS: - queryString = "ÿÿÿÿ\x02getstatus"; + queryString = "ÿÿÿÿgetstatus"; break; } @@ -233,6 +233,12 @@ namespace SharedLibraryCore.RCon if (!success) { + // t6m doesn't respond to set requests + if (type == StaticHelpers.QueryType.DVAR && parameters.Contains("set ")) + { + return await Task.FromResult(new string[] { "" }); + } + FailedReceives++; #if DEBUG Log.WriteDebug($"{FailedReceives} failed receives from {ServerConnection.RemoteEndPoint.ToString()}"); diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 1ab980bc1..dd047934d 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -52,7 +52,7 @@ namespace SharedLibraryCore return newStr; } - public static List PlayersFromStatus(string[] Status) + public static List PlayersFromStatus(this Server sv, string[] Status) { List StatusPlayers = new List(); @@ -65,9 +65,22 @@ namespace SharedLibraryCore String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int cID = -1; int Ping = -1; - Int32.TryParse(playerInfo[2], out Ping); - String cName = Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(StripColors(responseLine.Substring(46, 18)).Trim()))); - long npID = Regex.Match(responseLine, @"([a-z]|[0-9]){16}", RegexOptions.IgnoreCase).Value.ConvertLong(); + + try + { + Ping = (sv.GameName != Game.T6M) ? + Int32.Parse(playerInfo[2]) : + Int32.Parse(playerInfo[3]); + } + + catch (FormatException) { } + String cName = (sv.GameName != Game.T6M) ? + Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(StripColors(responseLine.Substring(46, 18)).Trim()))) : + Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(StripColors(responseLine.Substring(50, 15)).Trim()))); + long npID = sv.GameName != Game.T6M ? + Regex.Match(responseLine, @"([a-z]|[0-9]){16}", RegexOptions.IgnoreCase).Value.ConvertLong() : + playerInfo[4].ConvertLong(); + int.TryParse(playerInfo[0], out cID); var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}"); #if DEBUG @@ -76,7 +89,9 @@ namespace SharedLibraryCore int cIP = regex.Value.Split(':')[0].ConvertToIP(); regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+"); - int score = Int32.Parse(regex.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]); + int score = (sv.GameName != Game.T6M) ? + Int32.Parse(regex.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]) : + Int32.Parse(playerInfo[1]); Player P = new Player() { Name = cName, NetworkId = npID, ClientNumber = cID, IPAddress = cIP, Ping = Ping, Score = score }; StatusPlayers.Add(P); } @@ -391,31 +406,80 @@ namespace SharedLibraryCore public static async Task> GetDvarAsync(this Server server, string dvarName) { - string[] LineSplit = server.GameName != Game.T6M ? - await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, dvarName) : - await server.RemoteConnection.SendQueryAsync(QueryType.COMMAND, $"get {dvarName}"); - - if (LineSplit.Length < 3) + string[] LineSplit = null; + bool t6m = false; + if (server.GameName == Game.UKN) { - var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); - e.Data["dvar_name"] = dvarName; - throw e; + LineSplit = await server.RemoteConnection.SendQueryAsync(QueryType.COMMAND, $"get {dvarName}"); + if (LineSplit.Where(l => l.Contains("Unknown command")).Count() > 0) + { + LineSplit = await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, dvarName); + } + + else + t6m = true; } - string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries); - - if (ValueSplit.Length != 5) + else if (server.GameName == Game.T6M) { - var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); - e.Data["dvar_name"] = dvarName; - throw e; + LineSplit = await server.RemoteConnection.SendQueryAsync(QueryType.COMMAND, $"get {dvarName}"); } - string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", ""); - string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", ""); - string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", ""); + else + { + LineSplit = await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, dvarName); ; + } - return new DVAR(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) }; + if (server.GameName != Game.T6M && !t6m) + { + if (LineSplit.Length < 3) + { + var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); + e.Data["dvar_name"] = dvarName; + throw e; + } + + string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries); + + if (ValueSplit.Length != 5) + { + var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); + e.Data["dvar_name"] = dvarName; + throw e; + } + + string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", ""); + string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", ""); + string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", ""); + + return new DVAR(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) }; + } + + else + { + if (LineSplit.Length < 2) + { + var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); + e.Data["dvar_name"] = dvarName; + throw e; + } + + string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }); + + if (ValueSplit.Length == 0) + { + var e = new Exceptions.DvarException($"DVAR \"{dvarName}\" does not exist"); + e.Data["dvar_name"] = dvarName; + throw e; + } + + string DvarName = dvarName; + string DvarCurrentValue = Regex.Replace(ValueSplit[1], @"\^[0-9]", ""); + + return new DVAR(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) }; + } + + } public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue) @@ -435,7 +499,7 @@ namespace SharedLibraryCore #else string[] response = await server.RemoteConnection.SendQueryAsync(QueryType.DVAR, "status"); #endif - return Utilities.PlayersFromStatus(response); + return server.PlayersFromStatus(response); } public static bool IsRunningOnMono() => Type.GetType("Mono.Runtime") != null;