diff --git a/Application/Commands/FindPlayerCommand.cs b/Application/Commands/FindPlayerCommand.cs new file mode 100644 index 000000000..22f5b21ae --- /dev/null +++ b/Application/Commands/FindPlayerCommand.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Finds player by name + /// + public class FindPlayerCommand : Command + { + public FindPlayerCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "find"; + Description = _translationLookup["COMMANDS_FIND_DESC"]; + Alias = "f"; + Permission = EFClient.Permission.Administrator; + RequiresTarget = false; + Arguments = new[] + { + new CommandArgument() + { + Name = _translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + } + }; + } + + public override async Task ExecuteAsync(GameEvent gameEvent) + { + if (gameEvent.Data.Length < 3) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_FIND_MIN"]); + return; + } + + var players = await gameEvent.Owner.Manager.GetClientService().FindClientsByIdentifier(gameEvent.Data); + + if (!players.Any()) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_FIND_EMPTY"]); + return; + } + + foreach (var client in players) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_FIND_FORMAT_V2"].FormatExt(client.Name, + client.ClientId, Utilities.ConvertLevelToColor((EFClient.Permission) client.LevelInt, client.Level), + client.IPAddress, (DateTime.UtcNow - client.LastConnection).HumanizeForCurrentCulture())); + } + } + } +} \ No newline at end of file diff --git a/Application/Commands/ListAdminsCommand.cs b/Application/Commands/ListAdminsCommand.cs new file mode 100644 index 000000000..188ffb5c1 --- /dev/null +++ b/Application/Commands/ListAdminsCommand.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Lists all unmasked admins + /// + public class ListAdminsCommand : Command + { + public ListAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "admins"; + Description = _translationLookup["COMMANDS_ADMINS_DESC"]; + Alias = "a"; + Permission = EFClient.Permission.User; + RequiresTarget = false; + } + + public static string OnlineAdmins(Server server, ITranslationLookup lookup) + { + var onlineAdmins = server.GetClientsAsList() + .Where(p => p.Level > EFClient.Permission.Flagged) + .Where(p => !p.Masked) + .Select(p => + $"[(Color::Yellow){Utilities.ConvertLevelToColor(p.Level, p.ClientPermission.Name)}(Color::White)] {p.Name}") + .ToList(); + + return onlineAdmins.Any() ? string.Join(Environment.NewLine, onlineAdmins) : lookup["COMMANDS_ADMINS_NONE"]; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + foreach (var line in OnlineAdmins(gameEvent.Owner, _translationLookup).Split(Environment.NewLine)) + { + var _ = gameEvent.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) + ? gameEvent.Owner.Broadcast(line) + : gameEvent.Origin.Tell(line); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/ListAliasesCommand.cs b/Application/Commands/ListAliasesCommand.cs new file mode 100644 index 000000000..1056ac6b1 --- /dev/null +++ b/Application/Commands/ListAliasesCommand.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Lists alises of specified client + /// + public class ListAliasesCommand : Command + { + public ListAliasesCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "alias"; + Description = _translationLookup["COMMANDS_ALIAS_DESC"]; + Alias = "known"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = true; + Arguments = new[] + { + new CommandArgument() + { + Name = _translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true, + } + }; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + var message = new StringBuilder(); + var names = new List(gameEvent.Target.AliasLink.Children.Select(a => a.Name)); + var ips = new List(gameEvent.Target.AliasLink.Children.Select(a => a.IPAddress.ConvertIPtoString()) + .Distinct()); + + gameEvent.Origin.Tell($"[(Color::Accent){gameEvent.Target}(Color::White)]"); + + message.Append($"{_translationLookup["COMMANDS_ALIAS_ALIASES"]}: "); + message.Append(string.Join(" | ", names)); + gameEvent.Origin.Tell(message.ToString()); + + message.Clear(); + message.Append($"{_translationLookup["COMMANDS_ALIAS_IPS"]}: "); + message.Append(string.Join(" | ", ips)); + gameEvent.Origin.Tell(message.ToString()); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/ListClientsCommand.cs b/Application/Commands/ListClientsCommand.cs new file mode 100644 index 000000000..271e05019 --- /dev/null +++ b/Application/Commands/ListClientsCommand.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// List online clients + /// + public class ListClientsCommand : Command + { + public ListClientsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "list"; + Description = _translationLookup["COMMANDS_LIST_DESC"]; + Alias = "l"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = false; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + var clientList = gameEvent.Owner.GetClientsAsList() + .Select(client => + $"[(Color::Accent){client.ClientPermission.Name}(Color::White){(string.IsNullOrEmpty(client.Tag) ? "" : $" {client.Tag}")}(Color::White)][(Color::Yellow)#{client.ClientNumber}(Color::White)] {client.Name}") + .ToArray(); + + gameEvent.Origin.Tell(clientList); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/ListPluginsCommand.cs b/Application/Commands/ListPluginsCommand.cs new file mode 100644 index 000000000..4cc0566b4 --- /dev/null +++ b/Application/Commands/ListPluginsCommand.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Lists the loaded plugins + /// + public class ListPluginsCommand : Command + { + private readonly IEnumerable _plugins; + + public ListPluginsCommand(CommandConfiguration config, ITranslationLookup translationLookup, + IEnumerable plugins) : base(config, translationLookup) + { + Name = "plugins"; + Description = _translationLookup["COMMANDS_PLUGINS_DESC"]; + Alias = "p"; + Permission = EFClient.Permission.Administrator; + RequiresTarget = false; + _plugins = plugins; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_PLUGINS_LOADED"]); + foreach (var plugin in _plugins.Where(plugin => !plugin.IsParser)) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_LIST_PLUGINS_FORMAT"] + .FormatExt(plugin.Name, plugin.Version, plugin.Author)); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/ListReportsCommand.cs b/Application/Commands/ListReportsCommand.cs new file mode 100644 index 000000000..3c0f68563 --- /dev/null +++ b/Application/Commands/ListReportsCommand.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Helpers; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// List all reports on the server + /// + public class ListReportsCommand : Command + { + public ListReportsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "reports"; + Description = _translationLookup["COMMANDS_REPORTS_DESC"]; + Alias = "reps"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = false; + Arguments = new[] + { + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_CLEAR"], + Required = false + } + }; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + if (gameEvent.Data != null && gameEvent.Data.ToLower().Contains(_translationLookup["COMMANDS_ARGS_CLEAR"])) + { + gameEvent.Owner.Reports = new List(); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_REPORTS_CLEAR_SUCCESS"]); + return Task.CompletedTask; + } + + if (gameEvent.Owner.Reports.Count < 1) + { + gameEvent.Origin.Tell(_translationLookup["COMMANDS_REPORTS_NONE"]); + return Task.CompletedTask; + } + + foreach (var report in gameEvent.Owner.Reports) + { + gameEvent.Origin.Tell( + $"(Color::Accent){report.Origin.Name}(Color::White) -> (Color::Red){report.Target.Name}(Color::White): {report.Reason}"); + } + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/PrivateMessageCommand.cs b/Application/Commands/PrivateMessageCommand.cs new file mode 100644 index 000000000..b2a8b4311 --- /dev/null +++ b/Application/Commands/PrivateMessageCommand.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Sends a private message to another player + /// + public class PrivateMessageCommand : Command + { + public PrivateMessageCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) + { + Name = "privatemessage"; + Description = _translationLookup["COMMANDS_PM_DESC"]; + Alias = "pm"; + Permission = EFClient.Permission.User; + RequiresTarget = true; + Arguments = new[] + { + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + }, + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], + Required = true + } + }; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + gameEvent.Target.Tell(_translationLookup["COMMANDS_PRIVATE_MESSAGE_FORMAT"].FormatExt(gameEvent.Origin.Name, gameEvent.Data)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_PRIVATE_MESSAGE_RESULT"] + .FormatExt(gameEvent.Target.Name, gameEvent.Data)); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/ReportClientCommand.cs b/Application/Commands/ReportClientCommand.cs new file mode 100644 index 000000000..cb20288fc --- /dev/null +++ b/Application/Commands/ReportClientCommand.cs @@ -0,0 +1,77 @@ +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Report client for given reason + /// + public class ReportClientCommand : Command + { + public ReportClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "report"; + Description = _translationLookup["COMMANDS_REPORT_DESC"]; + Alias = "rep"; + Permission = EFClient.Permission.User; + RequiresTarget = true; + Arguments = new[] + { + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_PLAYER"], + Required = true + }, + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_REASON"], + Required = true + } + }; + } + + public override async Task ExecuteAsync(GameEvent commandEvent) + { + if (commandEvent.Data.ToLower().Contains("camp")) + { + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_CAMP"]); + return; + } + + var success = false; + + switch ((await commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin) + .WaitAsync(Utilities.DefaultCommandTimeout, commandEvent.Owner.Manager.CancellationToken)).FailReason) + { + case GameEvent.EventFailReason.None: + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_SUCCESS"]); + success = true; + break; + case GameEvent.EventFailReason.Exception: + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_DUPLICATE"]); + break; + case GameEvent.EventFailReason.Permission: + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL"] + .FormatExt(commandEvent.Target.Name)); + break; + case GameEvent.EventFailReason.Invalid: + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_SELF"]); + break; + case GameEvent.EventFailReason.Throttle: + commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_TOOMANY"]); + break; + } + + if (success) + { + commandEvent.Owner.ToAdmins( + $"(Color::Accent){commandEvent.Origin.Name}(Color::White) -> (Color::Red){commandEvent.Target.Name}(Color::White): {commandEvent.Data}"); + } + } + } +} \ No newline at end of file diff --git a/Application/Commands/SayAllCommand.cs b/Application/Commands/SayAllCommand.cs new file mode 100644 index 000000000..c819889f2 --- /dev/null +++ b/Application/Commands/SayAllCommand.cs @@ -0,0 +1,46 @@ +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Prints out a message to all clients on all servers + /// + public class SayAllCommand : Command + { + public SayAllCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "sayall"; + Description = _translationLookup["COMMANDS_SAY_ALL_DESC"]; + Alias = "sa"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = false; + Arguments = new[] + { + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], + Required = true + } + }; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + var message = $"(Color::Accent){gameEvent.Origin.Name}(Color::White) - (Color::Red){gameEvent.Data}"; + + foreach (var server in gameEvent.Owner.Manager.GetServers()) + { + server.Broadcast(message, gameEvent.Origin); + } + + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SAY_SUCCESS"]); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/SayCommand.cs b/Application/Commands/SayCommand.cs new file mode 100644 index 000000000..a23be59cc --- /dev/null +++ b/Application/Commands/SayCommand.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using SharedLibraryCore; +using SharedLibraryCore.Commands; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; +using EFClient = Data.Models.Client.EFClient; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Prints out a message to all clients on the server + /// + public class SayCommand : Command + { + public SayCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "say"; + Description = _translationLookup["COMMANDS_SAY_DESC"]; + Alias = "s"; + Permission = EFClient.Permission.Moderator; + RequiresTarget = false; + Arguments = new[] + { + new CommandArgument + { + Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], + Required = true + } + }; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + gameEvent.Owner.Broadcast( + _translationLookup["COMMANDS_SAY_FORMAT"].FormatExt(gameEvent.Origin.Name, gameEvent.Data), + gameEvent.Origin); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SAY_SUCCESS"]); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/Commands/WhoAmICommand.cs b/Application/Commands/WhoAmICommand.cs new file mode 100644 index 000000000..472f0d312 --- /dev/null +++ b/Application/Commands/WhoAmICommand.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; +using Data.Models.Client; +using SharedLibraryCore; +using SharedLibraryCore.Configuration; +using SharedLibraryCore.Interfaces; + +namespace IW4MAdmin.Application.Commands +{ + /// + /// Prints client information + /// + public class WhoAmICommand : Command + { + public WhoAmICommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) + { + Name = "whoami"; + Description = _translationLookup["COMMANDS_WHO_DESC"]; + Alias = "who"; + Permission = EFClient.Permission.User; + RequiresTarget = false; + } + + public override Task ExecuteAsync(GameEvent gameEvent) + { + var you = + "[(Color::Yellow)#{{clientNumber}}(Color::White)] [(Color::Yellow)@{{clientId}}(Color::White)] [{{networkId}}] [{{ip}}] [(Color::Cyan){{level}}(Color::White){{tag}}(Color::White)] {{name}}" + .FormatExt(gameEvent.Origin.ClientNumber, + gameEvent.Origin.ClientId, gameEvent.Origin.GuidString, + gameEvent.Origin.IPAddressString, gameEvent.Origin.ClientPermission.Name, + string.IsNullOrEmpty(gameEvent.Origin.Tag) ? "" : $" {gameEvent.Origin.Tag}", + gameEvent.Origin.Name); + gameEvent.Origin.Tell(you); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Application/DefaultSettings.json b/Application/DefaultSettings.json index 77077203a..54673aef4 100644 --- a/Application/DefaultSettings.json +++ b/Application/DefaultSettings.json @@ -1,11 +1,11 @@ { "AutoMessagePeriod": 60, "AutoMessages": [ - "This server uses ^5IW4M Admin v{{VERSION}} ^7get it at ^5raidmax.org/IW4MAdmin", - "^5IW4M Admin ^7sees ^5YOU!", + "This server uses (Color::Accent)IW4M Admin v{{VERSION}} (Color::White)get it at (Color::Accent)raidmax.org/IW4MAdmin", + "(Color::Accent)IW4M Admin (Color::White)sees (Color::Accent)YOU!", "{{TOPSTATS}}", - "This server has seen a total of ^5{{TOTALPLAYERS}} ^7players!", - "Cheaters are ^1unwelcome ^7 on this server", + "This server has seen a total of (Color::Accent){{TOTALPLAYERS}} (Color::White)players!", + "Cheaters are (Color::Red)unwelcome (Color::White)on this server", "Did you know 8/10 people agree with unverified statistics?" ], "GlobalRules": [ diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 412c8da81..0ba4a3251 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -23,7 +23,9 @@ using Serilog.Context; using static SharedLibraryCore.Database.Models.EFClient; using Data.Models; using Data.Models.Server; +using IW4MAdmin.Application.Commands; using Microsoft.EntityFrameworkCore; +using SharedLibraryCore.Formatting; using static Data.Models.Client.EFClient; namespace IW4MAdmin @@ -433,7 +435,7 @@ namespace IW4MAdmin if (E.Origin.Level > Permission.Moderator) { - E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count)); + E.Origin.Tell(loc["SERVER_REPORT_COUNT_V2"].FormatExt(E.Owner.Reports.Count)); } } @@ -1109,6 +1111,15 @@ namespace IW4MAdmin Version = RconParser.Version; } + if (!RconParser.Configuration.ColorCodeMapping.ContainsKey(ColorCodes.Accent.ToString())) + { + var accentKey = Manager.GetApplicationSettings().Configuration().IngameAccentColorKey; + RconParser.Configuration.ColorCodeMapping.Add(ColorCodes.Accent.ToString(), + RconParser.Configuration.ColorCodeMapping.TryGetValue(accentKey, out var colorCode) + ? colorCode + : ""); + } + var svRunning = await this.GetMappedDvarValueOrDefaultAsync("sv_running"); if (!string.IsNullOrEmpty(svRunning.Value) && svRunning.Value != "1") @@ -1344,7 +1355,7 @@ namespace IW4MAdmin return; } - var message = loc["COMMANDS_WARNING_FORMAT"] + var message = loc["COMMANDS_WARNING_FORMAT_V2"] .FormatExt(activeClient.Warnings, activeClient.Name, reason); activeClient.CurrentServer.Broadcast(message); } @@ -1472,7 +1483,7 @@ namespace IW4MAdmin Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString()))); Manager.GetMessageTokens().Add(new MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString()))); Manager.GetMessageTokens().Add(new MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.NextMapCommand.GetNextMap(s, _translationLookup))); - Manager.GetMessageTokens().Add(new MessageToken("ADMINS", (Server s) => Task.FromResult(SharedLibraryCore.Commands.ListAdminsCommand.OnlineAdmins(s, _translationLookup)))); + Manager.GetMessageTokens().Add(new MessageToken("ADMINS", (Server s) => Task.FromResult(ListAdminsCommand.OnlineAdmins(s, _translationLookup)))); } } } diff --git a/Application/RConParsers/DynamicRConParserConfiguration.cs b/Application/RConParsers/DynamicRConParserConfiguration.cs index 87fb54d6d..96355612c 100644 --- a/Application/RConParsers/DynamicRConParserConfiguration.cs +++ b/Application/RConParsers/DynamicRConParserConfiguration.cs @@ -3,6 +3,7 @@ using SharedLibraryCore.Interfaces; using SharedLibraryCore.RCon; using System.Collections.Generic; using System.Globalization; +using SharedLibraryCore.Formatting; namespace IW4MAdmin.Application.RConParsers { @@ -31,6 +32,22 @@ namespace IW4MAdmin.Application.RConParsers public int? DefaultRConPort { get; set; } public string DefaultInstallationDirectoryHint { get; set; } + public ColorCodeMapping ColorCodeMapping { get; set; } = new ColorCodeMapping + { + // this is the default mapping (IW4), but can be overridden as needed in the parsers + {ColorCodes.Black.ToString(), "^0"}, + {ColorCodes.Red.ToString(), "^1"}, + {ColorCodes.Green.ToString(), "^2"}, + {ColorCodes.Yellow.ToString(), "^3"}, + {ColorCodes.Blue.ToString(), "^4"}, + {ColorCodes.Cyan.ToString(), "^5"}, + {ColorCodes.Pink.ToString(), "^6"}, + {ColorCodes.White.ToString(), "^7"}, + {ColorCodes.Map.ToString(), "^8"}, + {ColorCodes.Grey.ToString(), "^9"}, + {ColorCodes.Wildcard.ToString(), ":^"}, + }; + public DynamicRConParserConfiguration(IParserRegexFactory parserRegexFactory) { Status = parserRegexFactory.CreateParserRegex(); @@ -42,4 +59,4 @@ namespace IW4MAdmin.Application.RConParsers MaxPlayersStatus = parserRegexFactory.CreateParserRegex(); } } -} +} \ No newline at end of file diff --git a/Plugins/ScriptPlugins/ParserPT6.js b/Plugins/ScriptPlugins/ParserPT6.js index 43d7ce4a6..01e839d71 100644 --- a/Plugins/ScriptPlugins/ParserPT6.js +++ b/Plugins/ScriptPlugins/ParserPT6.js @@ -3,7 +3,7 @@ var eventParser; var plugin = { author: 'RaidMax, Xerxes', - version: 1.1, + version: 1.2, name: 'Plutonium T6 Parser', isParser: true, @@ -38,6 +38,21 @@ var plugin = { rconParser.Configuration.Status.AddMapping(103, 4); rconParser.Configuration.Status.AddMapping(104, 5); rconParser.Configuration.Status.AddMapping(105, 6); + + // this is mostly default but just an example on how to map + rconParser.Configuration.ColorCodeMapping.Clear(); + rconParser.Configuration.ColorCodeMapping.Add('Black', '^0'); + rconParser.Configuration.ColorCodeMapping.Add('Red', '^1'); + rconParser.Configuration.ColorCodeMapping.Add('Green', '^2'); + rconParser.Configuration.ColorCodeMapping.Add('Yellow', '^3'); + rconParser.Configuration.ColorCodeMapping.Add('Blue', '^4'); + rconParser.Configuration.ColorCodeMapping.Add('Cyan', '^5'); + rconParser.Configuration.ColorCodeMapping.Add('Pink', '^6'); + rconParser.Configuration.ColorCodeMapping.Add('White', '^7'); + rconParser.Configuration.ColorCodeMapping.Add('Map', '^8'); + rconParser.Configuration.ColorCodeMapping.Add('Grey', '^9'); + rconParser.Configuration.ColorCodeMapping.Add('LightBlue', '^;'); + rconParser.Configuration.ColorCodeMapping.Add('LightYellow', '^:'); eventParser.Configuration.GameDirectory = ''; eventParser.Configuration.GuidNumberStyle = 7; // Integer diff --git a/Plugins/Stats/Client/ServerDistributionCalculator.cs b/Plugins/Stats/Client/ServerDistributionCalculator.cs index f772b29f3..02ed57fd3 100644 --- a/Plugins/Stats/Client/ServerDistributionCalculator.cs +++ b/Plugins/Stats/Client/ServerDistributionCalculator.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore; using SharedLibraryCore; using SharedLibraryCore.Interfaces; using Stats.Client.Abstractions; +using Stats.Config; using Stats.Helpers; namespace Stats.Client diff --git a/Plugins/Stats/Commands/MostKillsCommand.cs b/Plugins/Stats/Commands/MostKillsCommand.cs index b6f34294d..cddf45f1f 100644 --- a/Plugins/Stats/Commands/MostKillsCommand.cs +++ b/Plugins/Stats/Commands/MostKillsCommand.cs @@ -9,8 +9,8 @@ using Data.Models.Client.Stats; using SharedLibraryCore.Database.Models; using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; -using IW4MAdmin.Plugins.Stats.Config; using IW4MAdmin.Plugins.Stats.Helpers; +using Stats.Config; namespace IW4MAdmin.Plugins.Stats.Commands { @@ -76,7 +76,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands var iqList = await iqStats.ToListAsync(); - return iqList.Select((stats, index) => translationLookup["PLUGINS_STATS_COMMANDS_MOSTKILLS_FORMAT"] + return iqList.Select((stats, index) => translationLookup["PLUGINS_STATS_COMMANDS_MOSTKILLS_FORMAT_V2"] .FormatExt(index + 1, stats.Name, stats.Kills)) .Prepend(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTKILLS_HEADER"]); } diff --git a/Plugins/Stats/Commands/MostPlayedCommand.cs b/Plugins/Stats/Commands/MostPlayedCommand.cs index 47328860f..8966d4bd4 100644 --- a/Plugins/Stats/Commands/MostPlayedCommand.cs +++ b/Plugins/Stats/Commands/MostPlayedCommand.cs @@ -20,9 +20,9 @@ namespace IW4MAdmin.Plugins.Stats.Commands { var serverId = StatManager.GetIdForServer(s); - var mostPlayed = new List() + var mostPlayed = new List { - $"^5--{translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--" + $"(Color::Accent)--{translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--" }; await using var context = contextFactory.CreateContext(false); @@ -48,7 +48,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands var iqList = await iqStats.ToListAsync(); mostPlayed.AddRange(iqList.Select((stats, index) => - $"#{index + 1} " + translationLookup["COMMANDS_MOST_PLAYED_FORMAT"].FormatExt(stats.Name, stats.Kills, + $"#{index + 1} " + translationLookup["COMMANDS_MOST_PLAYED_FORMAT_V2"].FormatExt(stats.Name, stats.Kills, (DateTime.UtcNow - DateTime.UtcNow.AddSeconds(-stats.TimePlayed)) .HumanizeForCurrentCulture()))); diff --git a/Plugins/Stats/Commands/TopStats.cs b/Plugins/Stats/Commands/TopStats.cs index fd3bc9d3b..4cb4607bd 100644 --- a/Plugins/Stats/Commands/TopStats.cs +++ b/Plugins/Stats/Commands/TopStats.cs @@ -1,35 +1,32 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Linq; +using System.Linq; using System.Threading.Tasks; - using SharedLibraryCore; using System.Collections.Generic; -using Data.Abstractions; -using Data.Models.Client.Stats; -using SharedLibraryCore.Database.Models; using IW4MAdmin.Plugins.Stats.Helpers; using SharedLibraryCore.Configuration; using SharedLibraryCore.Interfaces; +using EFClient = Data.Models.Client.EFClient; namespace IW4MAdmin.Plugins.Stats.Commands { - class TopStats : Command + public class TopStats : Command { public static async Task> GetTopStats(Server s, ITranslationLookup translationLookup) { - long serverId = StatManager.GetIdForServer(s); + var serverId = StatManager.GetIdForServer(s); var topStatsText = new List() { - $"^5--{translationLookup["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--" + $"(Color::Accent)--{translationLookup["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--" }; var stats = await Plugin.Manager.GetTopStats(0, 5, serverId); - var statsList = stats.Select((stats, index) => $"#{index + 1} ^3{stats.Name}^7 - ^5{stats.KDR} ^7{translationLookup["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}"); + var statsList = stats.Select((stats, index) => + translationLookup["COMMANDS_TOPSTATS_RESULT"] + .FormatExt(index + 1, stats.Name, stats.KDR, stats.Performance)); topStatsText.AddRange(statsList); - // no one qualified + // no one qualified if (topStatsText.Count == 1) { topStatsText = new List() @@ -41,11 +38,10 @@ namespace IW4MAdmin.Plugins.Stats.Commands return topStatsText; } - private readonly CommandConfiguration _config; - private readonly IDatabaseContextFactory _contextFactory; + private new readonly CommandConfiguration _config; - public TopStats(CommandConfiguration config, ITranslationLookup translationLookup, - IDatabaseContextFactory contextFactory) : base(config, translationLookup) + public TopStats(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, + translationLookup) { Name = "topstats"; Description = translationLookup["PLUGINS_STATS_COMMANDS_TOP_DESC"]; @@ -54,7 +50,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands RequiresTarget = false; _config = config; - _contextFactory = contextFactory; } public override async Task ExecuteAsync(GameEvent E) @@ -76,4 +71,4 @@ namespace IW4MAdmin.Plugins.Stats.Commands } } } -} +} \ No newline at end of file diff --git a/Plugins/Stats/Commands/ViewStats.cs b/Plugins/Stats/Commands/ViewStats.cs index 5ef774de7..32ea905a4 100644 --- a/Plugins/Stats/Commands/ViewStats.cs +++ b/Plugins/Stats/Commands/ViewStats.cs @@ -24,9 +24,9 @@ namespace IW4MAdmin.Plugins.Stats.Commands Alias = "xlrstats"; Permission = EFClient.Permission.User; RequiresTarget = false; - Arguments = new CommandArgument[] + Arguments = new [] { - new CommandArgument() + new CommandArgument { Name = "player", Required = false @@ -73,14 +73,15 @@ namespace IW4MAdmin.Plugins.Stats.Commands if (pStats == null) { await using var context = _contextFactory.CreateContext(false); - pStats = (await context.Set() - .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId)); + pStats = await context.Set() + .FirstOrDefaultAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId); } // if it's still null then they've not gotten a kill or death yet statLine = pStats == null ? _translationLookup["PLUGINS_STATS_COMMANDS_NOTAVAILABLE"] - : $"^5{pStats.Kills} ^7{_translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{_translationLookup["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{_translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}"; + : _translationLookup["COMMANDS_VIEW_STATS_RESULT"].FormatExt(pStats.Kills, pStats.Deaths, + pStats.KDR, pStats.Performance, performanceRankingString); } // getting self stats @@ -108,7 +109,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands // if it's still null then they've not gotten a kill or death yet statLine = pStats == null ? _translationLookup["PLUGINS_STATS_COMMANDS_NOTAVAILABLE"] - : $"^5{pStats.Kills} ^7{_translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{_translationLookup["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{_translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}"; + : _translationLookup["COMMANDS_VIEW_STATS_RESULT"].FormatExt(pStats.Kills, pStats.Deaths, + pStats.KDR, pStats.Performance, performanceRankingString); } if (E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix)) diff --git a/Plugins/Stats/Config/StatsConfiguration.cs b/Plugins/Stats/Config/StatsConfiguration.cs index b50038534..3d2f3e1a7 100644 --- a/Plugins/Stats/Config/StatsConfiguration.cs +++ b/Plugins/Stats/Config/StatsConfiguration.cs @@ -1,11 +1,11 @@ -using SharedLibraryCore; -using SharedLibraryCore.Interfaces; -using Stats.Config; -using System; +using System; using System.Collections.Generic; +using IW4MAdmin.Plugins.Stats.Config; +using SharedLibraryCore; +using SharedLibraryCore.Interfaces; using static IW4MAdmin.Plugins.Stats.Cheat.Detection; -namespace IW4MAdmin.Plugins.Stats.Config +namespace Stats.Config { public class StatsConfiguration : IBaseConfiguration { @@ -74,28 +74,28 @@ namespace IW4MAdmin.Plugins.Stats.Config public IBaseConfiguration Generate() { AnticheatConfiguration.Enable = - Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]); - KillstreakMessages = new List() + Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"].PromptBool(); + KillstreakMessages = new List { - new StreakMessageConfiguration() + new StreakMessageConfiguration { Count = -1, - Message = "Try not to kill yourself anymore" + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_SUICIDE"] }, - new StreakMessageConfiguration() + new StreakMessageConfiguration { Count = 5, - Message = "Great job! You're on a ^55 killstreak!" + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_5"] }, - new StreakMessageConfiguration() + new StreakMessageConfiguration { Count = 10, - Message = "Amazing! ^510 kills ^7without dying!" + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_10"] }, - new StreakMessageConfiguration() + new StreakMessageConfiguration { Count = 25, - Message = "You better call in that nuke, ^525 killstreak^7!" + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_25"] } }; @@ -104,12 +104,12 @@ namespace IW4MAdmin.Plugins.Stats.Config new StreakMessageConfiguration() { Count = 5, - Message = "Pick it up soldier, you've died ^55 times ^7in a row..." + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_DEATH_STREAK_MESSAGE_5"] }, new StreakMessageConfiguration() { Count = 10, - Message = "Seriously? ^510 deaths ^7without getting a kill?" + Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_DEATH_STREAK_MESSAGE_10"] }, }; diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index 4e0d63ee6..01026c57e 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -21,6 +21,7 @@ using Data.Models.Server; using Humanizer.Localisation; using Microsoft.Extensions.Logging; using Stats.Client.Abstractions; +using Stats.Config; using Stats.Helpers; using static IW4MAdmin.Plugins.Stats.Cheat.Detection; using EFClient = SharedLibraryCore.Database.Models.EFClient; diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index 3959be7e4..7f4147271 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Logging; using SharedLibraryCore.Commands; using IW4MAdmin.Plugins.Stats.Client.Abstractions; using Stats.Client.Abstractions; +using Stats.Config; using EFClient = SharedLibraryCore.Database.Models.EFClient; namespace IW4MAdmin.Plugins.Stats @@ -88,7 +89,7 @@ namespace IW4MAdmin.Plugins.Stats break; case GameEvent.EventType.Command: var shouldPersist = !string.IsNullOrEmpty(E.Data) && - E.Extra is SayCommand; + E.Extra?.GetType().Name == "SayCommand"; if (shouldPersist) { await Manager.AddMessageAsync(E.Origin.ClientId, StatManager.GetIdForServer(S), false, E.Data); diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index ab1e7e34b..7015c031f 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -45,16 +45,16 @@ namespace IW4MAdmin.Plugins.Welcome public Task OnTickAsync(Server S) => Task.CompletedTask; - public async Task OnEventAsync(GameEvent E, Server S) + public async Task OnEventAsync(GameEvent gameEvent, Server server) { - if (E.Type == GameEvent.EventType.Join) + if (gameEvent.Type == GameEvent.EventType.Join) { - var newPlayer = E.Origin; - if ((newPlayer.Level >= Permission.Trusted && !E.Origin.Masked) || - (!string.IsNullOrEmpty(newPlayer.GetAdditionalProperty("ClientTag")) && + var newPlayer = gameEvent.Origin; + if (newPlayer.Level >= Permission.Trusted && !gameEvent.Origin.Masked|| + !string.IsNullOrEmpty(newPlayer.GetAdditionalProperty("ClientTag")) && newPlayer.Level != Permission.Flagged && newPlayer.Level != Permission.Banned && - !newPlayer.Masked)) - E.Owner.Broadcast( + !newPlayer.Masked) + gameEvent.Owner.Broadcast( await ProcessAnnouncement(_configHandler.Configuration().PrivilegedAnnouncementMessage, newPlayer)); @@ -73,10 +73,11 @@ namespace IW4MAdmin.Plugins.Welcome .FirstOrDefaultAsync(); } - E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penaltyReason}) has joined!"); + gameEvent.Owner.ToAdmins(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_FLAG_MESSAGE"] + .FormatExt(newPlayer.Name, penaltyReason)); } else - E.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().UserAnnouncementMessage, + gameEvent.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().UserAnnouncementMessage, newPlayer)); } } @@ -85,7 +86,7 @@ namespace IW4MAdmin.Plugins.Welcome { msg = msg.Replace("{{ClientName}}", joining.Name); msg = msg.Replace("{{ClientLevel}}", - $"{Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name)}{(string.IsNullOrEmpty(joining.GetAdditionalProperty("ClientTag")) ? "" : $" ^7({joining.GetAdditionalProperty("ClientTag")}^7)")}"); + $"{Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name)}{(string.IsNullOrEmpty(joining.GetAdditionalProperty("ClientTag")) ? "" : $" (Color::White)({joining.GetAdditionalProperty("ClientTag")}(Color::White))")}"); // this prevents it from trying to evaluate it every message if (msg.Contains("{{ClientLocation}}")) { diff --git a/Plugins/Welcome/WelcomeConfiguration.cs b/Plugins/Welcome/WelcomeConfiguration.cs index 177538d55..f12050da3 100644 --- a/Plugins/Welcome/WelcomeConfiguration.cs +++ b/Plugins/Welcome/WelcomeConfiguration.cs @@ -11,9 +11,9 @@ namespace IW4MAdmin.Plugins.Welcome public IBaseConfiguration Generate() { - UserAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERANNOUNCE"]; - UserWelcomeMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERWELCOME"]; - PrivilegedAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_PRIVANNOUNCE"]; + UserAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERANNOUNCE_V2"]; + UserWelcomeMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERWELCOME_V2"]; + PrivilegedAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_PRIVANNOUNCE_V2"]; return this; } diff --git a/SharedLibraryCore/Commands/CommandProcessing.cs b/SharedLibraryCore/Commands/CommandProcessing.cs index a85ad282b..8678220b1 100644 --- a/SharedLibraryCore/Commands/CommandProcessing.cs +++ b/SharedLibraryCore/Commands/CommandProcessing.cs @@ -136,7 +136,7 @@ namespace SharedLibraryCore.Commands E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); foreach (var p in matchingPlayers) { - E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}"); + E.Origin.Tell($"[(Color::Yellow){p.ClientNumber}(Color::White)] {p.Name}"); } throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 6a4a76189..7f17fc500 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -219,72 +219,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Prints out a message to all clients on the server - /// - public class SayCommand : Command - { - public SayCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "say"; - Description = _translationLookup["COMMANDS_SAY_DESC"]; - Alias = "s"; - Permission = Permission.Moderator; - RequiresTarget = false; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], - Required = true - } - }; - } - - public override Task ExecuteAsync(GameEvent E) - { - E.Owner.Broadcast($"{(E.Owner.GameName == Server.Game.IW4 ? "^:" : "")}{E.Origin.Name} - ^6{E.Data}^7", E.Origin); - E.Origin.Tell(_translationLookup["COMMANDS_SAY_SUCCESS"]); - return Task.CompletedTask; - } - } - - /// - /// Prints out a message to all clients on all servers - /// - public class SayAllCommand : Command - { - public SayAllCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "sayall"; - Description = _translationLookup["COMMANDS_SAY_ALL_DESC"]; - Alias = "sa"; - Permission = Permission.Moderator; - RequiresTarget = false; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], - Required = true - } - }; - } - - public override Task ExecuteAsync(GameEvent E) - { - string message = _translationLookup["COMMANDS_SAY_ALL_MESSAGE_FORMAT"].FormatExt(E.Origin.Name, E.Data); - - foreach (var server in E.Owner.Manager.GetServers()) - { - server.Broadcast(message, E.Origin); - } - - E.Origin.Tell(_translationLookup["COMMANDS_SAY_SUCCESS"]); - return Task.CompletedTask; - } - } - /// /// Temporarily bans a client /// @@ -452,57 +386,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Prints client information - /// - public class WhoAmICommand : Command - { - public WhoAmICommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "whoami"; - Description = _translationLookup["COMMANDS_WHO_DESC"]; - Alias = "who"; - Permission = Permission.User; - RequiresTarget = false; - } - - public override Task ExecuteAsync(GameEvent gameEvent) - { - var you = _translationLookup["COMMANDS_WHOAMI_FORMAT"].FormatExt(gameEvent.Origin.ClientNumber, gameEvent.Origin.ClientId, gameEvent.Origin.GuidString, - gameEvent.Origin.IPAddressString, gameEvent.Origin.ClientPermission.Name, string.IsNullOrEmpty(gameEvent.Origin.Tag) ? "" : $" {gameEvent.Origin.Tag}^7", gameEvent.Origin.Name); - gameEvent.Origin.Tell(you); - - return Task.CompletedTask; - } - } - - /// - /// List online clients - /// - public class ListClientsCommand : Command - { - public ListClientsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "list"; - Description = _translationLookup["COMMANDS_LIST_DESC"]; - Alias = "l"; - Permission = Permission.Moderator; - RequiresTarget = false; - } - - public override Task ExecuteAsync(GameEvent gameEvent) - { - var clientList = gameEvent.Owner.GetClientsAsList() - .Select(client => _translationLookup["COMMANDS_LIST_FORMAT"] - .FormatExt(client.ClientPermission.Name, string.IsNullOrEmpty(client.Tag) ? "" : $" {client.Tag}^7", client.ClientNumber, client.Name)) - .ToArray(); - - gameEvent.Origin.Tell(clientList); - - return Task.CompletedTask; - } - } - /// /// Fast restarts the map /// @@ -522,7 +405,7 @@ namespace SharedLibraryCore.Commands await E.Owner.ExecuteCommandAsync("fast_restart"); var _ = !E.Origin.Masked ? - E.Owner.Broadcast($"^5{E.Origin.Name} ^7{_translationLookup["COMMANDS_FASTRESTART_UNMASKED"]}") : + E.Owner.Broadcast($"(Color::Accent){E.Origin.Name} (Color::White){_translationLookup["COMMANDS_FASTRESTART_UNMASKED"]}") : E.Owner.Broadcast(_translationLookup["COMMANDS_FASTRESTART_MASKED"]); } } @@ -544,7 +427,7 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { _ = !E.Origin.Masked ? - E.Owner.Broadcast($"{_translationLookup["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]", E.Origin) : + E.Owner.Broadcast($"{_translationLookup["COMMANDS_MAPROTATE"]} [(Color::Accent){E.Origin.Name}(Color::White)]", E.Origin) : E.Owner.Broadcast(_translationLookup["COMMANDS_MAPROTATE"], E.Origin); await Task.Delay(E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000); @@ -618,13 +501,13 @@ namespace SharedLibraryCore.Commands else if (gameEvent.Origin.Level < Permission.Owner && !steppedPrivileges) { // only the owner is allowed to set levels - gameEvent.Origin.Tell($"{_translationLookup["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{gameEvent.Target.Name}"); + gameEvent.Origin.Tell($"{_translationLookup["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} (Color::White){gameEvent.Target.Name}"); return; } else if (gameEvent.Target.Level == Permission.Flagged) { - gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_FLAGGED"].FormatExt(gameEvent.Target.Name)); + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_FLAGGED"].FormatExt(gameEvent.Target.Name + "(Color::White)")); return; } @@ -633,8 +516,8 @@ namespace SharedLibraryCore.Commands { // can't promote a client to higher than your current perms // or your peer - gameEvent.Origin.Tell(string.Format(_translationLookup["COMMANDS_SETLEVEL_LEVELTOOHIGH"], gameEvent.Target.Name, (gameEvent.Origin.Level - 1).ToLocalizedLevelName())); - return; + gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_LEVELTOOHIGH_V2"] + .FormatExt(gameEvent.Target.Name, (gameEvent.Origin.Level - 1).ToLocalizedLevelName())); } // valid @@ -654,7 +537,7 @@ namespace SharedLibraryCore.Commands if (result.FailReason == GameEvent.EventFailReason.Invalid) { gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_INVALID"] - .FormatExt(gameEvent.Target.Name, newPerm.ToString())); + .FormatExt(gameEvent.Target.Name + "(Color::White)", newPerm.ToString())); return; } @@ -733,42 +616,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Lists all unmasked admins - /// - public class ListAdminsCommand : Command - { - public ListAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "admins"; - Description = _translationLookup["COMMANDS_ADMINS_DESC"]; - Alias = "a"; - Permission = Permission.User; - RequiresTarget = false; - } - - public static string OnlineAdmins(Server S, ITranslationLookup lookup) - { - var onlineAdmins = S.GetClientsAsList() - .Where(p => p.Level > Permission.Flagged) - .Where(p => !p.Masked) - .Select(p => $"[^3{Utilities.ConvertLevelToColor(p.Level, p.ClientPermission.Name)}^7] {p.Name}"); - - return onlineAdmins.Count() > 0 ? - string.Join(Environment.NewLine, onlineAdmins) : - lookup["COMMANDS_ADMINS_NONE"]; - } - - public override Task ExecuteAsync(GameEvent E) - { - foreach (string line in OnlineAdmins(E.Owner, _translationLookup).Split(Environment.NewLine)) - { - var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? E.Owner.Broadcast(line) : E.Origin.Tell(line); - } - - return Task.CompletedTask; - } - } /// /// Attempts to load the specified map @@ -809,50 +656,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Finds player by name - /// - public class FindPlayerCommand : Command - { - public FindPlayerCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "find"; - Description = _translationLookup["COMMANDS_FIND_DESC"]; - Alias = "f"; - Permission = Permission.Administrator; - RequiresTarget = false; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_PLAYER"], - Required = true - } - }; - } - - public override async Task ExecuteAsync(GameEvent E) - { - if (E.Data.Length < 3) - { - E.Origin.Tell(_translationLookup["COMMANDS_FIND_MIN"]); - return; - } - - var db_players = (await (E.Owner.Manager.GetClientService() as ClientService).FindClientsByIdentifier(E.Data)); - - if (db_players.Count == 0) - { - E.Origin.Tell(_translationLookup["COMMANDS_FIND_EMPTY"]); - return; - } - - foreach (var client in db_players) - { - E.Origin.Tell(_translationLookup["COMMANDS_FIND_FORMAT"].FormatExt(client.Name, client.ClientId, Utilities.ConvertLevelToColor((Permission)client.LevelInt, client.Level), client.IPAddress, client.LastConnectionText)); - } - } - } /// /// Lists server and global rules @@ -902,40 +705,7 @@ namespace SharedLibraryCore.Commands } } - /// - /// Sends a private message to another player - /// - public class PrivateMessageCommand : Command - { - public PrivateMessageCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "privatemessage"; - Description = _translationLookup["COMMANDS_PM_DESC"]; - Alias = "pm"; - Permission = Permission.User; - RequiresTarget = true; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_PLAYER"], - Required = true - }, - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_MESSAGE"], - Required = true - } - }; - } - - public override Task ExecuteAsync(GameEvent E) - { - E.Target.Tell($"^1{E.Origin.Name} ^3[PM]^7 - {E.Data}"); - E.Origin.Tell($"To ^3{E.Target.Name} ^7-> {E.Data}"); - return Task.CompletedTask; - } - } + /// /// Flag given client for specified reason @@ -1031,116 +801,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Report client for given reason - /// - public class ReportClientCommand : Command - { - public ReportClientCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "report"; - Description = _translationLookup["COMMANDS_REPORT_DESC"]; - Alias = "rep"; - Permission = Permission.User; - RequiresTarget = true; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_PLAYER"], - Required = true - }, - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_REASON"], - Required = true - } - }; - } - - public override async Task ExecuteAsync(GameEvent commandEvent) - { - if (commandEvent.Data.ToLower().Contains("camp")) - { - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_CAMP"]); - return; - } - - bool success = false; - - switch ((await commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, commandEvent.Owner.Manager.CancellationToken)).FailReason) - { - case GameEvent.EventFailReason.None: - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_SUCCESS"]); - success = true; - break; - case GameEvent.EventFailReason.Exception: - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_DUPLICATE"]); - break; - case GameEvent.EventFailReason.Permission: - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL"].FormatExt(commandEvent.Target.Name)); - break; - case GameEvent.EventFailReason.Invalid: - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_SELF"]); - break; - case GameEvent.EventFailReason.Throttle: - commandEvent.Origin.Tell(_translationLookup["COMMANDS_REPORT_FAIL_TOOMANY"]); - break; - } - - if (success) - { - commandEvent.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", commandEvent.Origin.Name, commandEvent.Target.Name, commandEvent.Data)); - } - } - } - - /// - /// List all reports on the server - /// - public class ListReportsCommand : Command - { - public ListReportsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "reports"; - Description = _translationLookup["COMMANDS_REPORTS_DESC"]; - Alias = "reps"; - Permission = Permission.Moderator; - RequiresTarget = false; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_CLEAR"], - Required = false - } - }; - } - - public override Task ExecuteAsync(GameEvent E) - { - if (E.Data != null && E.Data.ToLower().Contains(_translationLookup["COMMANDS_ARGS_CLEAR"])) - { - E.Owner.Reports = new List(); - E.Origin.Tell(_translationLookup["COMMANDS_REPORTS_CLEAR_SUCCESS"]); - return Task.CompletedTask; - } - - if (E.Owner.Reports.Count < 1) - { - E.Origin.Tell(_translationLookup["COMMANDS_REPORTS_NONE"]); - return Task.CompletedTask; - } - - foreach (Report R in E.Owner.Reports) - { - E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason)); - } - - return Task.CompletedTask; - } - } - /// /// Masks client from announcements and online admin list /// @@ -1218,49 +878,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Lists alises of specified client - /// - public class ListAliasesCommand : Command - { - public ListAliasesCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup) - { - Name = "alias"; - Description = _translationLookup["COMMANDS_ALIAS_DESC"]; - Alias = "known"; - Permission = EFClient.Permission.Moderator; - RequiresTarget = true; - Arguments = new[] - { - new CommandArgument() - { - Name = _translationLookup["COMMANDS_ARGS_PLAYER"], - Required = true, - } - }; - } - - public override Task ExecuteAsync(GameEvent E) - { - StringBuilder message = new StringBuilder(); - var names = new List(E.Target.AliasLink.Children.Select(a => a.Name)); - var IPs = new List(E.Target.AliasLink.Children.Select(a => a.IPAddress.ConvertIPtoString()).Distinct()); - - E.Origin.Tell($"[^3{E.Target}^7]"); - - message.Append($"{_translationLookup["COMMANDS_ALIAS_ALIASES"]}: "); - message.Append(String.Join(" | ", names)); - E.Origin.Tell(message.ToString()); - - message.Clear(); - message.Append($"{_translationLookup["COMMANDS_ALIAS_IPS"]}: "); - message.Append(String.Join(" | ", IPs)); - E.Origin.Tell(message.ToString()); - - return Task.CompletedTask; - } - } - /// /// Executes RCon command /// @@ -1298,33 +915,6 @@ namespace SharedLibraryCore.Commands } } - /// - /// Lists the loaded plugins - /// - /*public class ListPluginsCommand : Command - { - private readonly IPluginImporter _pluginImporter; - public ListPluginsCommand(CommandConfiguration config, ITranslationLookup translationLookup, IPluginImporter pluginImporter) : base(config, translationLookup) - { - Name = "plugins"; - Description = _translationLookup["COMMANDS_PLUGINS_DESC"]; - Alias = "p"; - Permission = Permission.Administrator; - RequiresTarget = false; - _pluginImporter = pluginImporter; - } - - public override Task ExecuteAsync(GameEvent E) - { - E.Origin.Tell(_translationLookup["COMMANDS_PLUGINS_LOADED"]); - foreach (var P in _pluginImporter.ActivePlugins) - { - E.Origin.Tell(string.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author)); - } - return Task.CompletedTask; - } - }*/ - /// /// Lists external IP /// @@ -1484,11 +1074,11 @@ namespace SharedLibraryCore.Commands if (E.Target == null) { - E.Origin.Tell(_translationLookup["COMMANDS_PING_SELF"].FormatExt(E.Origin.Ping)); + E.Origin.Tell(_translationLookup["COMMANDS_PING_SELF_V2"].FormatExt(E.Origin.Ping)); } else { - E.Origin.Tell(_translationLookup["COMMANDS_PING_TARGET"].FormatExt(E.Target.Name, E.Target.Ping)); + E.Origin.Tell(_translationLookup["COMMANDS_PING_TARGET_V2"].FormatExt(E.Target.Name, E.Target.Ping)); } return Task.CompletedTask; diff --git a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs index b5cad9c09..80869ef58 100644 --- a/SharedLibraryCore/Configuration/ApplicationConfiguration.cs +++ b/SharedLibraryCore/Configuration/ApplicationConfiguration.cs @@ -108,7 +108,7 @@ namespace SharedLibraryCore.Configuration public string ConnectionString { get; set; } [LocalizedDisplayName("WEBFRONT_CONFIGURATION_RCON_POLLRATE")] - public int RConPollRate { get; set; } = 5000; + public int RConPollRate { get; set; } = 8000; [LocalizedDisplayName("WEBFRONT_CONFIGURATION_MAX_TB")] public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0); @@ -116,6 +116,9 @@ namespace SharedLibraryCore.Configuration [LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_COLOR_CODES")] public bool EnableColorCodes { get; set; } + [ConfigurationIgnore] + public string IngameAccentColorKey { get; set; } = "Cyan"; + [LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGE_PERIOD")] public int AutoMessagePeriod { get; set; } diff --git a/SharedLibraryCore/Formatting/ColorCodes.cs b/SharedLibraryCore/Formatting/ColorCodes.cs new file mode 100644 index 000000000..49d19cacb --- /dev/null +++ b/SharedLibraryCore/Formatting/ColorCodes.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace SharedLibraryCore.Formatting +{ + public enum ColorCodes + { + Black, + Red, + Green, + Yellow, + Blue, + Cyan, + Purple, + Pink, + White, + Map, + Grey, + Wildcard, + Accent + } + + public class ColorCodeMapping : Dictionary + { + + } +} \ No newline at end of file diff --git a/SharedLibraryCore/Helpers/ParseEnum.cs b/SharedLibraryCore/Helpers/ParseEnum.cs index ec17f253e..eb005c866 100644 --- a/SharedLibraryCore/Helpers/ParseEnum.cs +++ b/SharedLibraryCore/Helpers/ParseEnum.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharedLibraryCore.Helpers { diff --git a/SharedLibraryCore/Interfaces/IPlugin.cs b/SharedLibraryCore/Interfaces/IPlugin.cs index 47b1737e5..89d6e2857 100644 --- a/SharedLibraryCore/Interfaces/IPlugin.cs +++ b/SharedLibraryCore/Interfaces/IPlugin.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace SharedLibraryCore.Interfaces { @@ -10,9 +9,9 @@ namespace SharedLibraryCore.Interfaces Task OnEventAsync(GameEvent E, Server S); Task OnTickAsync(Server S); - //for logging purposes - String Name { get; } + string Name { get; } float Version { get; } - String Author { get; } + string Author { get; } + bool IsParser => false; } } diff --git a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs index 6770ef824..943185676 100644 --- a/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs +++ b/SharedLibraryCore/Interfaces/IRConParserConfiguration.cs @@ -1,6 +1,8 @@ using SharedLibraryCore.RCon; using System.Collections.Generic; using System.Globalization; +using SharedLibraryCore.Formatting; +using SharedLibraryCore.Localization; namespace SharedLibraryCore.Interfaces { @@ -97,5 +99,7 @@ namespace SharedLibraryCore.Interfaces /// Default Indicator of where the game is installed (ex file path or registry entry) /// string DefaultInstallationDirectoryHint { get; } + + ColorCodeMapping ColorCodeMapping { get; } } } diff --git a/SharedLibraryCore/PartialEntities/EFClient.cs b/SharedLibraryCore/PartialEntities/EFClient.cs index 679f0a577..bda9d2a33 100644 --- a/SharedLibraryCore/PartialEntities/EFClient.cs +++ b/SharedLibraryCore/PartialEntities/EFClient.cs @@ -100,8 +100,9 @@ namespace SharedLibraryCore.Database.Models CorrelationId = CurrentServer.Manager.ProcessingEvents.Values .FirstOrDefault(ev => ev.Type == GameEvent.EventType.Command && (ev.Origin?.ClientId == ClientId || ev.ImpersonationOrigin?.ClientId == ClientId))?.CorrelationId ?? Guid.NewGuid() }; - - e.Output.Add(message.StripColors()); + + e.Output.Add(message.FormatMessageForEngine(CurrentServer?.RconParser.Configuration.ColorCodeMapping) + .StripColors()); CurrentServer?.Manager.AddEvent(e); return e; diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 75ea9c8cf..fdd7a2d6c 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -132,10 +132,12 @@ namespace SharedLibraryCore /// Message to be sent to all players public GameEvent Broadcast(string message, EFClient sender = null) { - string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "", $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); - ServerLogger.LogDebug("All->" + message.StripColors()); + var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "", + $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping)}"); + ServerLogger.LogDebug("All-> {Message}", + message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping).StripColors()); - var e = new GameEvent() + var e = new GameEvent { Type = GameEvent.EventType.Broadcast, Data = formattedMessage, @@ -165,6 +167,8 @@ namespace SharedLibraryCore /// EFClient to send message to protected async Task Tell(string message, EFClient targetClient) { + var engineMessage = message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping); + if (!Utilities.IsDevelopment) { var temporalClientId = targetClient.GetAdditionalProperty("ConnectionClientId"); @@ -173,24 +177,25 @@ namespace SharedLibraryCore var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell, clientNumber, - $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}"); + $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{engineMessage}"); if (targetClient.ClientNumber > -1 && message.Length > 0 && targetClient.Level != EFClient.Permission.Console) await this.ExecuteCommandAsync(formattedMessage); } else { - ServerLogger.LogDebug("Tell[{clientNumber}]->{message}", targetClient.ClientNumber, message.StripColors()); + ServerLogger.LogDebug("Tell[{ClientNumber}]->{Message}", targetClient.ClientNumber, + message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping).StripColors()); } - if (targetClient.Level == EFClient.Permission.Console) { Console.ForegroundColor = ConsoleColor.Green; using (LogContext.PushProperty("Server", ToString())) { - ServerLogger.LogInformation("Command output received: {message}", message); + ServerLogger.LogInformation("Command output received: {Message}", + engineMessage.StripColors()); } - Console.WriteLine(message.StripColors()); + Console.WriteLine(engineMessage.StripColors()); Console.ForegroundColor = ConsoleColor.Gray; } } diff --git a/SharedLibraryCore/Services/ClientService.cs b/SharedLibraryCore/Services/ClientService.cs index e74857130..0a5cba377 100644 --- a/SharedLibraryCore/Services/ClientService.cs +++ b/SharedLibraryCore/Services/ClientService.cs @@ -716,12 +716,15 @@ namespace SharedLibraryCore.Services // we want to project our results var iqClientProjection = iqClients.OrderByDescending(_client => _client.LastConnection) - .Select(_client => new PlayerInfo() + .Select(_client => new PlayerInfo { Name = _client.CurrentAlias.Name, - LevelInt = (int)_client.Level, + LevelInt = (int) _client.Level, LastConnection = _client.LastConnection, ClientId = _client.ClientId, + IPAddress = _client.CurrentAlias.IPAddress.HasValue + ? _client.CurrentAlias.IPAddress.Value.ToString() + : "" }); var clients = await iqClientProjection.ToListAsync(); diff --git a/SharedLibraryCore/TagHelpers/ColorCode.cs b/SharedLibraryCore/TagHelpers/ColorCode.cs index cce5dfc3e..d992fea92 100644 --- a/SharedLibraryCore/TagHelpers/ColorCode.cs +++ b/SharedLibraryCore/TagHelpers/ColorCode.cs @@ -1,8 +1,8 @@ -using Microsoft.AspNetCore.Razor.TagHelpers; -using System.Linq; +using System.Linq; using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Razor.TagHelpers; -namespace SharedLibraryCore +namespace SharedLibraryCore.TagHelpers { [HtmlTargetElement("color-code")] public class ColorCode : TagHelper diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 1e7324376..06468ea26 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -21,6 +21,7 @@ using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; using static Data.Models.Client.EFClient; using Data.Models; +using SharedLibraryCore.Formatting; using static Data.Models.EFPenalty; namespace SharedLibraryCore @@ -178,6 +179,24 @@ namespace SharedLibraryCore /// public static string FixIW4ForwardSlash(this string str) => str.Replace("//", "/ /"); + public static string FormatMessageForEngine(this string str, ColorCodeMapping mapping) + { + if (mapping == null || string.IsNullOrEmpty(str)) + { + return str; + } + + var output = str; + var colorCodeMatches = Regex.Matches(output, @"\(Color::(.{1,16})\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); + foreach (var match in colorCodeMatches.Where(m => m.Success)) + { + var key = match.Groups[1].ToString(); + output = output.Replace(match.Value, mapping.TryGetValue(key, out var code) ? code : ""); + } + + return output.FixIW4ForwardSlash() + mapping[ColorCodes.White.ToString()]; + } + private static readonly IList _zmGameTypes = new[] { "zclassic", "zstandard", "zcleansed", "zgrief" }; /// /// indicates if the given server is running a zombie game mode @@ -189,36 +208,25 @@ namespace SharedLibraryCore public static bool IsCodGame(this Server server) => server.RconParser?.RConEngine == "COD"; /// - /// Get the IW Engine color code corresponding to an admin level + /// Get the color key corresponding to a given user level /// /// Specified player level + /// /// - public static String ConvertLevelToColor(EFClient.Permission level, string localizedLevel) + public static string ConvertLevelToColor(Permission level, string localizedLevel) { - char colorCode = '6'; - // todo: maybe make this game independant? - switch (level) + // todo: make configurable + var colorCode = level switch { - case EFClient.Permission.Banned: - colorCode = '1'; - break; - case EFClient.Permission.Flagged: - colorCode = '9'; - break; - case EFClient.Permission.Owner: - colorCode = '5'; - break; - case EFClient.Permission.User: - colorCode = '2'; - break; - case EFClient.Permission.Trusted: - colorCode = '3'; - break; - default: - break; - } + Permission.Banned => "Red", + Permission.Flagged => "Map", + Permission.Owner => "Accent", + Permission.User => "Yellow", + Permission.Trusted => "Green", + _ => "Pink" + }; - return $"^{colorCode}{localizedLevel ?? level.ToString()}"; + return $"(Color::{colorCode}){localizedLevel ?? level.ToString()}"; } public static string ToLocalizedLevelName(this Permission permission) diff --git a/WebfrontCore/Controllers/ActionController.cs b/WebfrontCore/Controllers/ActionController.cs index 32e83c365..8eb7fa755 100644 --- a/WebfrontCore/Controllers/ActionController.cs +++ b/WebfrontCore/Controllers/ActionController.cs @@ -46,7 +46,8 @@ namespace WebfrontCore.Controllers case nameof(UnbanCommand): _unbanCommandName = cmd.Name; break; - case nameof(SayCommand): + // todo: this should be flag driven + case "SayCommand": _sayCommandName = cmd.Name; break; case nameof(KickCommand): diff --git a/WebfrontCore/Controllers/Client/ClientController.cs b/WebfrontCore/Controllers/Client/ClientController.cs index 68de0410b..48d36345f 100644 --- a/WebfrontCore/Controllers/Client/ClientController.cs +++ b/WebfrontCore/Controllers/Client/ClientController.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using Data.Models; using IW4MAdmin.Plugins.Stats.Config; +using Stats.Config; using WebfrontCore.ViewComponents; namespace WebfrontCore.Controllers diff --git a/WebfrontCore/Controllers/Client/Legacy/StatsController.cs b/WebfrontCore/Controllers/Client/Legacy/StatsController.cs index 0fdbf4bf8..ceb51d803 100644 --- a/WebfrontCore/Controllers/Client/Legacy/StatsController.cs +++ b/WebfrontCore/Controllers/Client/Legacy/StatsController.cs @@ -16,6 +16,7 @@ using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; using Data.Abstractions; using IW4MAdmin.Plugins.Stats.Config; +using Stats.Config; namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers { diff --git a/WebfrontCore/Startup.cs b/WebfrontCore/Startup.cs index ef3668da1..185e36d3d 100644 --- a/WebfrontCore/Startup.cs +++ b/WebfrontCore/Startup.cs @@ -26,6 +26,7 @@ using Data.Abstractions; using Data.Helpers; using IW4MAdmin.Plugins.Stats.Config; using Stats.Client.Abstractions; +using Stats.Config; using WebfrontCore.Controllers.API.Validation; using WebfrontCore.Middleware; diff --git a/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs b/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs index 6e92c5d5c..c9e719a3c 100644 --- a/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs +++ b/WebfrontCore/ViewComponents/TopPlayersViewComponent.cs @@ -5,6 +5,7 @@ using IW4MAdmin.Plugins.Stats.Config; using IW4MAdmin.Plugins.Stats.Helpers; using Microsoft.AspNetCore.Mvc; using SharedLibraryCore.Interfaces; +using Stats.Config; namespace WebfrontCore.ViewComponents {