From 2fc2109a2e2bbdf84ac6eebd17249783a0ae2f62 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sun, 15 Apr 2018 20:27:43 -0500 Subject: [PATCH] started work on T6M parsing rest api fixed bug in login program preventing regular users from executing commands make log reading async and changed encoding to UTF7 --- Application/EventParsers/T6MEventParser.cs | 26 ++++++-- Application/Manager.cs | 4 -- Application/RconParsers/T6MRConParser.cs | 76 ++++++++++++++++++++++ Application/Server.cs | 22 +++++-- Plugins/Login/Plugin.cs | 3 + SharedLibraryCore/Event.cs | 1 + SharedLibraryCore/File.cs | 16 ++--- SharedLibraryCore/Objects/Player.cs | 3 +- SharedLibraryCore/RCon/Connection.cs | 4 +- SharedLibraryCore/Utilities.cs | 11 ++-- 10 files changed, 131 insertions(+), 35 deletions(-) diff --git a/Application/EventParsers/T6MEventParser.cs b/Application/EventParsers/T6MEventParser.cs index 98e1ad544..ee51e5b40 100644 --- a/Application/EventParsers/T6MEventParser.cs +++ b/Application/EventParsers/T6MEventParser.cs @@ -12,22 +12,34 @@ namespace Application.EventParsers { public GameEvent GetEvent(Server server, string logLine) { - string[] lineSplit = logLine.Split(';'); - string cleanedEventName = Regex.Replace(lineSplit[0], @" +[0-9]+:[0-9]+ +", ""); + string cleanedLogLine = Regex.Replace(logLine, @"^ *[0-9]+:[0-9]+ *", ""); + string[] lineSplit = cleanedLogLine.Split(';'); - if (cleanedEventName[0] == 'K') + if (lineSplit[0][0] == 'K') { return new GameEvent() { Type = GameEvent.EventType.Script, - Data = logLine, + Data = cleanedLogLine, Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)), Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)), Owner = server }; } - if (cleanedEventName == "say") + if (lineSplit[0][0] == 'D') + { + return new GameEvent() + { + Type = GameEvent.EventType.Damage, + Data = cleanedLogLine, + Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)), + Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)), + Owner = server + }; + } + + if (lineSplit[0] == "say") { return new GameEvent() { @@ -39,7 +51,7 @@ namespace Application.EventParsers }; } - if (cleanedEventName.Contains("ShutdownGame")) + if (lineSplit[0].Contains("ShutdownGame")) { return new GameEvent() { @@ -57,7 +69,7 @@ namespace Application.EventParsers }; } - if (cleanedEventName.Contains("InitGame")) + if (lineSplit[0].Contains("InitGame")) { return new GameEvent() { diff --git a/Application/Manager.cs b/Application/Manager.cs index 3c91eb48a..d6dad8917 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -313,13 +313,9 @@ namespace IW4MAdmin.Application public ClientService GetClientService() => ClientSvc; public AliasService GetAliasService() => AliasSvc; public PenaltyService GetPenaltyService() => PenaltySvc; - public IConfigurationHandler GetApplicationSettings() => ConfigHandler; - public IDictionary GetPrivilegedClients() => PrivilegedClients; - public IEventApi GetEventApi() => Api; - public bool ShutdownRequested() => !Running; } } diff --git a/Application/RconParsers/T6MRConParser.cs b/Application/RconParsers/T6MRConParser.cs index bb9564ae1..cd2d8ced5 100644 --- a/Application/RconParsers/T6MRConParser.cs +++ b/Application/RconParsers/T6MRConParser.cs @@ -9,11 +9,49 @@ using SharedLibraryCore.Objects; using SharedLibraryCore.RCon; using SharedLibraryCore.Exceptions; using System.Text; +using System.Linq; +using System.Net.Http; namespace Application.RconParsers { public class T6MRConParser : IRConParser { + class T6MResponse + { + public class SInfo + { + public short Com_maxclients { get; set; } + public string Game { get; set; } + public string Gametype { get; set; } + public string Mapname { get; set; } + public short NumBots { get; set; } + public short NumClients { get; set; } + public short Round { get; set; } + public string Sv_hostname { get; set; } + } + + public class PInfo + { + public short Assists { get; set; } + public string Clan { get; set; } + public short Deaths { get; set; } + public short Downs { get; set; } + public short Headshots { get; set; } + public short Id { get; set; } + public bool IsBot { get; set; } + public short Kills { get; set; } + public string Name { get; set; } + public short Ping { get; set; } + public short Revives { get; set; } + public int Score { get; set; } + public long Xuid { get; set; } + public string Ip { get; set; } + } + + public SInfo Info { get; set; } + public PInfo[] Players { get; set; } + } + private static CommandPrefix Prefixes = new CommandPrefix() { Tell = "tell {0} {1}", @@ -65,6 +103,8 @@ namespace Application.RconParsers { string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); return ClientsFromStatus(response); + + //return ClientsFromResponse(connection); } public async Task SetDvarAsync(Connection connection, string dvarName, object dvarValue) @@ -74,6 +114,42 @@ namespace Application.RconParsers return true; } + private async Task> ClientsFromResponse(Connection conn) + { + using (var client = new HttpClient()) + { + client.BaseAddress = new Uri($"http://{conn.Endpoint.Address}:{conn.Endpoint.Port}/"); + + try + { + var parameters = new FormUrlEncodedContent(new[] + { + new KeyValuePair("rcon_password", conn.RConPassword) + }); + + var serverResponse = await client.PostAsync("/info", parameters); + var serverResponseObject = Newtonsoft.Json.JsonConvert.DeserializeObject(await serverResponse.Content.ReadAsStringAsync()); + + return serverResponseObject.Players.Select(p => new Player() + { + Name = p.Name, + NetworkId = p.Xuid, + ClientNumber = p.Id, + IPAddress = p.Ip.Split(':')[0].ConvertToIP(), + Ping = p.Ping, + Score = p.Score, + IsBot = p.IsBot, + }).ToList(); + } + + catch (HttpRequestException e) + { + throw new NetworkException(e.Message); + } + } + } + + private List ClientsFromStatus(string[] status) { List StatusPlayers = new List(); diff --git a/Application/Server.cs b/Application/Server.cs index 687b78149..3c7f0622f 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -48,7 +48,10 @@ namespace IW4MAdmin override public async Task AddPlayer(Player polledPlayer) { - if (polledPlayer.Ping == 999 || polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) || polledPlayer.ClientNumber < 0) + + if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot)|| + polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) || + polledPlayer.ClientNumber < 0) { //Logger.WriteDebug($"Skipping client not in connected state {P}"); return true; @@ -430,6 +433,7 @@ namespace IW4MAdmin DateTime lastCount = DateTime.Now; DateTime tickTime = DateTime.Now; bool firstRun = true; + int count = 0; override public async Task ProcessUpdatesAsync(CancellationToken cts) { @@ -510,14 +514,13 @@ namespace IW4MAdmin if (l_size != LogFile.Length()) { - // this should be the longest running task - await Task.FromResult(lines = LogFile.Tail(12)); + lines = l_size != -1 ? await LogFile.Tail(12) : lines; if (lines != oldLines) { l_size = LogFile.Length(); int end = (lines.Length == oldLines.Length) ? lines.Length - 1 : Math.Abs((lines.Length - oldLines.Length)) - 1; - for (int count = 0; count < lines.Length; count++) + for (count = 0; count < lines.Length; count++) { if (lines.Length < 1 && oldLines.Length < 1) continue; @@ -561,6 +564,13 @@ namespace IW4MAdmin return false; } + catch (InvalidOperationException) + { + Logger.WriteWarning("Event could not parsed properly"); + Logger.WriteDebug($"Log Line: {lines[count]}"); + return false; + } + catch (Exception E) { Logger.WriteError($"Encountered error on {IP}:{Port}"); @@ -654,7 +664,7 @@ namespace IW4MAdmin string mainPath = EventParser.GetGameDir(); mainPath = (GameName == Game.IW4 && onelog.Value > 0) ? "main" : mainPath; #if DEBUG - // basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II"; + basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II"; #endif string logPath = game.Value == string.Empty ? $"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" : @@ -680,7 +690,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/Login/Plugin.cs b/Plugins/Login/Plugin.cs index c012b9a79..47eb46c2b 100644 --- a/Plugins/Login/Plugin.cs +++ b/Plugins/Login/Plugin.cs @@ -36,6 +36,9 @@ namespace IW4MAdmin.Plugins.Login if (E.Type == GameEvent.EventType.Command) { + if (E.Origin.Level < SharedLibraryCore.Objects.Player.Permission.Moderator) + return Task.CompletedTask; + if (((Command)E.Extra).Name == new SharedLibraryCore.Commands.CSetPassword().Name && E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].Password == null) return Task.CompletedTask; diff --git a/SharedLibraryCore/Event.cs b/SharedLibraryCore/Event.cs index fe8fb262c..16f5983fa 100644 --- a/SharedLibraryCore/Event.cs +++ b/SharedLibraryCore/Event.cs @@ -36,6 +36,7 @@ namespace SharedLibraryCore // FROM GAME Script, Kill, + Damage, Death, } diff --git a/SharedLibraryCore/File.cs b/SharedLibraryCore/File.cs index 8d0e09a21..4136a7813 100644 --- a/SharedLibraryCore/File.cs +++ b/SharedLibraryCore/File.cs @@ -4,6 +4,7 @@ using System.Text; using System.IO; using System.Net; using System.Net.Http; +using System.Threading.Tasks; namespace SharedLibraryCore { @@ -23,12 +24,6 @@ namespace SharedLibraryCore FileCache = cl.GetStringAsync(Location).Result.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); } - public override string[] Tail(int lineCount) - { - // Retrieve(); - return FileCache; - } - public override long Length() { Retrieve(); @@ -44,7 +39,8 @@ namespace SharedLibraryCore if (fileName != string.Empty) { Name = fileName; - Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)); + Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Encoding.UTF7); + sze = Handle.BaseStream.Length; } } @@ -70,20 +66,20 @@ namespace SharedLibraryCore return Handle?.ReadToEnd(); } - public virtual String[] Tail(int lineCount) + public virtual async Task Tail(int lineCount) { var buffer = new List(lineCount); string line; for (int i = 0; i < lineCount; i++) { - line = Handle.ReadLine(); + line = await Handle.ReadLineAsync(); if (line == null) return buffer.ToArray(); buffer.Add(line); } int lastLine = lineCount - 1; //The index of the last line read from the buffer. Everything > this index was read earlier than everything <= this indes - while (null != (line = Handle.ReadLine())) + while (null != (line = await Handle.ReadLineAsync())) { lastLine++; if (lastLine == lineCount) lastLine = 0; diff --git a/SharedLibraryCore/Objects/Player.cs b/SharedLibraryCore/Objects/Player.cs index 8b0574c6c..7357c1bef 100644 --- a/SharedLibraryCore/Objects/Player.cs +++ b/SharedLibraryCore/Objects/Player.cs @@ -77,7 +77,8 @@ namespace SharedLibraryCore.Objects public int Score { get; set; } [NotMapped] public IList Meta { get; set; } - + [NotMapped] + public bool IsBot { get; set; } private int _ipaddress; public override int IPAddress { diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs index e0f44fe9d..8664b2463 100644 --- a/SharedLibraryCore/RCon/Connection.cs +++ b/SharedLibraryCore/RCon/Connection.cs @@ -34,8 +34,8 @@ namespace SharedLibraryCore.RCon public class Connection { - IPEndPoint Endpoint; - string RConPassword; + public IPEndPoint Endpoint { get; private set; } + public string RConPassword { get; private set; } Socket ServerConnection; ILogger Log; int FailedSends; diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 5737a16a8..a2993b0ce 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -331,7 +331,8 @@ namespace SharedLibraryCore Level = client.Level, LastConnection = client.LastConnection == DateTime.MinValue ? DateTime.UtcNow : client.LastConnection, CurrentAlias = client.CurrentAlias, - CurrentAliasId = client.CurrentAlias.AliasId + CurrentAliasId = client.CurrentAlias.AliasId, + IsBot = client.NetworkId == -1 }; } @@ -366,13 +367,13 @@ namespace SharedLibraryCore return pID; } - public static async Task> GetDvarAsync(this Server server, string dvarName) => await server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName); + public static Task> GetDvarAsync(this Server server, string dvarName) => server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName); - public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue) => await server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue); + public static Task SetDvarAsync(this Server server, string dvarName, object dvarValue) => server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue); - public static async Task ExecuteCommandAsync(this Server server, string commandName) => await server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName); + public static Task ExecuteCommandAsync(this Server server, string commandName) => server.RconParser.ExecuteCommandAsync(server.RemoteConnection, commandName); - public static async Task> GetStatusAsync(this Server server) => await server.RconParser.GetStatusAsync(server.RemoteConnection); + public static Task> GetStatusAsync(this Server server) => server.RconParser.GetStatusAsync(server.RemoteConnection); } }