diff --git a/.gitignore b/.gitignore index 890f16a1b..28797a3e9 100644 --- a/.gitignore +++ b/.gitignore @@ -229,4 +229,6 @@ bootstrap-custom.min.css /DiscordWebhook/env /DiscordWebhook/config.dev.json /GameLogServer/env -launchSettings.json \ No newline at end of file +launchSettings.json +/VpnDetectionPrivate.js +/Plugins/ScriptPlugins/VpnDetectionPrivate.js diff --git a/Application/Application.csproj b/Application/Application.csproj index 80fd79210..a1fc11b2f 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -6,7 +6,7 @@ 2.1.5 false RaidMax.IW4MAdmin.Application - 2.2.1.0 + 2.2.2.0 RaidMax Forever None IW4MAdmin @@ -31,8 +31,8 @@ true true - 2.2.1.0 - 2.2.1.0 + 2.2.2.0 + 2.2.2.0 diff --git a/Application/Core/ClientAuthentication.cs b/Application/Core/ClientAuthentication.cs index db3640f9e..fa064493c 100644 --- a/Application/Core/ClientAuthentication.cs +++ b/Application/Core/ClientAuthentication.cs @@ -1,4 +1,5 @@ -using SharedLibraryCore.Interfaces; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Interfaces; using SharedLibraryCore.Objects; using System; using System.Collections.Generic; @@ -9,16 +10,16 @@ namespace IW4MAdmin.Application.Core { class ClientAuthentication : IClientAuthentication { - private Queue ClientAuthenticationQueue; - private Dictionary AuthenticatedClients; + private Queue ClientAuthenticationQueue; + private Dictionary AuthenticatedClients; public ClientAuthentication() { - ClientAuthenticationQueue = new Queue(); - AuthenticatedClients = new Dictionary(); + ClientAuthenticationQueue = new Queue(); + AuthenticatedClients = new Dictionary(); } - public void AuthenticateClients(IList clients) + public void AuthenticateClients(IList clients) { // we need to un-auth all the clients that have disconnected var clientNetworkIds = clients.Select(c => c.NetworkId); @@ -33,10 +34,10 @@ namespace IW4MAdmin.Application.Core foreach (var client in clients) { // they've not been authenticated - if (!AuthenticatedClients.TryGetValue(client.NetworkId, out Player value)) + if (!AuthenticatedClients.TryGetValue(client.NetworkId, out EFClient value)) { // authenticate them - client.State = Player.ClientState.Authenticated; + client.State = EFClient.ClientState.Authenticated; AuthenticatedClients.Add(client.NetworkId, client); } else @@ -54,16 +55,16 @@ namespace IW4MAdmin.Application.Core // grab each client that's connected via log var clientToAuthenticate = ClientAuthenticationQueue.Dequeue(); // if they're not already authed, auth them - if (!AuthenticatedClients.TryGetValue(clientToAuthenticate.NetworkId, out Player value)) + if (!AuthenticatedClients.TryGetValue(clientToAuthenticate.NetworkId, out EFClient value)) { // authenticate them - clientToAuthenticate.State = Player.ClientState.Authenticated; + clientToAuthenticate.State = EFClient.ClientState.Authenticated; AuthenticatedClients.Add(clientToAuthenticate.NetworkId, clientToAuthenticate); } } } - public IList GetAuthenticatedClients() + public IList GetAuthenticatedClients() { if (AuthenticatedClients.Values.Count > 18) { @@ -74,7 +75,7 @@ namespace IW4MAdmin.Application.Core return AuthenticatedClients.Values.ToList(); } - public void RequestClientAuthentication(Player client) + public void RequestClientAuthentication(EFClient client) { ClientAuthenticationQueue.Enqueue(client); } diff --git a/Application/EventParsers/IW4EventParser.cs b/Application/EventParsers/IW4EventParser.cs index d0f80fdd5..cdbb5fd79 100644 --- a/Application/EventParsers/IW4EventParser.cs +++ b/Application/EventParsers/IW4EventParser.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using SharedLibraryCore; +using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using SharedLibraryCore.Objects; @@ -20,7 +21,7 @@ namespace IW4MAdmin.Application.EventParsers if (eventType == "JoinTeam") { - var origin = server.GetPlayersAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong()); + var origin = server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong()); return new GameEvent() { @@ -41,7 +42,7 @@ namespace IW4MAdmin.Application.EventParsers .Replace("\x15", "") .Trim(); - var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)); + var origin = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)); if (message[0] == '!' || message[0] == '@') { @@ -70,8 +71,8 @@ namespace IW4MAdmin.Application.EventParsers { if (!server.CustomCallback) { - var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)); - var target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)); + var origin = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)); + var target = server.GetClientsAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)); return new GameEvent() { @@ -86,8 +87,8 @@ namespace IW4MAdmin.Application.EventParsers if (eventType == "ScriptKill") { - var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); - var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()); + var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); + var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()); return new GameEvent() { Type = GameEvent.EventType.ScriptKill, @@ -100,8 +101,8 @@ namespace IW4MAdmin.Application.EventParsers if (eventType == "ScriptDamage") { - var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); - var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()); + var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); + var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()); return new GameEvent() { @@ -120,8 +121,8 @@ namespace IW4MAdmin.Application.EventParsers { if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success) { - var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); - var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); + var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()); + var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()); return new GameEvent() { @@ -146,12 +147,12 @@ namespace IW4MAdmin.Application.EventParsers Type = GameEvent.EventType.Join, Data = logLine, Owner = server, - Origin = new Player() + Origin = new EFClient() { Name = regexMatch.Groups[4].ToString().StripColors(), NetworkId = regexMatch.Groups[2].ToString().ConvertLong(), ClientNumber = Convert.ToInt32(regexMatch.Groups[3].ToString()), - State = Player.ClientState.Connecting, + State = EFClient.ClientState.Connecting, CurrentServer = server } }; @@ -185,11 +186,11 @@ namespace IW4MAdmin.Application.EventParsers { Type = GameEvent.EventType.MapEnd, Data = lineSplit[0], - Origin = new Player() + Origin = new EFClient() { ClientId = 1 }, - Target = new Player() + Target = new EFClient() { ClientId = 1 }, @@ -205,14 +206,8 @@ namespace IW4MAdmin.Application.EventParsers { Type = GameEvent.EventType.MapChange, Data = lineSplit[0], - Origin = new Player() - { - ClientId = 1 - }, - Target = new Player() - { - ClientId = 1 - }, + Origin = Utilities.IW4MAdminClient(server), + Target = Utilities.IW4MAdminClient(server), Owner = server, Extra = dump.DictionaryFromKeyValue() }; @@ -221,14 +216,8 @@ namespace IW4MAdmin.Application.EventParsers return new GameEvent() { Type = GameEvent.EventType.Unknown, - Origin = new Player() - { - ClientId = 1 - }, - Target = new Player() - { - ClientId = 1 - }, + Origin = Utilities.IW4MAdminClient(server), + Target = Utilities.IW4MAdminClient(server), Owner = server }; } diff --git a/Application/EventParsers/IW5EventParser.cs b/Application/EventParsers/IW5EventParser.cs index e452bdaa7..429b1bc32 100644 --- a/Application/EventParsers/IW5EventParser.cs +++ b/Application/EventParsers/IW5EventParser.cs @@ -1,4 +1,5 @@ using SharedLibraryCore; +using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using SharedLibraryCore.Objects; using System; @@ -23,7 +24,7 @@ namespace IW4MAdmin.Application.EventParsers int clientNum = Int32.Parse(lineSplit[2]); - var player = new Player() + var player = new EFClient() { NetworkId = lineSplit[1].ConvertLong(), ClientNumber = clientNum, @@ -33,11 +34,11 @@ namespace IW4MAdmin.Application.EventParsers return new GameEvent() { Type = GameEvent.EventType.Join, - Origin = new Player() + Origin = new EFClient() { ClientId = 1 }, - Target = new Player() + Target = new EFClient() { ClientId = 1 }, diff --git a/Application/Main.cs b/Application/Main.cs index 6ea5b2164..8f19df87a 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -110,7 +110,7 @@ namespace IW4MAdmin.Application var consoleTask = Task.Run(async () => { String userInput; - Player Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]); + var Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]); do { diff --git a/Application/Manager.cs b/Application/Manager.cs index be9aeb84c..b9b3bf8f2 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -19,6 +19,7 @@ using SharedLibraryCore.Events; using IW4MAdmin.Application.API.Master; using IW4MAdmin.Application.Migration; +using SharedLibraryCore.Database.Models; namespace IW4MAdmin.Application { @@ -26,7 +27,7 @@ namespace IW4MAdmin.Application { private List _servers; public List Servers => _servers.OrderByDescending(s => s.ClientNum).ToList(); - public Dictionary PrivilegedClients { get; set; } + public Dictionary PrivilegedClients { get; set; } public ILogger Logger => GetLogger(0); public bool Running { get; private set; } public bool IsInitialized { get; private set; } @@ -60,7 +61,7 @@ namespace IW4MAdmin.Application ClientSvc = new ClientService(); AliasSvc = new AliasService(); PenaltySvc = new PenaltyService(); - PrivilegedClients = new Dictionary(); + PrivilegedClients = new Dictionary(); ConfigHandler = new BaseConfigurationHandler("IW4MAdminSettings"); StartTime = DateTime.UtcNow; OnQuit = new ManualResetEventSlim(); @@ -144,7 +145,7 @@ namespace IW4MAdmin.Application if (e.Target != null) { // update the target incase they left or have newer info - e.Target = newEvent.Owner.GetPlayersAsList() + e.Target = newEvent.Owner.GetClientsAsList() .FirstOrDefault(p => p.NetworkId == e.Target.NetworkId); // we have to throw out the event because they left if (e.Target == null) @@ -285,7 +286,7 @@ namespace IW4MAdmin.Application } // todo: optimize this (or replace it) - var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted)) + var ipList = (await ClientSvc.Find(c => c.Level > EFClient.Permission.Trusted)) .Select(c => new { c.Password, @@ -299,7 +300,7 @@ namespace IW4MAdmin.Application { try { - PrivilegedClients.Add(a.ClientId, new Player() + PrivilegedClients.Add(a.ClientId, new EFClient() { Name = a.Name, ClientId = a.ClientId, @@ -593,13 +594,13 @@ namespace IW4MAdmin.Application return MessageTokens; } - public IList GetActiveClients() => _servers.SelectMany(s => s.Players).Where(p => p != null).ToList(); + public IList GetActiveClients() => _servers.SelectMany(s => s.Clients).Where(p => p != null).ToList(); public ClientService GetClientService() => ClientSvc; public AliasService GetAliasService() => AliasSvc; public PenaltyService GetPenaltyService() => PenaltySvc; public IConfigurationHandler GetApplicationSettings() => ConfigHandler; - public IDictionary GetPrivilegedClients() => PrivilegedClients; + public IDictionary GetPrivilegedClients() => PrivilegedClients; public bool ShutdownRequested() => !Running; public IEventHandler GetEventHandler() => Handler; diff --git a/Application/RconParsers/IW4RConParser.cs b/Application/RconParsers/IW4RConParser.cs index 0348808f4..d04046a5b 100644 --- a/Application/RconParsers/IW4RConParser.cs +++ b/Application/RconParsers/IW4RConParser.cs @@ -9,6 +9,7 @@ using SharedLibraryCore.Objects; using SharedLibraryCore; using SharedLibraryCore.RCon; using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Database.Models; namespace IW4MAdmin.Application.RconParsers { @@ -61,7 +62,7 @@ namespace IW4MAdmin.Application.RconParsers }; } - public async Task> GetStatusAsync(Connection connection) + public async Task> GetStatusAsync(Connection connection) { string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); return ClientsFromStatus(response); @@ -74,9 +75,9 @@ namespace IW4MAdmin.Application.RconParsers public virtual CommandPrefix GetCommandPrefixes() => Prefixes; - private List ClientsFromStatus(string[] Status) + private List ClientsFromStatus(string[] Status) { - List StatusPlayers = new List(); + List StatusPlayers = new List(); if (Status.Length < 4) throw new ServerException("Unexpected status response received"); @@ -106,7 +107,7 @@ namespace IW4MAdmin.Application.RconParsers string name = regex.Groups[5].Value.StripColors().Trim(); int ip = regex.Groups[7].Value.Split(':')[0].ConvertToIP(); - Player P = new Player() + var P = new EFClient() { Name = name, NetworkId = networkId, @@ -115,7 +116,7 @@ namespace IW4MAdmin.Application.RconParsers Ping = ping, Score = score, IsBot = ip == 0, - State = Player.ClientState.Connecting + State = EFClient.ClientState.Connecting }; StatusPlayers.Add(P); } diff --git a/Application/RconParsers/IW5MRConParser.cs b/Application/RconParsers/IW5MRConParser.cs index b0948610e..a2d5f49c6 100644 --- a/Application/RconParsers/IW5MRConParser.cs +++ b/Application/RconParsers/IW5MRConParser.cs @@ -9,6 +9,7 @@ using SharedLibraryCore.Interfaces; using SharedLibraryCore.Objects; using SharedLibraryCore.RCon; using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Database.Models; namespace IW4MAdmin.Application.RconParsers { @@ -105,7 +106,7 @@ namespace IW4MAdmin.Application.RconParsers }; } - public async Task> GetStatusAsync(Connection connection) + public async Task> GetStatusAsync(Connection connection) { string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); return ClientsFromStatus(response); @@ -118,9 +119,9 @@ namespace IW4MAdmin.Application.RconParsers return true; } - private List ClientsFromStatus(string[] status) + private List ClientsFromStatus(string[] status) { - List StatusPlayers = new List(); + List StatusPlayers = new List(); foreach (string statusLine in status) { @@ -145,7 +146,7 @@ namespace IW4MAdmin.Application.RconParsers regex = Regex.Match(responseLine, @" +(\d+ +){3}"); int score = Int32.Parse(regex.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]); - var p = new Player() + var p = new EFClient() { Name = name, NetworkId = networkId, @@ -154,7 +155,7 @@ namespace IW4MAdmin.Application.RconParsers Ping = Ping, Score = score, IsBot = false, - State = Player.ClientState.Connecting + State = EFClient.ClientState.Connecting }; StatusPlayers.Add(p); diff --git a/Application/RconParsers/T6MRConParser.cs b/Application/RconParsers/T6MRConParser.cs index 6a26d599d..9f38ae7fd 100644 --- a/Application/RconParsers/T6MRConParser.cs +++ b/Application/RconParsers/T6MRConParser.cs @@ -9,6 +9,7 @@ using SharedLibraryCore.Objects; using SharedLibraryCore.RCon; using SharedLibraryCore.Exceptions; using System.Text; +using SharedLibraryCore.Database.Models; namespace IW4MAdmin.Application.RconParsers { @@ -60,7 +61,7 @@ namespace IW4MAdmin.Application.RconParsers }; } - public async Task> GetStatusAsync(Connection connection) + public async Task> GetStatusAsync(Connection connection) { string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, "status"); return ClientsFromStatus(response); @@ -73,9 +74,9 @@ namespace IW4MAdmin.Application.RconParsers return true; } - private List ClientsFromStatus(string[] status) + private List ClientsFromStatus(string[] status) { - List StatusPlayers = new List(); + List StatusPlayers = new List(); foreach (string statusLine in status) { @@ -98,7 +99,7 @@ namespace IW4MAdmin.Application.RconParsers #endif int ipAddress = regex.Value.Split(':')[0].ConvertToIP(); regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+"); - var p = new Player() + var p = new EFClient() { Name = name, NetworkId = networkId, @@ -106,7 +107,7 @@ namespace IW4MAdmin.Application.RconParsers IPAddress = ipAddress, Ping = Ping, Score = 0, - State = Player.ClientState.Connecting, + State = EFClient.ClientState.Connecting, IsBot = networkId == 0 }; diff --git a/Application/Server.cs b/Application/Server.cs index 3d8fa41f6..3b61d5767 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -1,24 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using System.Text.RegularExpressions; -using System.Runtime.InteropServices; - +using IW4MAdmin.Application.EventParsers; +using IW4MAdmin.Application.IO; +using IW4MAdmin.Application.RconParsers; using SharedLibraryCore; -using SharedLibraryCore.Interfaces; -using SharedLibraryCore.Objects; +using SharedLibraryCore.Configuration; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Dtos; -using SharedLibraryCore.Configuration; using SharedLibraryCore.Exceptions; +using SharedLibraryCore.Interfaces; using SharedLibraryCore.Localization; - -using IW4MAdmin.Application.RconParsers; -using IW4MAdmin.Application.EventParsers; -using IW4MAdmin.Application.IO; +using SharedLibraryCore.Objects; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; namespace IW4MAdmin { @@ -58,234 +56,101 @@ namespace IW4MAdmin return Id; } - public async Task OnPlayerJoined(Player logClient) + public async Task OnClientJoined(EFClient polledClient) { - var existingClient = Players[logClient.ClientNumber]; + var existingClient = Clients[polledClient.ClientNumber]; - if (existingClient == null || - (existingClient.NetworkId != logClient.NetworkId && - existingClient.State != Player.ClientState.Connected)) + if (existingClient != null) { - Logger.WriteDebug($"Log detected {logClient} joining"); - Players[logClient.ClientNumber] = logClient; + await existingClient.OnJoin(polledClient.IPAddress); } - - await Task.CompletedTask; } - override public async Task AddPlayer(Player polledPlayer) + override public async Task OnClientConnected(EFClient clientFromLog) { - if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot) || - polledPlayer.Ping < 1 || - polledPlayer.ClientNumber < 0) - { - return false; - } - - // set this when they are waiting for authentication - if (Players[polledPlayer.ClientNumber] == null && - polledPlayer.State == Player.ClientState.Connecting) - { - Players[polledPlayer.ClientNumber] = polledPlayer; - return false; - } - -#if !DEBUG - if (polledPlayer.Name.Length < 3) - { - Logger.WriteDebug($"Kicking {polledPlayer} because their name is too short"); - string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_MINNAME"]); - await this.ExecuteCommandAsync(formattedKick); - return false; - } - - if (Players.FirstOrDefault(p => p != null && p.Name == polledPlayer.Name && p.NetworkId != polledPlayer.NetworkId) != null) - { - Logger.WriteDebug($"Kicking {polledPlayer} because their name is already in use"); - string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_NAME_INUSE"]); - await this.ExecuteCommandAsync(formattedKick); - return false; - } - - if (polledPlayer.Name == "Unknown Soldier" || - polledPlayer.Name == "UnknownSoldier" || - polledPlayer.Name == "CHEATER") - { - Logger.WriteDebug($"Kicking {polledPlayer} because their name is generic"); - string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_GENERICNAME"]); - await this.ExecuteCommandAsync(formattedKick); - return false; - } - - if (polledPlayer.Name.Where(c => Char.IsControl(c)).Count() > 0) - { - Logger.WriteDebug($"Kicking {polledPlayer} because their name contains control characters"); - string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_CONTROLCHARS"]); - await this.ExecuteCommandAsync(formattedKick); - return false; - } - -#endif - Logger.WriteDebug($"Client slot #{polledPlayer.ClientNumber} now reserved"); + Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved"); try { - Player player = null; - var client = await Manager.GetClientService().GetUnique(polledPlayer.NetworkId); + EFClient client = await Manager.GetClientService().GetUnique(clientFromLog.NetworkId); // first time client is connecting to server if (client == null) { - Logger.WriteDebug($"Client {polledPlayer} first time connecting"); - player = (await Manager.GetClientService().Create(polledPlayer)).AsPlayer(); + Logger.WriteDebug($"Client {clientFromLog} first time connecting"); + client = await Manager.GetClientService().Create(clientFromLog); } // client has connected in the past else { - client.LastConnection = DateTime.UtcNow; - client.Connections += 1; - var existingAlias = client.AliasLink.Children - .FirstOrDefault(a => a.Name == polledPlayer.Name && a.IPAddress == polledPlayer.IPAddress); + .FirstOrDefault(a => a.Name == clientFromLog.Name); if (existingAlias == null) { - Logger.WriteDebug($"Client {polledPlayer} has connected previously under a different ip/name"); + Logger.WriteDebug($"Client {clientFromLog} has connected previously under a different ip/name"); client.CurrentAlias = new EFAlias() { - IPAddress = polledPlayer.IPAddress, - Name = polledPlayer.Name, + // this gets updated on client join + IPAddress = clientFromLog.IPAddress, + Name = clientFromLog.Name, }; - // we need to update their new ip and name to the virtual property - client.Name = polledPlayer.Name; - client.IPAddress = polledPlayer.IPAddress; } else { client.CurrentAlias = existingAlias; client.CurrentAliasId = existingAlias.AliasId; - client.Name = existingAlias.Name; - client.IPAddress = existingAlias.IPAddress; } await Manager.GetClientService().Update(client); - player = client.AsPlayer(); } - // reserved slots stuff - if ((MaxClients - ClientNum) < ServerConfig.ReservedSlotNumber && - !player.IsPrivileged()) - { - Logger.WriteDebug($"Kicking {polledPlayer} their spot is reserved"); - string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_SLOT_IS_RESERVED"]); - await this.ExecuteCommandAsync(formattedKick); - return false; - } - - Logger.WriteInfo($"Client {player} connected..."); + Logger.WriteInfo($"Client {client} connected..."); // Do the player specific stuff - player.ClientNumber = polledPlayer.ClientNumber; - player.IsBot = polledPlayer.IsBot; - player.Score = polledPlayer.Score; - player.CurrentServer = this; + client.ClientNumber = clientFromLog.ClientNumber; + client.IsBot = clientFromLog.IsBot; + client.Score = clientFromLog.Score; + client.Ping = clientFromLog.Ping; + client.CurrentServer = this; - player.DelayedEvents = (Players[player.ClientNumber]?.DelayedEvents) ?? new Queue(); - Players[player.ClientNumber] = player; + Clients[client.ClientNumber] = client; + client.OnConnect(); - var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(player.AliasLinkId, player.IPAddress); - var currentBan = activePenalties.FirstOrDefault(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan); - - var currentAutoFlag = activePenalties.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1) - .Where(p => p.Active) - .OrderByDescending(p => p.When) - .FirstOrDefault(); - - // remove their auto flag status after a week - if (player.Level == Player.Permission.Flagged && - currentAutoFlag != null && - (DateTime.Now - currentAutoFlag.When).TotalDays > 7) - { - player.Level = Player.Permission.User; - } - - if (currentBan != null) - { - Logger.WriteInfo($"Banned client {player} trying to connect..."); - var autoKickClient = Utilities.IW4MAdminClient(this); - - // the player is permanently banned - if (currentBan.Type == Penalty.PenaltyType.Ban) - { - // don't store the kick message - string formattedKick = String.Format( - RconParser.GetCommandPrefixes().Kick, - polledPlayer.ClientNumber, - $"{loc["SERVER_BAN_PREV"]} {currentBan.Offense} ({loc["SERVER_BAN_APPEAL"]} {Website})"); - await this.ExecuteCommandAsync(formattedKick); - } - - else - { - string formattedKick = String.Format( - RconParser.GetCommandPrefixes().Kick, - polledPlayer.ClientNumber, - $"{loc["SERVER_TB_REMAIN"]} ({(currentBan.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})"); - await this.ExecuteCommandAsync(formattedKick); - } - - // reban the "evading" guid - if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban) - { - // hack: re apply the automated offense to the reban - if (currentBan.AutomatedOffense != null) - { - autoKickClient.AdministeredPenalties.Add(new EFPenalty() { AutomatedOffense = currentBan.AutomatedOffense }); - } - player.Ban($"{currentBan.Offense}", autoKickClient); - } - - // they didn't fully connect so empty their slot - Players[player.ClientNumber] = null; - return false; - } - - player.State = Player.ClientState.Connected; - return true; + client.State = EFClient.ClientState.Connected; } catch (Exception ex) { - Logger.WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {polledPlayer.Name}::{polledPlayer.NetworkId}"); + Logger.WriteError($"{loc["SERVER_ERROR_ADDPLAYER"]} {clientFromLog.Name}::{clientFromLog.NetworkId}"); Logger.WriteDebug(ex.Message); Logger.WriteDebug(ex.StackTrace); - return false; } } //Remove player by CLIENT NUMBER - override public async Task RemovePlayer(int cNum) + override public async Task RemoveClient(int cNum) { - if (cNum >= 0 && Players[cNum] != null) + if (cNum >= 0 && Clients[cNum] != null) { - Player Leaving = Players[cNum]; + EFClient Leaving = Clients[cNum]; // occurs when the player disconnects via log before being authenticated by RCon - if (Leaving.State != Player.ClientState.Connected) + if (Leaving.State != EFClient.ClientState.Connected) { - Players[cNum] = null; + Clients[cNum] = null; } else { Logger.WriteInfo($"Client {Leaving} [{Leaving.State.ToString().ToLower()}] disconnecting..."); - Leaving.State = Player.ClientState.Disconnecting; + Leaving.State = EFClient.ClientState.Disconnecting; Leaving.TotalConnectionTime += Leaving.ConnectionLength; Leaving.LastConnection = DateTime.UtcNow; await Manager.GetClientService().Update(Leaving); - Players[cNum] = null; + Clients[cNum] = null; } } } @@ -340,7 +205,7 @@ namespace IW4MAdmin if (E.Type == GameEvent.EventType.Command && E.Extra != null && (canExecuteCommand || - E.Origin?.Level == Player.Permission.Console)) + E.Origin?.Level == EFClient.Permission.Console)) { await (((Command)E.Extra).ExecuteAsync(E)); } @@ -355,25 +220,17 @@ namespace IW4MAdmin { if (E.Type == GameEvent.EventType.Connect) { - E.Origin.State = Player.ClientState.Authenticated; - // add them to the server - if (!await AddPlayer(E.Origin)) - { - E.Origin.State = Player.ClientState.Connecting; - Logger.WriteDebug("client didn't pass authentication, so we are discontinuing event"); - return false; - } - // hack: makes the event propgate with the correct info - E.Origin = Players[E.Origin.ClientNumber]; + E.Origin.State = EFClient.ClientState.Authenticated; + await OnClientConnected(E.Origin); ChatHistory.Add(new ChatInfo() { - Name = E.Origin?.Name ?? "ERROR!", + Name = E.Origin.Name, Message = "CONNECTED", Time = DateTime.UtcNow }); - if (E.Origin.Level > Player.Permission.Moderator) + if (E.Origin.Level > EFClient.Permission.Moderator) { E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count)); } @@ -381,7 +238,7 @@ namespace IW4MAdmin else if (E.Type == GameEvent.EventType.Join) { - await OnPlayerJoined(E.Origin); + await OnClientJoined(E.Origin); } else if (E.Type == GameEvent.EventType.Flag) @@ -446,11 +303,11 @@ namespace IW4MAdmin else if (E.Type == GameEvent.EventType.Quit) { - var origin = Players.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId); + var origin = Clients.FirstOrDefault(p => p != null && p.NetworkId == E.Origin.NetworkId); if (origin != null && // we only want to forward the event if they are connected. - origin.State == Player.ClientState.Connected && + origin.State == EFClient.ClientState.Connected && // make sure we don't get the disconnect event from every time the game ends origin.ConnectionLength < Manager.GetApplicationSettings().Configuration().RConPollRate) { @@ -465,9 +322,9 @@ namespace IW4MAdmin } else if (origin != null && - origin.State != Player.ClientState.Connected) + origin.State != EFClient.ClientState.Connected) { - await RemovePlayer(origin.ClientNumber); + await RemoveClient(origin.ClientNumber); } } @@ -481,9 +338,9 @@ namespace IW4MAdmin }); var currentState = E.Origin.State; - await RemovePlayer(E.Origin.ClientNumber); + await RemoveClient(E.Origin.ClientNumber); - if (currentState != Player.ClientState.Connected) + if (currentState != EFClient.ClientState.Connected) { throw new ServerException("Disconnecting player was not in a connected state"); } @@ -569,12 +426,16 @@ namespace IW4MAdmin } while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2)) + { ChatHistory.RemoveAt(0); + } // the last client hasn't fully disconnected yet // so there will still be at least 1 client left if (ClientNum < 2) + { ChatHistory.Clear(); + } return true; } @@ -585,12 +446,12 @@ namespace IW4MAdmin /// array index 1 = disconnecting clients /// /// - async Task[]> PollPlayersAsync() + async Task[]> PollPlayersAsync() { #if DEBUG var now = DateTime.Now; #endif - var currentClients = GetPlayersAsList(); + var currentClients = GetClientsAsList(); var polledClients = (await this.GetStatusAsync()).AsEnumerable(); if (this.Manager.GetApplicationSettings().Configuration().IgnoreBots) { @@ -604,15 +465,15 @@ namespace IW4MAdmin foreach (var client in polledClients) { // todo: move out somehwere - var existingClient = Players[client.ClientNumber] ?? client; + var existingClient = Clients[client.ClientNumber] ?? client; existingClient.Ping = client.Ping; existingClient.Score = client.Score; } var disconnectingClients = currentClients.Except(polledClients); - var connectingClients = polledClients.Except(currentClients.Where(c => c.State == Player.ClientState.Connected)); + var connectingClients = polledClients.Except(currentClients.Where(c => c.State == EFClient.ClientState.Connected)); - return new List[] { connectingClients.ToList(), disconnectingClients.ToList() }; + return new List[] { connectingClients.ToList(), disconnectingClients.ToList() }; } DateTime start = DateTime.Now; @@ -626,11 +487,13 @@ namespace IW4MAdmin if (Manager.ShutdownRequested()) { // todo: fix up disconnect - //for (int i = 0; i < Players.Count; i++) - // await RemovePlayer(i); + //for (int i = 0; i < EFClients.Count; i++) + // await RemoveClient(i); foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins) + { await plugin.OnUnloadAsync(); + } } // only check every 2 minutes if the server doesn't seem to be responding @@ -644,7 +507,7 @@ namespace IW4MAdmin foreach (var disconnectingClient in polledClients[1]) { - if (disconnectingClient.State == Player.ClientState.Disconnecting) + if (disconnectingClient.State == EFClient.ClientState.Disconnecting) { continue; } @@ -669,8 +532,8 @@ namespace IW4MAdmin foreach (var client in polledClients[0]) { // this prevents duplicate events from being sent to the event api - if (GetPlayersAsList().Count(c => c.NetworkId == client.NetworkId && - c.State == Player.ClientState.Connected) != 0) + if (GetClientsAsList().Count(c => c.NetworkId == client.NetworkId && + c.State == EFClient.ClientState.Connected) != 0) { continue; } @@ -730,9 +593,12 @@ namespace IW4MAdmin // update the player history if ((lastCount - playerCountStart).TotalMinutes >= SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) { - while (PlayerHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours - PlayerHistory.Dequeue(); - PlayerHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum)); + while (ClientHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours + { + ClientHistory.Dequeue(); + } + + ClientHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum)); playerCountStart = DateTime.Now; } @@ -782,7 +648,9 @@ namespace IW4MAdmin new IW3RConParser(); if (ServerConfig.UseIW5MParser) + { RconParser = new IW5MRConParser(); + } var version = await this.GetDvarAsync("version"); GameName = Utilities.GetGame(version.Value); @@ -793,16 +661,26 @@ namespace IW4MAdmin RconParser = new IW4RConParser(); } else if (GameName == Game.IW5) + { EventParser = new IW5EventParser(); + } else if (GameName == Game.T5M) + { EventParser = new T5MEventParser(); + } else if (GameName == Game.T6M) + { EventParser = new T6MEventParser(); + } else + { EventParser = new IW3EventParser(); // this uses the 'main' folder for log paths + } if (GameName == Game.UKN) + { Logger.WriteWarning($"Game name not recognized: {version}"); + } var infoResponse = await this.GetInfoAsync(); // this is normally slow, but I'm only doing it because different games have different prefixes @@ -906,7 +784,7 @@ namespace IW4MAdmin #endif } - protected override async Task Warn(String Reason, Player Target, Player Origin) + protected override async Task Warn(String Reason, EFClient Target, EFClient Origin) { // ensure player gets warned if command not performed on them in game if (Target.ClientNumber < 0) @@ -948,7 +826,7 @@ namespace IW4MAdmin await Manager.GetPenaltyService().Create(newPenalty); } - protected override async Task Kick(String Reason, Player Target, Player Origin) + protected override async Task Kick(String Reason, EFClient Target, EFClient Origin) { // ensure player gets kicked if command not performed on them in game if (Target.ClientNumber < 0) @@ -971,7 +849,7 @@ namespace IW4MAdmin #endif #if DEBUG - await Target.CurrentServer.RemovePlayer(Target.ClientNumber); + await Target.CurrentServer.RemoveClient(Target.ClientNumber); #endif var newPenalty = new Penalty() @@ -988,7 +866,7 @@ namespace IW4MAdmin await Manager.GetPenaltyService().Create(newPenalty); } - protected override async Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin) + protected override async Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin) { // ensure player gets banned if command not performed on them in game if (Target.ClientNumber < 0) @@ -1009,7 +887,7 @@ namespace IW4MAdmin await Target.CurrentServer.ExecuteCommandAsync(formattedKick); } #else - await Target.CurrentServer.RemovePlayer(Target.ClientNumber); + await Target.CurrentServer.RemoveClient(Target.ClientNumber); #endif Penalty newPenalty = new Penalty() @@ -1027,15 +905,15 @@ namespace IW4MAdmin await Manager.GetPenaltyService().Create(newPenalty); } - override protected async Task Ban(String Message, Player Target, Player Origin) + override protected async Task Ban(String Message, EFClient Target, EFClient Origin) { // ensure player gets banned if command not performed on them in game if (Target.ClientNumber < 0) { - Player ingameClient = null; + EFClient ingameClient = null; ingameClient = Manager.GetServers() - .Select(s => s.GetPlayersAsList()) + .Select(s => s.GetClientsAsList()) .FirstOrDefault(l => l.FirstOrDefault(c => c.ClientId == Target.ClientId) != null) ?.First(c => c.ClientId == Target.ClientId); @@ -1049,13 +927,13 @@ namespace IW4MAdmin else { // this is set only because they're still in the server. - Target.Level = Player.Permission.Banned; + Target.Level = EFClient.Permission.Banned; #if !DEBUG string formattedString = String.Format(RconParser.GetCommandPrefixes().Kick, Target.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{Message} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7"); await Target.CurrentServer.ExecuteCommandAsync(formattedString); #else - await Target.CurrentServer.RemovePlayer(Target.ClientNumber); + await Target.CurrentServer.RemoveClient(Target.ClientNumber); #endif } @@ -1077,7 +955,7 @@ namespace IW4MAdmin Manager.GetPrivilegedClients().Remove(Target.ClientId); } - override public async Task Unban(string reason, Player Target, Player Origin) + override public async Task Unban(string reason, EFClient Target, EFClient Origin) { var unbanPenalty = new Penalty() { diff --git a/DiscordWebhook/DiscordWebhook.pyproj b/DiscordWebhook/DiscordWebhook.pyproj index c3ed35f4f..786fe607c 100644 --- a/DiscordWebhook/DiscordWebhook.pyproj +++ b/DiscordWebhook/DiscordWebhook.pyproj @@ -27,6 +27,11 @@ true false + + true + false + bin\Prerelease\ + @@ -50,7 +55,7 @@ True - + @@ -59,7 +64,7 @@ - + diff --git a/GameLogServer/GameLogServer.pyproj b/GameLogServer/GameLogServer.pyproj index 249fa7af5..d07c347c9 100644 --- a/GameLogServer/GameLogServer.pyproj +++ b/GameLogServer/GameLogServer.pyproj @@ -27,10 +27,16 @@ true false + + true + false + bin\Prerelease\ + Code + Code diff --git a/GameLogServer/GameLogServer/restart_resource.py b/GameLogServer/GameLogServer/restart_resource.py new file mode 100644 index 000000000..762b2d0d1 --- /dev/null +++ b/GameLogServer/GameLogServer/restart_resource.py @@ -0,0 +1,29 @@ +from flask_restful import Resource +from flask import request +import requests +import os +import subprocess +import re + +def get_pid_of_server_windows(port): + process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE) + output = process.communicate()[0] + matches = re.search(' *(UDP) +([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):'+ str(port) + ' +[^\w]*([0-9]+)', output.decode('utf-8')) + if matches is not None: + return matches.group(3) + else: + return 0 + +class RestartResource(Resource): + def get(self): + try: + response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved') + if response.status_code == 200: + pid = get_pid_of_server_windows(response.json()['port']) + subprocess.check_output("Taskkill /PID %s /F" % pid) + else: + return {}, 400 + except Exception as e: + print(e) + return {}, 500 + return {}, 200 diff --git a/GameLogServer/GameLogServer/server.py b/GameLogServer/GameLogServer/server.py index 3ec49269e..ba4f1d0fe 100644 --- a/GameLogServer/GameLogServer/server.py +++ b/GameLogServer/GameLogServer/server.py @@ -1,9 +1,11 @@ from flask import Flask from flask_restful import Api from .log_resource import LogResource +from .restart_resource import RestartResource app = Flask(__name__) def init(): api = Api(app) api.add_resource(LogResource, '/log/') + api.add_resource(RestartResource, '/restart') diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index dbb34eeb0..679c734e5 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -1,4 +1,3 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.16 @@ -41,6 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug ProjectSection(SolutionItems) = preProject Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js + Plugins\ScriptPlugins\VpnDetectionPrivate.js = Plugins\ScriptPlugins\VpnDetectionPrivate.js EndProjectSection EndProject Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "GameLogServer", "GameLogServer\GameLogServer.pyproj", "{42EFDA12-10D3-4C40-A210-9483520116BC}" diff --git a/Master/Master.pyproj b/Master/Master.pyproj index 271fede2d..aba301767 100644 --- a/Master/Master.pyproj +++ b/Master/Master.pyproj @@ -17,7 +17,7 @@ true Master Master - MSBuild|dev_env|$(MSBuildProjectFullPath) + MSBuild|env|X:\IW4MAdmin\GameLogServer\GameLogServer.pyproj False @@ -111,23 +111,15 @@ - + - - dev_env - 3.6 - dev_env (Python 3.6 (64-bit)) - Scripts\python.exe - Scripts\pythonw.exe - PYTHONPATH - X64 - +