From ed83c4c01167790af40049fce7ca0125394b0cbb Mon Sep 17 00:00:00 2001 From: RaidMax Date: Mon, 5 Nov 2018 21:01:29 -0600 Subject: [PATCH] started work on getting the restart functionality in the gamelogserver fix bug with unbanned players still showing as banned via lock icon move player based stuff into client class finally renamed Player to EFClient via partial class don't try to run this build because it's in between stages --- .gitignore | 4 +- Application/Application.csproj | 6 +- Application/Core/ClientAuthentication.cs | 25 +- Application/EventParsers/IW4EventParser.cs | 49 +-- Application/EventParsers/IW5EventParser.cs | 7 +- Application/Main.cs | 2 +- Application/Manager.cs | 15 +- Application/RconParsers/IW4RConParser.cs | 11 +- Application/RconParsers/IW5MRConParser.cs | 11 +- Application/RconParsers/T6MRConParser.cs | 11 +- Application/Server.cs | 326 ++++++------------ DiscordWebhook/DiscordWebhook.pyproj | 9 +- GameLogServer/GameLogServer.pyproj | 6 + .../GameLogServer/restart_resource.py | 29 ++ GameLogServer/GameLogServer/server.py | 2 + IW4MAdmin.sln | 2 +- Master/Master.pyproj | 14 +- Master/requirements.txt | 14 +- Plugins/IW4ScriptCommands/Commands/Balance.cs | 9 +- .../IW4ScriptCommands.csproj | 1 + Plugins/Login/Commands/CLogin.cs | 3 +- Plugins/Login/Plugin.cs | 7 +- Plugins/ProfanityDeterment/Plugin.cs | 7 +- Plugins/ProfanityDeterment/Tracking.cs | 5 +- Plugins/Stats/Cheat/Detection.cs | 98 +++--- Plugins/Stats/Cheat/Strain.cs | 11 +- Plugins/Stats/Commands/MostPlayed.cs | 5 +- Plugins/Stats/Commands/ResetStats.cs | 3 +- Plugins/Stats/Commands/TopStats.cs | 5 +- Plugins/Stats/Commands/ViewStats.cs | 3 +- Plugins/Stats/Helpers/StatManager.cs | 80 +++-- Plugins/Tests/ClientTests.cs | 65 ++-- Plugins/Tests/ManagerTests.cs | 11 +- Plugins/Tests/PluginTests.cs | 5 +- Plugins/Welcome/Plugin.cs | 11 +- RunPublishPre.cmd | 12 +- RunPublishRelease.cmd | 12 +- SharedLibraryCore/Command.cs | 6 +- .../Commands/CommandProcessing.cs | 11 +- SharedLibraryCore/Commands/NativeCommands.cs | 228 ++++++------ SharedLibraryCore/Database/ContextSeed.cs | 3 +- SharedLibraryCore/Database/DatabaseContext.cs | 39 +-- SharedLibraryCore/Database/Models/EFClient.cs | 10 +- SharedLibraryCore/Dtos/ClientInfo.cs | 11 +- SharedLibraryCore/Events/GameEvent.cs | 13 +- .../Interfaces/IClientAuthentication.cs | 9 +- SharedLibraryCore/Interfaces/IManager.cs | 5 +- SharedLibraryCore/Interfaces/IRConParser.cs | 3 +- .../Objects/{Player.cs => EFClient.cs} | 182 ++++++++-- SharedLibraryCore/Objects/Permission.cs | 5 +- SharedLibraryCore/Objects/Report.cs | 9 +- SharedLibraryCore/RCon/Connection.cs | 3 - SharedLibraryCore/ScriptPlugin.cs | 5 +- SharedLibraryCore/Server.cs | 54 +-- SharedLibraryCore/Services/ClientService.cs | 9 +- SharedLibraryCore/Services/PenaltyService.cs | 13 +- SharedLibraryCore/Utilities.cs | 38 +- WebfrontCore/Controllers/API/APIController.cs | 30 +- WebfrontCore/Controllers/ActionController.cs | 2 +- WebfrontCore/Controllers/BaseController.cs | 6 +- WebfrontCore/Controllers/ClientController.cs | 5 +- WebfrontCore/Controllers/ConsoleController.cs | 3 +- WebfrontCore/Controllers/ServerController.cs | 4 +- .../ViewComponents/ServerListViewComponent.cs | 4 +- version.txt | 1 + 65 files changed, 864 insertions(+), 743 deletions(-) create mode 100644 GameLogServer/GameLogServer/restart_resource.py rename SharedLibraryCore/Objects/{Player.cs => EFClient.cs} (68%) 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 - +