add configurable command and broadcast command prefix for issue #149

This commit is contained in:
RaidMax 2020-07-31 20:40:03 -05:00
parent 6155493181
commit 04a95aa58a
27 changed files with 403 additions and 61 deletions

View File

@ -81,7 +81,7 @@ namespace IW4MAdmin.Application
ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow;
PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger) };
AdditionalEventParsers = new List<IEventParser>() { new BaseEventParser(parserRegexFactory, logger, appConfigHandler.Configuration()) };
AdditionalRConParsers = new List<IRConParser>() { new BaseRConParser(parserRegexFactory) };
TokenAuthenticator = new TokenAuthentication();
_logger = logger;
@ -432,6 +432,11 @@ namespace IW4MAdmin.Application
commandsToAddToConfig.AddRange(unsavedCommands);
}
// this is because I want to store the command prefix in IW4MAdminSettings, but can't easily
// inject it to all the places that need it
cmdConfig.CommandPrefix = config.CommandPrefix;
cmdConfig.BroadcastCommandPrefix = config.BroadcastCommandPrefix;
foreach (var cmd in commandsToAddToConfig)
{
cmdConfig.Commands.Add(cmd.CommandConfigNameForType(),
@ -729,7 +734,7 @@ namespace IW4MAdmin.Application
public IEventParser GenerateDynamicEventParser(string name)
{
return new DynamicEventParser(_parserRegexFactory, _logger)
return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration())
{
Name = name
};

View File

@ -1,4 +1,5 @@
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System;
@ -12,11 +13,13 @@ namespace IW4MAdmin.Application.EventParsers
{
private readonly Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)> _customEventRegistrations;
private readonly ILogger _logger;
private readonly ApplicationConfiguration _appConfig;
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger)
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig)
{
_customEventRegistrations = new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
_logger = logger;
_appConfig = appConfig;
Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
{
@ -128,7 +131,7 @@ namespace IW4MAdmin.Application.EventParsers
int clientNumber = int.Parse(matchResult.Values[Configuration.Say.GroupMapping[ParserRegex.GroupType.OriginClientNumber]]);
// todo: these need to defined outside of here
if (message[0] == '!' || message[0] == '@')
if (message.StartsWith(_appConfig.CommandPrefix) || message.StartsWith(_appConfig.BroadcastCommandPrefix))
{
return new GameEvent()
{

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Application.EventParsers
{
@ -8,7 +9,7 @@ namespace IW4MAdmin.Application.EventParsers
/// </summary>
sealed internal class DynamicEventParser : BaseEventParser
{
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger) : base(parserRegexFactory, logger)
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig) : base(parserRegexFactory, logger, appConfig)
{
}
}

View File

@ -139,7 +139,7 @@ namespace IW4MAdmin
{
try
{
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E);
C = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration());
}
catch (CommandException e)

View File

@ -59,7 +59,7 @@ namespace SharedLibraryCore
/// <summary>
/// Helper property to provide the syntax of the command
/// </summary>
public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} !{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}";
public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} {_config.CommandPrefix}{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}";
/// <summary>
/// Alternate name for this command to be executed by
@ -123,5 +123,7 @@ namespace SharedLibraryCore
/// indicates if this command allows impersonation (run as)
/// </summary>
public bool AllowImpersonation { get; set; }
public bool IsBroadcast { get; set; }
}
}

View File

@ -1,4 +1,5 @@
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Exceptions;
using System;
using System.Collections.Generic;
@ -10,12 +11,14 @@ namespace SharedLibraryCore.Commands
{
public class CommandProcessing
{
public static async Task<Command> ValidateCommand(GameEvent E)
public static async Task<Command> ValidateCommand(GameEvent E, ApplicationConfiguration appConfig)
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var Manager = E.Owner.Manager;
bool isBroadcast = E.Data.StartsWith(appConfig.BroadcastCommandPrefix);
int prefixLength = isBroadcast ? appConfig.BroadcastCommandPrefix.Length : appConfig.CommandPrefix.Length;
string CommandString = E.Data.Substring(1, E.Data.Length - 1).Split(' ')[0];
string CommandString = E.Data.Substring(prefixLength, E.Data.Length - prefixLength).Split(' ')[0];
E.Message = E.Data;
Command C = null;
@ -34,6 +37,8 @@ namespace SharedLibraryCore.Commands
throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\"");
}
C.IsBroadcast = isBroadcast;
if (!C.AllowImpersonation && E.ImpersonationOrigin != null)
{
E.ImpersonationOrigin.Tell(loc["COMMANDS_RUN_AS_FAIL"]);

View File

@ -773,6 +773,8 @@ namespace SharedLibraryCore.Commands
/// </summary>
public class ListAdminsCommand : Command
{
private readonly CommandConfiguration _config;
public ListAdminsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "admins";
@ -780,6 +782,8 @@ namespace SharedLibraryCore.Commands
Alias = "a";
Permission = Permission.User;
RequiresTarget = false;
_config = config;
}
public static string OnlineAdmins(Server S, ITranslationLookup lookup)
@ -798,7 +802,7 @@ namespace SharedLibraryCore.Commands
{
foreach (string line in OnlineAdmins(E.Owner, _translationLookup).Split(Environment.NewLine))
{
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
}
return Task.CompletedTask;
@ -903,6 +907,8 @@ namespace SharedLibraryCore.Commands
/// </summary>
public class ListRulesCommands : Command
{
private readonly CommandConfiguration _config;
public ListRulesCommands(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "rules";
@ -910,6 +916,8 @@ namespace SharedLibraryCore.Commands
Alias = "r";
Permission = Permission.User;
RequiresTarget = false;
_config = config;
}
public override Task ExecuteAsync(GameEvent E)
@ -917,7 +925,7 @@ namespace SharedLibraryCore.Commands
if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Length < 1 &&
E.Owner.ServerConfig.Rules?.Length < 1)
{
var _ = E.Message.IsBroadcastCommand() ?
var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ?
E.Owner.Broadcast(_translationLookup["COMMANDS_RULES_NONE"]) :
E.Origin.Tell(_translationLookup["COMMANDS_RULES_NONE"]);
}
@ -933,7 +941,7 @@ namespace SharedLibraryCore.Commands
foreach (string r in rules)
{
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
var _ = E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix) ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
}
}

View File

@ -66,6 +66,12 @@ namespace SharedLibraryCore.Configuration
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_LOCALE")]
public string CustomLocale { get; set; }
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_COMMAND_PREFIX")]
public string CommandPrefix { get; set; } = "!";
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BROADCAST_COMMAND_PREFIX")]
public string BroadcastCommandPrefix { get; set; } = "@";
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_DB_PROVIDER")]
public string DatabaseProvider { get; set; } = "sqlite";
[ConfigurationOptional]

View File

@ -1,6 +1,7 @@
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace SharedLibraryCore.Configuration
{
@ -14,6 +15,18 @@ namespace SharedLibraryCore.Configuration
/// </summary>
public Dictionary<string, CommandProperties> Commands { get; set; } = new Dictionary<string, CommandProperties>();
/// <summary>
/// prefix indicated the chat message is a command
/// </summary>
[JsonIgnore]
public string CommandPrefix { get; set; }
/// <summary>
/// prefix indicating that the chat message is a broadcast command
/// </summary>
[JsonIgnore]
public string BroadcastCommandPrefix { get; set; }
public IBaseConfiguration Generate()
{
throw new NotImplementedException();

View File

@ -64,13 +64,19 @@ namespace SharedLibraryCore.Configuration.Validation
RuleFor(_app => _app.GlobalRules)
.NotNull();
RuleForEach(_app => _app.Servers)
.NotEmpty()
.SetValidator(new ServerConfigurationValidator());
RuleFor(_app => _app.MasterUrl)
.NotNull()
.Must(_url => _url != null && _url.Scheme == Uri.UriSchemeHttp);
RuleFor(_app => _app.CommandPrefix)
.NotEmpty();
RuleFor(_app => _app.BroadcastCommandPrefix)
.NotEmpty();
RuleForEach(_app => _app.Servers)
.NotEmpty()
.SetValidator(new ServerConfigurationValidator());
}
}
}

View File

@ -49,5 +49,10 @@ namespace SharedLibraryCore.Interfaces
/// Indicates if the commands can be run as another client
/// </summary>
bool AllowImpersonation { get; }
/// <summary>
/// Indicates if the command result should be broadcasted to all clients
/// </summary>
bool IsBroadcast { get; set; }
}
}

View File

@ -486,7 +486,7 @@ namespace SharedLibraryCore.Database.Models
.DisallowedClientNames
?.Any(_name => Regex.IsMatch(Name, _name)) ?? false)
{
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is generic");
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is not allowed");
Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer));
return false;
}

View File

@ -235,9 +235,14 @@ namespace SharedLibraryCore
return str;
}
public static bool IsBroadcastCommand(this string str)
public static bool IsBroadcastCommand(this string str, string broadcastCommandPrefix)
{
return str[0] == '@';
return str.StartsWith(broadcastCommandPrefix);
}
public static IManagerCommand AsCommand(this GameEvent gameEvent)
{
return gameEvent.Extra as IManagerCommand;
}
/// <summary>

View File

@ -22,6 +22,9 @@
</ItemGroup>
<ItemGroup>
<None Update="Files\GameEvent.Command.CustomPrefix.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Files\GameEvents.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NUnit.Framework;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using System;
using static SharedLibraryCore.GameEvent;
@ -18,17 +19,20 @@ namespace ApplicationTests
private EventLogTest eventLogData;
private IServiceProvider serviceProvider;
private ILogger fakeLogger;
private ApplicationConfiguration appConfig;
[SetUp]
public void Setup()
{
eventLogData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvents.json"));
appConfig = ConfigurationGenerators.CreateApplicationConfiguration();
fakeLogger = A.Fake<ILogger>();
serviceProvider = new ServiceCollection()
.AddSingleton<BaseEventParser>()
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
.AddSingleton<IParserRegexFactory, ParserRegexFactory>()
.AddSingleton(appConfig)
.AddSingleton(fakeLogger)
.BuildServiceProvider();
}
@ -38,22 +42,6 @@ namespace ApplicationTests
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
void AssertMatch(GameEvent src, LogEvent expected)
{
Assert.AreEqual(expected.ExpectedEventType, src.Type);
Assert.AreEqual(expected.ExpectedData, src.Data);
Assert.AreEqual(expected.ExpectedMessage, src.Message);
Assert.AreEqual(expected.ExpectedTime, src.GameTime);
//Assert.AreEqual(expected.ExpectedOriginClientName, src.Origin?.Name);
Assert.AreEqual(expected.ExpectedOriginClientNumber, src.Origin?.ClientNumber);
Assert.AreEqual(expected.ExpectedOriginNetworkId, src.Origin?.NetworkId.ToString("X"));
//Assert.AreEqual(expected.ExpectedTargetClientName, src.Target?.Name);
Assert.AreEqual(expected.ExpectedTargetClientNumber, src.Target?.ClientNumber);
Assert.AreEqual(expected.ExpectedTargetNetworkId, src.Target?.NetworkId.ToString("X"));
}
foreach (var e in eventLogData.Events)
{
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
@ -109,5 +97,45 @@ namespace ApplicationTests
A.CallTo(() => fakeLogger.WriteWarning(A<string>.Ignored))
.MustHaveHappenedOnceExactly();
}
[Test]
public void Test_CustomCommandPrefix_Parses()
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
var commandData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvent.Command.CustomPrefix.json"));
appConfig.CommandPrefix = "^^";
var e = commandData.Events[0];
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
AssertMatch(parsedEvent, e);
}
[Test]
public void Test_CustomBroadcastCommandPrefix_Parses()
{
var eventParser = serviceProvider.GetService<BaseEventParser>();
var commandData = JsonConvert.DeserializeObject<EventLogTest>(System.IO.File.ReadAllText("Files/GameEvent.Command.CustomPrefix.json"));
appConfig.BroadcastCommandPrefix = "@@";
var e = commandData.Events[1];
var parsedEvent = eventParser.GenerateGameEvent(e.EventLine);
AssertMatch(parsedEvent, e);
}
private static void AssertMatch(GameEvent src, LogEvent expected)
{
Assert.AreEqual(expected.ExpectedEventType, src.Type);
Assert.AreEqual(expected.ExpectedData, src.Data);
Assert.AreEqual(expected.ExpectedMessage, src.Message);
Assert.AreEqual(expected.ExpectedTime, src.GameTime);
//Assert.AreEqual(expected.ExpectedOriginClientName, src.Origin?.Name);
Assert.AreEqual(expected.ExpectedOriginClientNumber, src.Origin?.ClientNumber);
Assert.AreEqual(expected.ExpectedOriginNetworkId, src.Origin?.NetworkId.ToString("X"));
//Assert.AreEqual(expected.ExpectedTargetClientName, src.Target?.Name);
Assert.AreEqual(expected.ExpectedTargetClientNumber, src.Target?.ClientNumber);
Assert.AreEqual(expected.ExpectedTargetNetworkId, src.Target?.NetworkId.ToString("X"));
}
}
}

View File

@ -57,7 +57,8 @@ namespace ApplicationTests
.Returns(new Command[]
{
new ImpersonatableCommand(cmdConfig, transLookup),
new NonImpersonatableCommand(cmdConfig, transLookup)
new NonImpersonatableCommand(cmdConfig, transLookup),
new MockCommand(cmdConfig, transLookup)
});
A.CallTo(() => manager.AddEvent(A<GameEvent>.Ignored))
@ -519,5 +520,22 @@ namespace ApplicationTests
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.ChangePermission && !_event.Failed));
}
#endregion
#region PREFIX_PROCESSING
[Test]
public async Task Test_CommandProcessing_IsBroadcastCommand()
{
string broadcastPrefix = "@@";
var config = ConfigurationGenerators.CreateApplicationConfiguration();
config.BroadcastCommandPrefix = broadcastPrefix;
var server = serviceProvider.GetRequiredService<IW4MServer>();
var cmd = EventGenerators.GenerateEvent(GameEvent.EventType.Command, $"{broadcastPrefix}{nameof(MockCommand)}", server);
var result = await CommandProcessing.ValidateCommand(cmd, config);
Assert.AreEqual(nameof(MockCommand), result.Name);
Assert.IsTrue(result.IsBroadcast);
}
#endregion
}
}

View File

@ -0,0 +1,32 @@
{
"Events": [
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;^^help",
"ExpectedEventType": "Command",
"ExpectedData": "^^help",
"ExpectedMessage": "^^help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;@@help",
"ExpectedEventType": "Command",
"ExpectedData": "@@help",
"ExpectedMessage": "@@help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
}
]
}

View File

@ -0,0 +1,159 @@
{
"Events": [
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;this is a test message",
"ExpectedEventType": "Say",
"ExpectedData": "this is a test message",
"ExpectedMessage": "this is a test message",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 sayteam;AAA;1;Player;this is a test team message",
"ExpectedEventType": "Say",
"ExpectedData": "this is a test team message",
"ExpectedMessage": "this is a test team message",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": " 12:34 say;AAA;1;Player;!help",
"ExpectedEventType": "Command",
"ExpectedData": "!help",
"ExpectedMessage": "!help",
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Player",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 754
},
{
"Game": "IW4",
"EventLine": "2824:03 K;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedEventType": "Kill",
"ExpectedData": "K;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 11,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": "BBB",
"ExpectedTargetClientNumber": 6,
"ExpectedTargetClientName": "Snake",
"ExpectedTime": 169443
},
{
"Game": "IW4",
"EventLine": "6:44 K;AAA;0;allies;Player;;-1;world;;none;100;MOD_FALLING;none",
"ExpectedEventType": "Kill",
"ExpectedData": "K;AAA;0;allies;Player;;-1;world;;none;100;MOD_FALLING;none",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "FFFFFFFFFFFFFFFF",
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": "",
"ExpectedTargetNetworkId": "AAA",
"ExpectedTargetClientNumber": 0,
"ExpectedTargetClientName": "Player",
"ExpectedTime": 404
},
{
"Game": "IW4",
"EventLine": "2824:03 D;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedEventType": "Damage",
"ExpectedData": "D;BBB;6;axis;Snake;AAA;11;allies;Doctor;sa80_reflex_xmags_mp;55;MOD_HEAD_SHOT;head",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 11,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": "BBB",
"ExpectedTargetClientNumber": 6,
"ExpectedTargetClientName": "Snake",
"ExpectedTime": 169443
},
{
"Game": "IW4",
"EventLine": "1:43 J;AAA;1;Doctor",
"ExpectedEventType": "Preconnect",
"ExpectedData": "J;AAA;1;Doctor",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Doctor",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 103
},
{
"Game": "IW4",
"EventLine": "1344:54 Q;AAA;1;Cosmic Riptide",
"ExpectedEventType": "Predisconnect",
"ExpectedData": "Q;AAA;1;Cosmic Riptide",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": "AAA",
"ExpectedOriginClientNumber": 1,
"ExpectedOriginClientName": "Cosmic Riptide",
"ExpectedTargetNetworkId": null,
"ExpectedTargetClientNumber": null,
"ExpectedTargetClientName": null,
"ExpectedTime": 80694
},
{
"Game": "IW4",
"EventLine": "75:44 ExitLevel: executed",
"ExpectedEventType": "MapEnd",
"ExpectedData": "ExitLevel: executed",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 4544
},
{
"Game": "IW4",
"EventLine": " 75:44 InitGame",
"ExpectedEventType": "MapChange",
"ExpectedData": "InitGame",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 4544
},
{
"Game": "IW4",
"EventLine": " 0:00 CustomLogLine",
"ExpectedEventType": "Unknown",
"ExpectedData": "CustomLogLine",
"ExpectedMessage": null,
"ExpectedOriginNetworkId": 0,
"ExpectedOriginClientNumber": -1,
"ExpectedOriginClientName": null,
"ExpectedTargetNetworkId": 0,
"ExpectedTargetClientNumber": -1,
"ExpectedTargetClientName": null,
"ExpectedTime": 0
}
]
}

View File

@ -0,0 +1,26 @@
using SharedLibraryCore;
using System;
namespace ApplicationTests.Fixtures
{
static class EventGenerators
{
public static GameEvent GenerateEvent(GameEvent.EventType type, string data, Server owner)
{
switch (type)
{
case GameEvent.EventType.Command:
return new GameEvent
{
Origin = ClientGenerators.CreateDatabaseClient(),
Data = data,
Message = data,
Owner = owner
};
default:
throw new NotImplementedException();
}
}
}
}

View File

@ -1,7 +1,6 @@
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using System;
using System.Threading.Tasks;
namespace ApplicationTests.Mocks
@ -33,4 +32,17 @@ namespace ApplicationTests.Mocks
return Task.CompletedTask;
}
}
class MockCommand : Command
{
public MockCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
Name = nameof(MockCommand);
}
public override Task ExecuteAsync(GameEvent E)
{
return Task.CompletedTask;
}
}
}

View File

@ -3,6 +3,7 @@ using IW4MAdmin;
using IW4MAdmin.Application;
using IW4MAdmin.Application.EventParsers;
using NUnit.Framework;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using System;
using System.Diagnostics;
@ -35,7 +36,7 @@ namespace ApplicationTests
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6MapRotation.log");
@ -61,7 +62,7 @@ namespace ApplicationTests
new SharedLibraryCore.Configuration.ServerConfiguration() { IPAddress = "127.0.0.1", Port = 28960 },
A.Fake<ITranslationLookup>(), A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6Game.log");

View File

@ -16,6 +16,7 @@ using ApplicationTests.Fixtures;
using System.Threading.Tasks;
using Stats.Helpers;
using Stats.Dtos;
using SharedLibraryCore.Configuration;
namespace ApplicationTests
{
@ -73,7 +74,7 @@ namespace ApplicationTests
A.Fake<ITranslationLookup>(),
A.Fake<IRConConnectionFactory>(), A.Fake<IGameLogReaderFactory>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>());
var parser = new BaseEventParser(A.Fake<IParserRegexFactory>(), A.Fake<ILogger>(), A.Fake<ApplicationConfiguration>());
parser.Configuration.GuidNumberStyle = System.Globalization.NumberStyles.Integer;
var log = System.IO.File.ReadAllLines("Files\\T6GameStats.log");

View File

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using WebfrontCore.ViewModels;
using static SharedLibraryCore.Database.Models.EFClient;
@ -13,9 +14,11 @@ namespace WebfrontCore.Controllers
{
public class ActionController : BaseController
{
private readonly ApplicationConfiguration _appConfig;
public ActionController(IManager manager) : base(manager)
{
_appConfig = manager.GetApplicationSettings().Configuration();
}
public IActionResult BanForm()
@ -80,8 +83,8 @@ namespace WebfrontCore.Controllers
}
string command = Duration == 6 ?
$"!ban @{targetId} {Reason}" :
$"!tempban @{targetId} {duration} {Reason}";
$"{_appConfig.CommandPrefix}ban @{targetId} {Reason}" :
$"{_appConfig.CommandPrefix}tempban @{targetId} {duration} {Reason}";
var server = Manager.GetServers().First();
@ -120,7 +123,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.EndPoint,
command = $"!unban @{targetId} {Reason}"
command = $"{_appConfig.CommandPrefix}unban @{targetId} {Reason}"
}));
}
@ -189,7 +192,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.EndPoint,
command = $"!setlevel @{targetId} {level}"
command = $"{_appConfig.CommandPrefix}setlevel @{targetId} {level}"
}));
}
@ -256,7 +259,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.EndPoint,
command = $"!say {message}"
command = $"{_appConfig.CommandPrefix}say {message}"
}));
}
@ -294,7 +297,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.EndPoint,
command = $"!flag @{targetId} {reason}"
command = $"{_appConfig.CommandPrefix}flag @{targetId} {reason}"
}));
}
@ -326,7 +329,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = server.EndPoint,
command = $"!unflag @{targetId} {reason}"
command = $"{_appConfig.CommandPrefix}unflag @{targetId} {reason}"
}));
}
@ -369,7 +372,7 @@ namespace WebfrontCore.Controllers
return await Task.FromResult(RedirectToAction("ExecuteAsync", "Console", new
{
serverId = client.CurrentServer.EndPoint,
command = $"!kick {client.ClientNumber} {reason}"
command = $"{_appConfig.CommandPrefix}kick {client.ClientNumber} {reason}"
}));
}
}

View File

@ -1,9 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -11,9 +11,11 @@ namespace WebfrontCore.Controllers
{
public class ConsoleController : BaseController
{
private readonly ApplicationConfiguration _appconfig;
public ConsoleController(IManager manager) : base(manager)
{
_appconfig = manager.GetApplicationSettings().Configuration();
}
public IActionResult Index()
@ -50,7 +52,8 @@ namespace WebfrontCore.Controllers
var remoteEvent = new GameEvent()
{
Type = GameEvent.EventType.Command,
Data = command,
Data = command.StartsWith(_appconfig.CommandPrefix) || command.StartsWith(_appconfig.BroadcastCommandPrefix) ?
command : $"{_appconfig.CommandPrefix}{command}",
Origin = client,
Owner = server,
IsRemote = true
@ -63,7 +66,7 @@ namespace WebfrontCore.Controllers
{
// wait for the event to process
var completedEvent = await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken);
if (completedEvent.FailReason == GameEvent.EventFailReason.Timeout)
{
response = new[]

View File

@ -61,6 +61,7 @@ namespace WebfrontCore.Controllers
{
ViewBag.IsFluid = true;
ViewBag.Title = Localization["WEBFRONT_NAV_HELP"];
ViewBag.CommandPrefix = Manager.GetApplicationSettings().Configuration().CommandPrefix;
// we don't need to the name of the shared library assembly
var excludedAssembly = typeof(BaseController).Assembly;

View File

@ -26,7 +26,7 @@
<td>@command.Alias</td>
<td>@command.Description</td>
<td>@command.RequiresTarget</td>
<td>!@command.Syntax.Split('!')[1]</td>
<td>@ViewBag.CommandPrefix@command.Syntax.Split(@ViewBag.CommandPrefix)[1]</td>
<td>@command.Permission.ToLocalizedLevelName()</td>
</tr>
}
@ -62,7 +62,7 @@
</tr>
<tr>
<td scope="row" class="bg-primary">@loc["WEBFRONT_HELP_COMMAND_DESC_SYNTAX"]</td>
<td class="bg-dark text-light">!@command.Syntax.Split('!')[1]</td>
<td class="bg-dark text-light">@ViewBag.CommandPrefix@command.Syntax.Split(@ViewBag.CommandPrefix)[1]</td>
</tr>
<tr>
<td scope="row" class="bg-primary" style="border-bottom: #222 1px solid;">@loc["WEBFRONT_HELP_COMMAND_DESC_REQUIRED_LEVEL"]</td>

View File

@ -6,10 +6,6 @@
return false;
}
if (command[0] !== '!') {
$('#console_command_response').text(_localization['WEBFRONT_CONSOLE_COMMAND']).addClass('text-danger');
return false;
}
showLoader();
$.get('/Console/ExecuteAsync', { serverId: serverId, command: command })
.done(function (response) {