diff --git a/Application/Application.csproj b/Application/Application.csproj index e9e42a771..364bd0e4d 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -32,7 +32,7 @@ true true - 2.2.7.6 + 2.2.7.7 2.2.7.7 7.1 diff --git a/Application/IO/GameLogEventDetection.cs b/Application/IO/GameLogEventDetection.cs index 35cb588f3..a23ce5fa1 100644 --- a/Application/IO/GameLogEventDetection.cs +++ b/Application/IO/GameLogEventDetection.cs @@ -30,7 +30,9 @@ namespace IW4MAdmin.Application.IO { while (!_server.Manager.CancellationToken.IsCancellationRequested) { +#if !DEBUG if (_server.IsInitialized) +#endif { try { diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 1d39ab9e1..ef43d3ccd 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -585,6 +585,13 @@ namespace IW4MAdmin try { +#if DEBUG + if (Manager.GetApplicationSettings().Configuration().RConPollRate == int.MaxValue) + { + return true; + } +#endif + var polledClients = await PollPlayersAsync(); var waiterList = new List(); @@ -766,7 +773,8 @@ namespace IW4MAdmin if (version?.Value?.Length != 0) { - RconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => _parser.Version == version.Value) ?? RconParser; + var matchedRconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => _parser.Version == version.Value); + RconParser.Configuration = matchedRconParser != null ? matchedRconParser.Configuration : RconParser.Configuration; EventParser = Manager.AdditionalEventParsers.FirstOrDefault(_parser => _parser.Version == version.Value) ?? EventParser; Version = RconParser.Version; } @@ -1011,11 +1019,9 @@ namespace IW4MAdmin #endif } - // this should link evading clients if (isEvade) { Logger.WriteInfo($"updating alias for banned client {targetClient}"); - await Manager.GetClientService().UpdateAlias(targetClient); } EFPenalty newPenalty = new EFPenalty() diff --git a/Application/Misc/Logger.cs b/Application/Misc/Logger.cs index 8b4cc9462..69e7dd94c 100644 --- a/Application/Misc/Logger.cs +++ b/Application/Misc/Logger.cs @@ -1,6 +1,7 @@ using SharedLibraryCore; using SharedLibraryCore.Interfaces; using System; +using System.Diagnostics; using System.IO; using System.Threading; @@ -73,10 +74,12 @@ namespace IW4MAdmin.Application // lets keep it simple and dispose of everything quickly as logging wont be that much (relatively) Console.WriteLine(LogLine); File.AppendAllText(FileName, LogLine + Environment.NewLine); + Debug.WriteLine(msg); #else if (type == LogType.Error || type == LogType.Verbose) + { Console.WriteLine(LogLine); - //if (type != LogType.Debug) + } File.AppendAllText(FileName, $"{LogLine}{Environment.NewLine}"); #endif } diff --git a/Application/RconParsers/BaseRConParser.cs b/Application/RconParsers/BaseRConParser.cs index 04a1abc9b..8a5ac2bb6 100644 --- a/Application/RconParsers/BaseRConParser.cs +++ b/Application/RconParsers/BaseRConParser.cs @@ -12,7 +12,11 @@ using static SharedLibraryCore.Server; namespace IW4MAdmin.Application.RconParsers { +#if DEBUG + public class BaseRConParser : IRConParser +#else class BaseRConParser : IRConParser +#endif { public BaseRConParser() { @@ -52,7 +56,7 @@ namespace IW4MAdmin.Application.RconParsers public IRConParserConfiguration Configuration { get; set; } - public string Version { get; set; } = "CoD"; + public virtual string Version { get; set; } = "CoD"; public Game GameName { get; set; } = Game.COD; public bool CanGenerateLogPath { get; set; } = true; @@ -89,7 +93,7 @@ namespace IW4MAdmin.Application.RconParsers }; } - public async Task> GetStatusAsync(Connection connection) + public virtual async Task> GetStatusAsync(Connection connection) { string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS); return ClientsFromStatus(response); diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index b96653921..84a779ef0 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -34,8 +34,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Plugins\Tests\Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IW4ScriptCommands", "Plugins\IW4ScriptCommands\IW4ScriptCommands.csproj", "{6C706CE5-A206-4E46-8712-F8C48D526091}" EndProject -Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "DiscordWebhook", "DiscordWebhook\DiscordWebhook.pyproj", "{15A81D6E-7502-46CE-8530-0647A380B5F4}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlugins", "{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA}" ProjectSection(SolutionItems) = preProject Plugins\ScriptPlugins\ParserCoD4x.js = Plugins\ScriptPlugins\ParserCoD4x.js @@ -309,18 +307,6 @@ Global {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x64.Build.0 = Release|Any CPU {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.ActiveCfg = Release|Any CPU {6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.Build.0 = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x64.ActiveCfg = Debug|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x86.ActiveCfg = Debug|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x64.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.ActiveCfg = Release|Any CPU - {15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x86.ActiveCfg = Release|Any CPU {42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs index 4d5817666..d7b9f2aff 100644 --- a/Plugins/Stats/Cheat/Detection.cs +++ b/Plugins/Stats/Cheat/Detection.cs @@ -133,6 +133,8 @@ namespace IW4MAdmin.Plugins.Stats.Cheat var weightedSessionAverage = HitLocationCount.Where(_hit => _hit.Value.Count > 0) .Sum(_hit => _hit.Value.Offset * _hit.Value.Count) / totalSessionHits; + AngleDifferenceAverage = weightedSessionAverage; + if (weightedSessionAverage > Thresholds.MaxOffset(totalSessionHits) && totalSessionHits > 40) { diff --git a/Plugins/Tests/ClientTests.cs b/Plugins/Tests/ClientTests.cs index efcea2b0e..597a3bcbb 100644 --- a/Plugins/Tests/ClientTests.cs +++ b/Plugins/Tests/ClientTests.cs @@ -2,11 +2,13 @@ using SharedLibraryCore; using SharedLibraryCore.Commands; using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Events; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; +using System.Threading.Tasks; using Xunit; namespace Tests @@ -14,12 +16,12 @@ namespace Tests [Collection("ManagerCollection")] public class ClientTests { - readonly ApplicationManager Manager; + private readonly ApplicationManager _manager; const int TestTimeout = 10000; public ClientTests(ManagerFixture fixture) { - Manager = fixture.Manager; + _manager = fixture.Manager; } [Fact] @@ -41,38 +43,122 @@ namespace Tests } [Fact] - public void WarnClientShouldSucceed() + public void BanEvasionShouldLink() { - while (!Manager.IsInitialized) + var server = _manager.Servers[0]; + var waiter = new ManualResetEventSlim(); + + _manager.GetApplicationSettings().Configuration().RConPollRate = 5000; + + + while (!server.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var e = new GameEvent() + { + Type = GameEvent.EventType.PreConnect, + Owner = server, + Origin = new EFClient() + { + NetworkId = 1337, + ClientNumber = 0, + CurrentAlias = new EFAlias() + { + Name = "Ban Me", + IPAddress = 1337 + } + } + }; - Assert.False(client == null, "no client found to warn"); + _manager.GetEventHandler().AddEvent(e); + e.OnProcessed.Wait(); - var warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer }); - warnEvent.OnProcessed.Wait(); + e = new GameEvent() + { + Type = GameEvent.EventType.PreConnect, + Owner = server, + Origin = new EFClient() + { + NetworkId = 1338, + ClientNumber = 1, + CurrentAlias = new EFAlias() + { + Name = "Ban Me", + IPAddress = null + } + } + }; - //Assert.True((client.Warnings == 1 || - // warnEvent.Failed) && - // Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1, - // "warning did not get applied"); + _manager.GetEventHandler().AddEvent(e); + e.OnProcessed.Wait(); + + e = new GameEvent() + { + Type = GameEvent.EventType.Update, + Owner = server, + Origin = new EFClient() + { + NetworkId = 1338, + ClientNumber = 1, + CurrentAlias = new EFAlias() + { + Name = "Ban Me", + IPAddress = 1337 + } + } + }; + + _manager.GetEventHandler().AddEvent(e); + e.OnProcessed.Wait(); + + } + + [Fact] + public void WarnClientShouldSucceed() + { + var onJoined = new ManualResetEventSlim(); + var server = _manager.Servers[0]; + + while (!server.IsInitialized) + { + Thread.Sleep(100); + } + + _manager.OnServerEvent += (sender, eventArgs) => + { + if (eventArgs.Event.Type == GameEvent.EventType.Connect) + { + onJoined.Set(); + } + }; + + server.EmulateClientJoinLog(); + onJoined.Wait(); + + var client = server.Clients[0]; + + var warnEvent = client.Warn("test warn", Utilities.IW4MAdminClient(server)); + warnEvent.OnProcessed.Wait(5000); + + Assert.False(warnEvent.Failed); warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer }); - warnEvent.OnProcessed.Wait(); + warnEvent.OnProcessed.Wait(5000); Assert.True(warnEvent.FailReason == GameEvent.EventFailReason.Permission && client.Warnings == 1, "warning was applied without proper permissions"); // warn clear var warnClearEvent = client.WarnClear(new EFClient { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer }); + warnClearEvent.OnProcessed.Wait(5000); Assert.True(warnClearEvent.FailReason == GameEvent.EventFailReason.Permission && client.Warnings == 1, "warning was removed without proper permissions"); - warnClearEvent = client.WarnClear(new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer }); + warnClearEvent = client.WarnClear(Utilities.IW4MAdminClient(server)); + warnClearEvent.OnProcessed.Wait(5000); Assert.True(!warnClearEvent.Failed && client.Warnings == 0, "warning was not cleared"); } @@ -80,12 +166,12 @@ namespace Tests [Fact] public void ReportClientShouldSucceed() { - while (!Manager.IsInitialized) + while (!_manager.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault(); Assert.False(client == null, "no client found to report"); // fail @@ -127,12 +213,12 @@ namespace Tests [Fact] public void FlagClientShouldSucceed() { - while (!Manager.IsInitialized) + while (!_manager.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault(); Assert.False(client == null, "no client found to flag"); var flagEvent = client.Flag("test flag", new EFClient { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer }); @@ -177,12 +263,12 @@ namespace Tests [Fact] void KickClientShouldSucceed() { - while (!Manager.IsInitialized) + while (!_manager.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault(); Assert.False(client == null, "no client found to kick"); var kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer }); @@ -193,18 +279,18 @@ namespace Tests kickEvent = client.Kick("test kick", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer }); kickEvent.OnProcessed.Wait(); - Assert.True(Manager.Servers.First().GetClientsAsList().FirstOrDefault(c => c.NetworkId == client.NetworkId) == null, "client was not kicked"); + Assert.True(_manager.Servers.First().GetClientsAsList().FirstOrDefault(c => c.NetworkId == client.NetworkId) == null, "client was not kicked"); } [Fact] void TempBanClientShouldSucceed() { - while (!Manager.IsInitialized) + while (!_manager.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault(); Assert.False(client == null, "no client found to tempban"); var tbCommand = new CTempBan(); @@ -217,19 +303,19 @@ namespace Tests Owner = client.CurrentServer }).Wait(); - Assert.True(Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.TempBan) == 1, + Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.TempBan) == 1, "tempban was not added"); } [Fact] void BanUnbanClientShouldSucceed() { - while (!Manager.IsInitialized) + while (!_manager.IsInitialized) { Thread.Sleep(100); } - var client = Manager.Servers.First().GetClientsAsList().FirstOrDefault(); + var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault(); Assert.False(client == null, "no client found to ban"); var banCommand = new CBan(); @@ -242,7 +328,7 @@ namespace Tests Owner = client.CurrentServer }).Wait(); - Assert.True(Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 1, + Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 1, "ban was not added"); var unbanCommand = new CUnban(); @@ -255,7 +341,7 @@ namespace Tests Owner = client.CurrentServer }).Wait(); - Assert.True(Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 0, + Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 0, "ban was not removed"); } diff --git a/Plugins/Tests/ManagerFixture.cs b/Plugins/Tests/ManagerFixture.cs index 69ab998d2..824e46df2 100644 --- a/Plugins/Tests/ManagerFixture.cs +++ b/Plugins/Tests/ManagerFixture.cs @@ -16,9 +16,9 @@ namespace Tests public ManagerFixture() { - File.WriteAllText("test_mp.log", "test_log_file"); + string logFile = @"X:\IW4MAdmin\Plugins\Tests\bin\Debug\netcoreapp2.2\test_mp.log"; - //IW4MAdmin.Application.Localization.Configure.Initialize("en-US"); + File.WriteAllText(logFile, Environment.NewLine); Manager = ApplicationManager.GetInstance(); @@ -31,18 +31,22 @@ namespace Tests AutoMessages = new List(), IPAddress = "127.0.0.1", Password = "test", - Port = 28963, + Port = 28960, Rules = new List(), - ManualLogPath = "http://google.com" + RConParserVersion = "test", + EventParserVersion = "IW4x (v0.6.0)", + ManualLogPath = logFile } }, AutoMessages = new List(), GlobalRules = new List(), Maps = new List(), - RConPollRate = 10000 + RConPollRate = int.MaxValue }; + Manager.ConfigHandler = new BaseConfigurationHandler("test"); Manager.ConfigHandler.Set(config); + Manager.AdditionalRConParsers.Add(new TestRconParser()); Manager.Init().Wait(); diff --git a/Plugins/Tests/ManagerTests.cs b/Plugins/Tests/ManagerTests.cs index 8f6372bae..807f38602 100644 --- a/Plugins/Tests/ManagerTests.cs +++ b/Plugins/Tests/ManagerTests.cs @@ -1,7 +1,6 @@ using IW4MAdmin.Application; using SharedLibraryCore; using SharedLibraryCore.Database.Models; -using SharedLibraryCore.Interfaces; using System; using System.Collections.Generic; using System.Diagnostics; @@ -33,162 +32,17 @@ namespace Tests [Fact] public void AreCommandAliasesUnique() { - var mgr = Program.ServerManager; + var mgr = Manager; bool test = mgr.GetCommands().Count == mgr.GetCommands().Select(c => c.Alias).Distinct().Count(); + foreach (var duplicate in mgr.GetCommands().GroupBy(_cmd => _cmd.Alias).Where(_grp => _grp.Count() > 1).Select(_grp => new { Command = _grp.First().Name, Alias = _grp.Key })) + { + Debug.WriteLine($"{duplicate.Command}: {duplicate.Alias}"); + } + Assert.True(test, "command aliases are not unique"); } - [Fact] - public void AddAndRemoveClientsViaJoinShouldSucceed() - { - var server = Manager.GetServers().First(); - var waiters = new Queue(); - - int clientStartIndex = 4; - int clientNum = 10; - - for (int i = clientStartIndex; i < clientStartIndex + clientNum; i++) - { - var e = new GameEvent() - { - Type = GameEvent.EventType.Join, - Origin = new EFClient() - { - Name = $"Player{i}", - NetworkId = i, - ClientNumber = i - 1 - }, - Owner = server - }; - - server.Manager.GetEventHandler().AddEvent(e); - waiters.Enqueue(e); - } - - while (waiters.Count > 0) - { - waiters.Dequeue().OnProcessed.Wait(); - } - - Assert.True(server.ClientNum == clientNum, $"client num does not match added client num [{server.ClientNum}:{clientNum}]"); - - for (int i = clientStartIndex; i < clientStartIndex + clientNum; i++) - { - var e = new GameEvent() - { - Type = GameEvent.EventType.Disconnect, - Origin = new EFClient() - { - Name = $"Player{i}", - NetworkId = i, - ClientNumber = i - 1 - }, - Owner = server - }; - - server.Manager.GetEventHandler().AddEvent(e); - waiters.Enqueue(e); - } - - while (waiters.Count > 0) - { - waiters.Dequeue().OnProcessed.Wait(); - } - - Assert.True(server.ClientNum == 0, "there are still clients connected"); - } - - [Fact] - public void AddAndRemoveClientsViaRconShouldSucceed() - { - var server = Manager.GetServers().First(); - var waiters = new Queue(); - - int clientIndexStart = 1; - int clientNum = 8; - - for (int i = clientIndexStart; i < clientNum + clientIndexStart; i++) - { - var e = new GameEvent() - { - Type = GameEvent.EventType.Connect, - Origin = new EFClient() - { - Name = $"Player{i}", - NetworkId = i, - ClientNumber = i - 1, - IPAddress = i, - Ping = 50, - CurrentServer = server - }, - Owner = server, - }; - - Manager.GetEventHandler().AddEvent(e); - waiters.Enqueue(e); - } - - while (waiters.Count > 0) - { - waiters.Dequeue().OnProcessed.Wait(); - } - - int actualClientNum = server.GetClientsAsList().Count(p => p.State == EFClient.ClientState.Connected); - Assert.True(actualClientNum == clientNum, $"client connected states don't match [{actualClientNum}:{clientNum}"); - - for (int i = clientIndexStart; i < clientNum + clientIndexStart; i++) - { - var e = new GameEvent() - { - Type = GameEvent.EventType.Disconnect, - Origin = new EFClient() - { - Name = $"Player{i}", - NetworkId = i, - ClientNumber = i - 1, - IPAddress = i, - Ping = 50, - CurrentServer = server - }, - Owner = server, - }; - - Manager.GetEventHandler().AddEvent(e); - waiters.Enqueue(e); - } - - while (waiters.Count > 0) - { - waiters.Dequeue().OnProcessed.Wait(); - } - - actualClientNum = server.ClientNum; - Assert.True(actualClientNum == 0, "there are clients still connected"); - } - - - [Fact] - public void AddClientViaLog() - { - var resetEvent = new ManualResetEventSlim(); - resetEvent.Reset(); - - Manager.OnServerEvent += (sender, eventArgs) => - { - if (eventArgs.Event.Type == GameEvent.EventType.Join) - { - eventArgs.Event.OnProcessed.Wait(); - Assert.True(false); - } - }; - - File.AppendAllText("test_mp.log", " 2:33 J;224b3d0bc64ab4f9;0;goober"); - - - resetEvent.Wait(5000); - } - [Fact] public void PrintCommands() { diff --git a/Plugins/Tests/ServerTests.cs b/Plugins/Tests/ServerTests.cs index c62138f90..69c65932c 100644 --- a/Plugins/Tests/ServerTests.cs +++ b/Plugins/Tests/ServerTests.cs @@ -1,10 +1,113 @@ -using System; +using IW4MAdmin.Application; +using SharedLibraryCore; +using System; using System.Collections.Generic; +using System.IO; using System.Text; +using System.Threading; +using Xunit; namespace Tests { - class ServerTests + [Collection("ManagerCollection")] + public class ServerTests { + private readonly ApplicationManager _manager; + + public ServerTests(ManagerFixture fixture) + { + _manager = fixture.Manager; + } + + [Fact] + public void AddAndRemoveClientViaLog() + { + var resetEvent = new ManualResetEventSlim(); + var server = _manager.Servers[0]; + + var currentClientCount = server.ClientNum; + int eventsProcessed = 0; + + _manager.OnServerEvent += (sender, eventArgs) => + { + if (eventArgs.Event.Type == GameEvent.EventType.Connect) + { + eventArgs.Event.OnProcessed.Wait(); + Assert.False(eventArgs.Event.Failed, "connect event was not processed"); + Assert.True(server.ClientNum == currentClientCount + 1, "client count was not incremented"); + eventsProcessed++; + resetEvent.Set(); + } + + if (eventArgs.Event.Type == GameEvent.EventType.Disconnect) + { + eventArgs.Event.OnProcessed.Wait(); + Assert.False(eventArgs.Event.Failed, "disconnect event was not processed"); + Assert.True(server.ClientNum == currentClientCount, "client count was not decremented"); + eventsProcessed++; + resetEvent.Set(); + } + }; + + server.EmulateClientJoinLog(); + + resetEvent.Wait(15000); + resetEvent.Reset(); + + Assert.Equal(1, eventsProcessed); + + server.EmulateClientQuitLog(); + + resetEvent.Wait(15000); + + Assert.Equal(2, eventsProcessed); + } + + [Fact] + public void AddAndRemoveClientViaRcon() + { + var resetEvent = new ManualResetEventSlim(); + var server = _manager.Servers[0]; + + var currentClientCount = server.ClientNum; + int eventsProcessed = 0; + + _manager.GetApplicationSettings().Configuration().RConPollRate = 5000; + _manager.OnServerEvent += (sender, eventArgs) => + { + if (eventArgs.Event.Type == GameEvent.EventType.Connect) + { + eventArgs.Event.OnProcessed.Wait(); + Assert.False(eventArgs.Event.Failed, "connect event was not processed"); + Assert.True(server.ClientNum == currentClientCount + 1, "client count was not incremented"); + eventsProcessed++; + resetEvent.Set(); + } + + if (eventArgs.Event.Type == GameEvent.EventType.Disconnect) + { + eventArgs.Event.OnProcessed.Wait(); + Assert.False(eventArgs.Event.Failed, "disconnect event was not processed"); + Assert.True(server.ClientNum == currentClientCount, "client count was not decremented"); + eventsProcessed++; + resetEvent.Set(); + } + }; + + (server.RconParser as TestRconParser).FakeClientCount = 1; + + resetEvent.Wait(15000); + resetEvent.Reset(); + + Assert.Equal(1, eventsProcessed); + + (server.RconParser as TestRconParser).FakeClientCount = 0; + + resetEvent.Wait(15000); + + Assert.Equal(2, eventsProcessed); + + _manager.GetApplicationSettings().Configuration().RConPollRate = int.MaxValue; + } } } diff --git a/Plugins/Tests/TestHelpers.cs b/Plugins/Tests/TestHelpers.cs new file mode 100644 index 000000000..30d13ed7f --- /dev/null +++ b/Plugins/Tests/TestHelpers.cs @@ -0,0 +1,24 @@ +using IW4MAdmin.Application; +using SharedLibraryCore; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Tests +{ + internal static class TestHelpers + { + internal static void EmulateClientJoinLog(this Server svr) + { + long guid = svr.ClientNum + 1; + File.AppendAllText(svr.LogPath, $"0:00 J;{guid};{svr.ClientNum};test_client_{svr.ClientNum}\r\n"); + } + + internal static void EmulateClientQuitLog(this Server svr) + { + long guid = Math.Max(1, svr.ClientNum); + File.AppendAllText(svr.LogPath, $"0:00 Q;{guid};{svr.ClientNum};test_client_{svr.ClientNum}\r\n"); + } + } +} diff --git a/Plugins/Tests/TestRconParser.cs b/Plugins/Tests/TestRconParser.cs new file mode 100644 index 000000000..b1c6d8aba --- /dev/null +++ b/Plugins/Tests/TestRconParser.cs @@ -0,0 +1,38 @@ +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.RCon; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Tests +{ + class TestRconParser : IW4MAdmin.Application.RconParsers.BaseRConParser + { + public int FakeClientCount { get; set; } + public List FakeClients { get; set; } = new List(); + + public override string Version => "test"; + + public override async Task> GetStatusAsync(Connection connection) + { + var clientList = new List(); + + for (int i = 0; i < FakeClientCount; i++) + { + clientList.Add(new EFClient() + { + ClientNumber = i, + NetworkId = i + 1, + CurrentAlias = new EFAlias() + { + Name = $"test_bot_{i}", + IPAddress = i + 1 + } + }); + } + + return clientList.Count > 0 ? clientList : FakeClients; + } + } +} diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 98f205dc8..8538d5e02 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -31,7 +31,7 @@ namespace SharedLibraryCore.Commands public class CRestart : Command { public CRestart() : - base("restart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RESTART_DESC"], "rs", EFClient.Permission.Owner, false) + base("restart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RESTART_DESC"], "res", EFClient.Permission.Owner, false) { } public override Task ExecuteAsync(GameEvent E) diff --git a/SharedLibraryCore/PartialEntities/EFClient.cs b/SharedLibraryCore/PartialEntities/EFClient.cs index 19897d413..8ea67af22 100644 --- a/SharedLibraryCore/PartialEntities/EFClient.cs +++ b/SharedLibraryCore/PartialEntities/EFClient.cs @@ -205,12 +205,7 @@ namespace SharedLibraryCore.Database.Models int reportCount = sender.GetAdditionalProperty("_reportCount"); - if (Level > sender.Level) - { - e.FailReason = GameEvent.EventFailReason.Permission; - } - - else if (Equals(sender)) + if (Equals(sender)) { e.FailReason = GameEvent.EventFailReason.Invalid; } diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index e3f31500e..2a739bba4 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -109,6 +109,20 @@ namespace SharedLibraryCore.Services var aliasSql = iqAliases.ToSql(); #endif var aliases = await iqAliases.ToListAsync(); + + // see if they have a matching IP + Name but new NetworkId + var existingExactAlias = aliases.FirstOrDefault(a => a.Name == name && a.IPAddress == ip); + bool hasExactAliasMatch = existingExactAlias != null; + + // if existing alias matches link them + var newAliasLink = existingExactAlias?.Link; + // if no exact matches find the first IP or LinkId that matches + newAliasLink = newAliasLink ?? aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault()?.Link; + // if no matches are found, use our current one ( it will become permanent ) + newAliasLink = newAliasLink ?? entity.AliasLink; + + bool hasExistingAlias = aliases.Count > 0; + bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId; // update each of the aliases where this is no IP but the name is identical foreach (var alias in aliases.Where(_alias => (_alias.IPAddress == null || _alias.IPAddress == 0))) @@ -118,20 +132,6 @@ namespace SharedLibraryCore.Services await context.SaveChangesAsync(); - // see if they have a matching IP + Name but new NetworkId - var existingExactAlias = aliases.FirstOrDefault(a => a.Name == name && a.IPAddress == ip); - bool hasExactAliasMatch = existingExactAlias != null; - - // if existing alias matches link them - var newAliasLink = existingExactAlias?.Link; - // if no exact matches find the first IP or LinkId that matches - newAliasLink = newAliasLink ?? aliases.FirstOrDefault()?.Link; - // if no matches are found, use our current one ( it will become permanent ) - newAliasLink = newAliasLink ?? entity.AliasLink; - - bool hasExistingAlias = aliases.Count > 0; - bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId; - // this happens when the link we found is different than the one we create before adding an IP if (isAliasLinkUpdated) {