From 36af673fc7babe1f40c105916e060b3da714930f Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sat, 4 Apr 2020 12:40:23 -0500 Subject: [PATCH] add ability to register custom event generators for event parsers / truncate long client names fix --- Application/ApplicationManager.cs | 22 ++++- Application/EventParsers/BaseEventParser.cs | 91 +++++++++--------- .../EventParsers/DynamicEventParser.cs | 2 +- Application/Main.cs | 12 +++ .../AutomessageFeed/AutomessageFeed.csproj | 2 +- .../IW4ScriptCommands.csproj | 2 +- Plugins/LiveRadar/LiveRadar.csproj | 2 +- Plugins/Login/Login.csproj | 2 +- .../ProfanityDeterment.csproj | 2 +- Plugins/Stats/Events/Script.cs | 94 +++++++++++++++++++ Plugins/Stats/Stats.csproj | 2 +- Plugins/Web/StatsWeb/StatsWeb.csproj | 2 +- SharedLibraryCore/Database/Models/EFAlias.cs | 7 +- SharedLibraryCore/Events/GameEvent.cs | 4 + SharedLibraryCore/Interfaces/IEventParser.cs | 13 ++- .../Interfaces/IEventParserConfiguration.cs | 3 +- .../Interfaces/IRegisterEvent.cs | 19 ++++ SharedLibraryCore/Services/ClientService.cs | 9 +- SharedLibraryCore/SharedLibraryCore.csproj | 8 +- SharedLibraryCore/Utilities.cs | 12 +++ .../ApplicationTests/BaseEventParserTests.cs | 55 +++++++++++ Tests/ApplicationTests/ServerTests.cs | 4 +- Tests/ApplicationTests/StatsTests.cs | 2 +- Tests/ApplicationTests/UtilitiesTests.cs | 32 +++++++ 24 files changed, 334 insertions(+), 69 deletions(-) create mode 100644 Plugins/Stats/Events/Script.cs create mode 100644 SharedLibraryCore/Interfaces/IRegisterEvent.cs create mode 100644 Tests/ApplicationTests/UtilitiesTests.cs diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index cfc816ecf..9ccd8dfc9 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -44,6 +44,7 @@ namespace IW4MAdmin.Application public bool IsRestartRequested { get; private set; } public IMiddlewareActionHandler MiddlewareActionHandler { get; } private readonly List _commands; + private readonly ILogger _logger; private readonly List MessageTokens; private readonly ClientService ClientSvc; readonly AliasService AliasSvc; @@ -60,11 +61,12 @@ namespace IW4MAdmin.Application private readonly IConfigurationHandler _commandConfiguration; private readonly IGameServerInstanceFactory _serverInstanceFactory; private readonly IParserRegexFactory _parserRegexFactory; + private readonly IEnumerable _customParserEvents; public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable commands, ITranslationLookup translationLookup, IConfigurationHandler commandConfiguration, - IConfigurationHandler appConfigHandler, IGameServerInstanceFactory serverInstanceFactory, - IEnumerable plugins, IParserRegexFactory parserRegexFactory) + IConfigurationHandler appConfigHandler, IGameServerInstanceFactory serverInstanceFactory, + IEnumerable plugins, IParserRegexFactory parserRegexFactory, IEnumerable customParserEvents) { MiddlewareActionHandler = actionHandler; _servers = new ConcurrentBag(); @@ -75,9 +77,10 @@ namespace IW4MAdmin.Application ConfigHandler = appConfigHandler; StartTime = DateTime.UtcNow; PageList = new PageList(); - AdditionalEventParsers = new List() { new BaseEventParser(parserRegexFactory) }; + AdditionalEventParsers = new List() { new BaseEventParser(parserRegexFactory, logger) }; AdditionalRConParsers = new List() { new BaseRConParser(parserRegexFactory) }; TokenAuthenticator = new TokenAuthentication(); + _logger = logger; _metaService = new MetaService(); _tokenSource = new CancellationTokenSource(); _loggers.Add(0, logger); @@ -86,6 +89,7 @@ namespace IW4MAdmin.Application _commandConfiguration = commandConfiguration; _serverInstanceFactory = serverInstanceFactory; _parserRegexFactory = parserRegexFactory; + _customParserEvents = customParserEvents; Plugins = plugins; } @@ -557,6 +561,16 @@ namespace IW4MAdmin.Application MetaService.AddRuntimeMeta(getPenaltyMeta); #endregion + #region CUSTOM_EVENTS + foreach (var customEvent in _customParserEvents.SelectMany(_events => _events.Events)) + { + foreach (var parser in AdditionalEventParsers) + { + parser.RegisterCustomEvent(customEvent.Item1, customEvent.Item2, customEvent.Item3); + } + } + #endregion + await InitializeServers(); } @@ -781,7 +795,7 @@ namespace IW4MAdmin.Application public IEventParser GenerateDynamicEventParser(string name) { - return new DynamicEventParser(_parserRegexFactory) + return new DynamicEventParser(_parserRegexFactory, _logger) { Name = name }; diff --git a/Application/EventParsers/BaseEventParser.cs b/Application/EventParsers/BaseEventParser.cs index ff89e95d6..7550a3113 100644 --- a/Application/EventParsers/BaseEventParser.cs +++ b/Application/EventParsers/BaseEventParser.cs @@ -2,6 +2,7 @@ using SharedLibraryCore.Database.Models; using SharedLibraryCore.Interfaces; using System; +using System.Collections.Generic; using System.Linq; using static SharedLibraryCore.Server; @@ -9,8 +10,14 @@ namespace IW4MAdmin.Application.EventParsers { public class BaseEventParser : IEventParser { - public BaseEventParser(IParserRegexFactory parserRegexFactory) + private readonly Dictionary)> _customEventRegistrations; + private readonly ILogger _logger; + + public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger) { + _customEventRegistrations = new Dictionary)>(); + _logger = logger; + Configuration = new DynamicEventParserConfiguration(parserRegexFactory) { GameDirectory = "main", @@ -272,51 +279,25 @@ namespace IW4MAdmin.Application.EventParsers }; } - // this is a custom event printed out by _customcallbacks.gsc (used for team balance) - if (eventType == "JoinTeam") + if (_customEventRegistrations.ContainsKey(eventType)) { - return new GameEvent() + var eventModifier = _customEventRegistrations[eventType]; + + try { - Type = GameEvent.EventType.JoinTeam, - Data = logLine, - Origin = new EFClient() { NetworkId = lineSplit[1].ConvertGuidToLong(Configuration.GuidNumberStyle) }, - RequiredEntity = GameEvent.EventRequiredEntity.Target, - GameTime = gameTime - }; - } + return eventModifier.Item2(logLine, Configuration, new GameEvent() + { + Type = GameEvent.EventType.Other, + Data = logLine, + Subtype = eventModifier.Item1, + GameTime = gameTime + }); + } - // this is a custom event printed out by _customcallbacks.gsc (used for anticheat) - if (eventType == "ScriptKill") - { - long originId = lineSplit[1].ConvertGuidToLong(Configuration.GuidNumberStyle, 1); - long targetId = lineSplit[2].ConvertGuidToLong(Configuration.GuidNumberStyle, 1); - - return new GameEvent() + catch (Exception e) { - Type = GameEvent.EventType.ScriptKill, - Data = logLine, - Origin = new EFClient() { NetworkId = originId }, - Target = new EFClient() { NetworkId = targetId }, - RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target, - GameTime = gameTime - }; - } - - // this is a custom event printed out by _customcallbacks.gsc (used for anticheat) - if (eventType == "ScriptDamage") - { - long originId = lineSplit[1].ConvertGuidToLong(Configuration.GuidNumberStyle, 1); - long targetId = lineSplit[2].ConvertGuidToLong(Configuration.GuidNumberStyle, 1); - - return new GameEvent() - { - Type = GameEvent.EventType.ScriptDamage, - Data = logLine, - Origin = new EFClient() { NetworkId = originId }, - Target = new EFClient() { NetworkId = targetId }, - RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target, - GameTime = gameTime - }; + _logger.WriteWarning($"Could not handle custom event generation - {e.GetExceptionInfo()}"); + } } return new GameEvent() @@ -329,5 +310,31 @@ namespace IW4MAdmin.Application.EventParsers GameTime = gameTime }; } + + /// + public void RegisterCustomEvent(string eventSubtype, string eventTriggerValue, Func eventModifier) + { + if (string.IsNullOrWhiteSpace(eventSubtype)) + { + throw new ArgumentException("Event subtype cannot be empty"); + } + + if (string.IsNullOrWhiteSpace(eventTriggerValue)) + { + throw new ArgumentException("Event trigger value cannot be empty"); + } + + if (eventModifier == null) + { + throw new ArgumentException("Event modifier must be specified"); + } + + if (_customEventRegistrations.ContainsKey(eventTriggerValue)) + { + throw new ArgumentException($"Event trigger value '{eventTriggerValue}' is already registered"); + } + + _customEventRegistrations.Add(eventTriggerValue, (eventSubtype, eventModifier)); + } } } diff --git a/Application/EventParsers/DynamicEventParser.cs b/Application/EventParsers/DynamicEventParser.cs index 8ea618d90..fff3ee561 100644 --- a/Application/EventParsers/DynamicEventParser.cs +++ b/Application/EventParsers/DynamicEventParser.cs @@ -8,7 +8,7 @@ namespace IW4MAdmin.Application.EventParsers /// sealed internal class DynamicEventParser : BaseEventParser { - public DynamicEventParser(IParserRegexFactory parserRegexFactory) : base(parserRegexFactory) + public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger) : base(parserRegexFactory, logger) { } } diff --git a/Application/Main.cs b/Application/Main.cs index 0ffb3848e..0082763b0 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -321,6 +321,18 @@ namespace IW4MAdmin.Application serviceCollection.AddSingleton(scriptPlugin); } + // register any eventable types + foreach (var assemblyType in typeof(Program).Assembly.GetTypes() + .Where(_asmType => typeof(IRegisterEvent).IsAssignableFrom(_asmType)) + .Union(pluginImplementations + .Item1.SelectMany(_asm => _asm.Assembly.GetTypes()) + .Distinct() + .Where(_asmType => typeof(IRegisterEvent).IsAssignableFrom(_asmType)))) + { + var instance = Activator.CreateInstance(assemblyType) as IRegisterEvent; + serviceCollection.AddSingleton(instance); + } + return serviceCollection; } } diff --git a/Plugins/AutomessageFeed/AutomessageFeed.csproj b/Plugins/AutomessageFeed/AutomessageFeed.csproj index bfc986656..9fd94e874 100644 --- a/Plugins/AutomessageFeed/AutomessageFeed.csproj +++ b/Plugins/AutomessageFeed/AutomessageFeed.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj index 90c95642e..837d4aa6a 100644 --- a/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj +++ b/Plugins/IW4ScriptCommands/IW4ScriptCommands.csproj @@ -10,7 +10,7 @@ - + diff --git a/Plugins/LiveRadar/LiveRadar.csproj b/Plugins/LiveRadar/LiveRadar.csproj index f1cf46f6d..359467ae9 100644 --- a/Plugins/LiveRadar/LiveRadar.csproj +++ b/Plugins/LiveRadar/LiveRadar.csproj @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Login/Login.csproj b/Plugins/Login/Login.csproj index 9cef02c85..2febd0edb 100644 --- a/Plugins/Login/Login.csproj +++ b/Plugins/Login/Login.csproj @@ -23,7 +23,7 @@ - + diff --git a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj index 33b45bc79..c56f82769 100644 --- a/Plugins/ProfanityDeterment/ProfanityDeterment.csproj +++ b/Plugins/ProfanityDeterment/ProfanityDeterment.csproj @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Stats/Events/Script.cs b/Plugins/Stats/Events/Script.cs new file mode 100644 index 000000000..8fa09d374 --- /dev/null +++ b/Plugins/Stats/Events/Script.cs @@ -0,0 +1,94 @@ +using SharedLibraryCore; +using SharedLibraryCore.Database.Models; +using SharedLibraryCore.Interfaces; +using System.Collections.Generic; +using EventGeneratorCallback = System.ValueTuple>; + +namespace IW4MAdmin.Plugins.Stats.Events +{ + public class Script : IRegisterEvent + { + private const string EVENT_SCRIPTKILL = "ScriptKill"; + private const string EVENT_SCRIPTDAMAGE = "ScriptDamage"; + private const string EVENT_JOINTEAM = "JoinTeam"; + + /// + /// this is a custom event printed out by _customcallbacks.gsc (used for anticheat) + /// + /// + private EventGeneratorCallback ScriptKill() + { + return (EVENT_SCRIPTKILL, EVENT_SCRIPTKILL, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) => + { + string[] lineSplit = eventLine.Split(";"); + long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1); + long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1); + + autoEvent.Type = GameEvent.EventType.ScriptKill; + autoEvent.Origin = new EFClient() { NetworkId = originId }; + autoEvent.Target = new EFClient() { NetworkId = targetId }; + autoEvent.RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target; + autoEvent.GameTime = autoEvent.GameTime; + + return autoEvent; + } + ); + } + + /// + /// this is a custom event printed out by _customcallbacks.gsc (used for anticheat) + /// + /// + private EventGeneratorCallback ScriptDamage() + { + // this is a custom event printed out by _customcallbacks.gsc (used for anticheat) + return (EVENT_SCRIPTDAMAGE, EVENT_SCRIPTDAMAGE, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) => + { + string[] lineSplit = eventLine.Split(";"); + long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1); + long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1); + + autoEvent.Type = GameEvent.EventType.ScriptDamage; + autoEvent.Origin = new EFClient() { NetworkId = originId }; + autoEvent.Target = new EFClient() { NetworkId = targetId }; + autoEvent.RequiredEntity = GameEvent.EventRequiredEntity.Origin | GameEvent.EventRequiredEntity.Target; + + return autoEvent; + } + ); + } + + /// + /// this is a custom event printed out by _customcallbacks.gsc (used for anticheat) + /// + /// + private EventGeneratorCallback JoinTeam() + { + // this is a custom event printed out by _customcallbacks.gsc (used for anticheat) + return (EVENT_JOINTEAM, EVENT_JOINTEAM, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) => + { + string[] lineSplit = eventLine.Split(";"); + long originId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle, 1); + long targetId = lineSplit[2].ConvertGuidToLong(config.GuidNumberStyle, 1); + + autoEvent.Type = GameEvent.EventType.JoinTeam; + autoEvent.Origin = new EFClient() { NetworkId = lineSplit[1].ConvertGuidToLong(config.GuidNumberStyle) }; + autoEvent.RequiredEntity = GameEvent.EventRequiredEntity.Target; + + return autoEvent; + } + ); + } + + public IEnumerable Events => + new[] + { + ScriptKill(), + ScriptDamage(), + JoinTeam() + }; + } +} diff --git a/Plugins/Stats/Stats.csproj b/Plugins/Stats/Stats.csproj index 4b4db7393..8a8923c8e 100644 --- a/Plugins/Stats/Stats.csproj +++ b/Plugins/Stats/Stats.csproj @@ -16,7 +16,7 @@ - + diff --git a/Plugins/Web/StatsWeb/StatsWeb.csproj b/Plugins/Web/StatsWeb/StatsWeb.csproj index 1a2e1a4a9..a0cc3d7cb 100644 --- a/Plugins/Web/StatsWeb/StatsWeb.csproj +++ b/Plugins/Web/StatsWeb/StatsWeb.csproj @@ -14,7 +14,7 @@ Always - + diff --git a/SharedLibraryCore/Database/Models/EFAlias.cs b/SharedLibraryCore/Database/Models/EFAlias.cs index f9be14c2d..0ccc94bd9 100644 --- a/SharedLibraryCore/Database/Models/EFAlias.cs +++ b/SharedLibraryCore/Database/Models/EFAlias.cs @@ -13,13 +13,16 @@ namespace SharedLibraryCore.Database.Models [ForeignKey("LinkId")] public virtual EFAliasLink Link { get; set; } [Required] - [MaxLength(24)] + [MaxLength(MAX_NAME_LENGTH)] public string Name { get; set; } - [MaxLength(24)] + [MaxLength(MAX_NAME_LENGTH)] public string SearchableName { get; set; } [Required] public int? IPAddress { get; set; } [Required] public DateTime DateAdded { get; set; } + + [NotMapped] + public const int MAX_NAME_LENGTH = 24; } } diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 24f53c070..3298d085a 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -214,6 +214,10 @@ namespace SharedLibraryCore } public EventType Type; + /// + /// suptype of the event for more detailed classification + /// + public string Subtype { get; set; } public EventRequiredEntity RequiredEntity { get; set; } public string Data; // Data is usually the message sent by player public string Message; diff --git a/SharedLibraryCore/Interfaces/IEventParser.cs b/SharedLibraryCore/Interfaces/IEventParser.cs index 733b4de1c..b0457920a 100644 --- a/SharedLibraryCore/Interfaces/IEventParser.cs +++ b/SharedLibraryCore/Interfaces/IEventParser.cs @@ -1,4 +1,5 @@ -using static SharedLibraryCore.Server; +using System; +using static SharedLibraryCore.Server; namespace SharedLibraryCore.Interfaces { @@ -33,8 +34,16 @@ namespace SharedLibraryCore.Interfaces string URLProtocolFormat { get; set; } /// - /// Specifies the text name of the game the parser is for + /// specifies the text name of the game the parser is for /// string Name { get; set; } + + /// + /// registers a custom event subtype to be triggered when a value is detected + /// + /// subtype assigned to the event when generated + /// event keyword to trigger an event generation + /// function pointer that modifies the generated game event + void RegisterCustomEvent(string eventSubtype, string eventTriggerValue, Func eventModifier); } } diff --git a/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs index 7d5736f6a..a6f5870de 100644 --- a/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IEventParserConfiguration.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System.Collections.Generic; +using System.Globalization; namespace SharedLibraryCore.Interfaces { diff --git a/SharedLibraryCore/Interfaces/IRegisterEvent.cs b/SharedLibraryCore/Interfaces/IRegisterEvent.cs new file mode 100644 index 000000000..c8dfc2e43 --- /dev/null +++ b/SharedLibraryCore/Interfaces/IRegisterEvent.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace SharedLibraryCore.Interfaces +{ + /// + /// interface defining the capabilities of a custom event registration + /// + public interface IRegisterEvent + { + /// + /// collection of custom event registrations + /// + /// (Subtype, trigger value, event generator) + /// + /// + IEnumerable<(string, string, Func)> Events { get; } + } +} diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index 5cc6e9b88..e58ab242b 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -18,6 +18,7 @@ namespace SharedLibraryCore.Services { int? linkId = null; int? aliasId = null; + entity.Name = entity.Name.CapClientName(EFAlias.MAX_NAME_LENGTH); if (entity.IPAddress != null) { @@ -102,19 +103,21 @@ namespace SharedLibraryCore.Services } } - private async Task UpdateAlias(string name, int? ip, EFClient entity, DatabaseContext context) + private async Task UpdateAlias(string originalName, int? ip, EFClient entity, DatabaseContext context) { + string name = originalName.CapClientName(EFAlias.MAX_NAME_LENGTH); + // entity is the tracked db context item // get all aliases by IP address and LinkId var iqAliases = context.Aliases .Include(a => a.Link) // we only want alias that have the same IP address or share a link .Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId)); - + var aliases = await iqAliases.ToListAsync(); var currentIPs = aliases.Where(_a2 => _a2.IPAddress != null).Select(_a2 => _a2.IPAddress).Distinct(); var floatingIPAliases = await context.Aliases.Where(_alias => currentIPs.Contains(_alias.IPAddress)).ToListAsync(); - aliases.AddRange(floatingIPAliases); + aliases.AddRange(floatingIPAliases); // see if they have a matching IP + Name but new NetworkId var existingExactAlias = aliases.OrderBy(_alias => _alias.LinkId).FirstOrDefault(a => a.Name == name && a.IPAddress == ip); diff --git a/SharedLibraryCore/SharedLibraryCore.csproj b/SharedLibraryCore/SharedLibraryCore.csproj index e8e8f9130..80a2fabca 100644 --- a/SharedLibraryCore/SharedLibraryCore.csproj +++ b/SharedLibraryCore/SharedLibraryCore.csproj @@ -6,7 +6,7 @@ RaidMax.IW4MAdmin.SharedLibraryCore - 2.2.7 + 2.2.8 RaidMax Forever None Debug;Release;Prerelease @@ -15,13 +15,13 @@ IW4MAdmin https://github.com/RaidMax/IW4M-Admin/ https://www.raidmax.org/IW4MAdmin/ - 2019 + 2020 true true MIT Shared Library for IW4MAdmin - 2.2.7.0 - 2.2.7.0 + 2.2.8.0 + 2.2.8.0 diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 06a6c6868..0bc918d09 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -94,6 +94,18 @@ namespace SharedLibraryCore return newStr; } + /// + /// caps client name to the specified character length - 3 + /// and adds ellipses to the end of the reamining client name + /// + /// client name + /// max number of characters for the name + /// + public static string CapClientName(this string str, int maxLength) => + str.Length > maxLength ? + $"{str.Substring(0, maxLength - 3)}..." : + str; + /// /// helper method to get the information about an exception and inner exceptions /// diff --git a/Tests/ApplicationTests/BaseEventParserTests.cs b/Tests/ApplicationTests/BaseEventParserTests.cs index efe64de43..fd8eb35bc 100644 --- a/Tests/ApplicationTests/BaseEventParserTests.cs +++ b/Tests/ApplicationTests/BaseEventParserTests.cs @@ -1,4 +1,5 @@ using ApplicationTests.Fixtures; +using FakeItEasy; using IW4MAdmin.Application.EventParsers; using IW4MAdmin.Application.Factories; using Microsoft.Extensions.DependencyInjection; @@ -7,6 +8,7 @@ using NUnit.Framework; using SharedLibraryCore; using SharedLibraryCore.Interfaces; using System; +using static SharedLibraryCore.GameEvent; namespace ApplicationTests { @@ -15,15 +17,19 @@ namespace ApplicationTests { private EventLogTest eventLogData; private IServiceProvider serviceProvider; + private ILogger fakeLogger; [SetUp] public void Setup() { eventLogData = JsonConvert.DeserializeObject(System.IO.File.ReadAllText("Files/GameEvents.json")); + + fakeLogger = A.Fake(); serviceProvider = new ServiceCollection() .AddSingleton() .AddTransient() .AddSingleton() + .AddSingleton(fakeLogger) .BuildServiceProvider(); } @@ -54,5 +60,54 @@ namespace ApplicationTests AssertMatch(parsedEvent, e); } } + + [Test] + public void TestCustomEvents() + { + var eventParser = serviceProvider.GetService(); + string eventMessage = "Hello this is my test event message"; + string triggerValue = "testTrigger"; + string eventType = "testType"; + + eventParser.RegisterCustomEvent(eventType, triggerValue, (logLine, config, generatedEvent) => + { + generatedEvent.Message = eventMessage; + return generatedEvent; + }); + + var customEvent = eventParser.GenerateGameEvent($"23:14 {triggerValue}"); + + Assert.AreEqual(EventType.Other, customEvent.Type); + Assert.AreEqual(eventType, customEvent.Subtype); + Assert.AreEqual(eventMessage, customEvent.Message); + } + + [Test] + public void TestCustomEventRegistrationArguments() + { + var eventParser = serviceProvider.GetService(); + + Assert.Throws(() => eventParser.RegisterCustomEvent(null, null, null)); + Assert.Throws(() => eventParser.RegisterCustomEvent("test", null, null)); + Assert.Throws(() => eventParser.RegisterCustomEvent("test", "test2", null)); + Assert.Throws(() => + { + // testing duplicate registers + eventParser.RegisterCustomEvent("test", "test", (a, b, c) => new GameEvent()); + eventParser.RegisterCustomEvent("test", "test", (a, b, c) => new GameEvent()); + }); + } + + [Test] + public void TestCustomEventParsingLogsWarningOnException() + { + var eventParser = serviceProvider.GetService(); + + eventParser.RegisterCustomEvent("test", "test", (a, b, c) => throw new Exception()); + eventParser.GenerateGameEvent("12:12 test"); + + A.CallTo(() => fakeLogger.WriteWarning(A.Ignored)) + .MustHaveHappenedOnceExactly(); + } } } diff --git a/Tests/ApplicationTests/ServerTests.cs b/Tests/ApplicationTests/ServerTests.cs index bd333cf07..e37437870 100644 --- a/Tests/ApplicationTests/ServerTests.cs +++ b/Tests/ApplicationTests/ServerTests.cs @@ -35,7 +35,7 @@ namespace ApplicationTests new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, A.Fake(), A.Fake()); - var parser = new BaseEventParser(A.Fake()); + var parser = new BaseEventParser(A.Fake(), A.Fake()); parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; var log = System.IO.File.ReadAllLines("Files\\T6MapRotation.log"); @@ -61,7 +61,7 @@ namespace ApplicationTests new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 }, A.Fake(), A.Fake()); - var parser = new BaseEventParser(A.Fake()); + var parser = new BaseEventParser(A.Fake(), A.Fake()); parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; var log = System.IO.File.ReadAllLines("Files\\T6Game.log"); diff --git a/Tests/ApplicationTests/StatsTests.cs b/Tests/ApplicationTests/StatsTests.cs index 32fdbe4d1..9e783ead8 100644 --- a/Tests/ApplicationTests/StatsTests.cs +++ b/Tests/ApplicationTests/StatsTests.cs @@ -56,7 +56,7 @@ namespace ApplicationTests A.Fake(), A.Fake()); - var parser = new BaseEventParser(A.Fake()); + var parser = new BaseEventParser(A.Fake(), A.Fake()); parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer; var log = System.IO.File.ReadAllLines("Files\\T6GameStats.log"); diff --git a/Tests/ApplicationTests/UtilitiesTests.cs b/Tests/ApplicationTests/UtilitiesTests.cs new file mode 100644 index 000000000..fbf3ced65 --- /dev/null +++ b/Tests/ApplicationTests/UtilitiesTests.cs @@ -0,0 +1,32 @@ +using NUnit.Framework; +using SharedLibraryCore; + +namespace ApplicationTests +{ + [TestFixture] + public class UtilitiesTests + { + [Test] + public void TestCapClientNameLengthReachesMax() + { + string originalName = "SomeVeryLongName"; + string expectedName = "SomeVeryLong..."; + int maxLength = originalName.Length - 1; + + string cappedName = originalName.CapClientName(maxLength); + + Assert.AreEqual(expectedName, cappedName); + } + + [Test] + public void TestCapClientNameRetainsOriginal() + { + string originalName = "Short"; + int maxLength = originalName.Length; + + string cappedName = originalName.CapClientName(maxLength); + + Assert.AreEqual(originalName, cappedName); + } + } +}