Compare commits

...

4 Commits

Author SHA1 Message Date
RaidMax
9117440566 merge to pre (#172)
* update GenerateGuidFromString to resolve to a stable hash code.
fix bots not showing up on live radar

* add 0.0.0.0 as internal "ip" even though it's not actually a valid IP but for cod4x

* implement pm admins command for issue #170

* implement service resolver for script plugins
2020-09-26 18:15:56 -05:00
RaidMax
ad6b6a6465 merge to pre (#169)
* update GenerateGuidFromString to resolve to a stable hash code.
fix bots not showing up on live radar

* add 0.0.0.0 as internal "ip" even though it's not actually a valid IP but for cod4x
2020-09-21 15:37:31 -05:00
RaidMax
eb8145a168 add 0.0.0.0 as internal "ip" even though it's not actually a valid IP but for cod4x 2020-09-04 12:58:54 -05:00
RaidMax
a560e05df8 update pipeline file for seperate builds 2020-08-31 12:33:39 -05:00
23 changed files with 277 additions and 40 deletions

View File

@ -67,13 +67,14 @@ namespace IW4MAdmin.Application
private readonly IEventHandler _eventHandler; private readonly IEventHandler _eventHandler;
private readonly IScriptCommandFactory _scriptCommandFactory; private readonly IScriptCommandFactory _scriptCommandFactory;
private readonly IMetaRegistration _metaRegistration; private readonly IMetaRegistration _metaRegistration;
private readonly IScriptPluginServiceResolver _scriptPluginServiceResolver;
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands, public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration, ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory, IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IGameServerInstanceFactory serverInstanceFactory,
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents, IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService, IEventHandler eventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory, IMetaService metaService,
IMetaRegistration metaRegistration) IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver)
{ {
MiddlewareActionHandler = actionHandler; MiddlewareActionHandler = actionHandler;
_servers = new ConcurrentBag<Server>(); _servers = new ConcurrentBag<Server>();
@ -100,6 +101,7 @@ namespace IW4MAdmin.Application
_eventHandler = eventHandler; _eventHandler = eventHandler;
_scriptCommandFactory = scriptCommandFactory; _scriptCommandFactory = scriptCommandFactory;
_metaRegistration = metaRegistration; _metaRegistration = metaRegistration;
_scriptPluginServiceResolver = scriptPluginServiceResolver;
Plugins = plugins; Plugins = plugins;
} }
@ -277,12 +279,12 @@ namespace IW4MAdmin.Application
{ {
if (plugin is ScriptPlugin scriptPlugin) if (plugin is ScriptPlugin scriptPlugin)
{ {
await scriptPlugin.Initialize(this, _scriptCommandFactory); await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver);
scriptPlugin.Watcher.Changed += async (sender, e) => scriptPlugin.Watcher.Changed += async (sender, e) =>
{ {
try try
{ {
await scriptPlugin.Initialize(this, _scriptCommandFactory); await scriptPlugin.Initialize(this, _scriptCommandFactory, _scriptPluginServiceResolver);
} }
catch (Exception ex) catch (Exception ex)
@ -449,7 +451,8 @@ namespace IW4MAdmin.Application
Name = cmd.Name, Name = cmd.Name,
Alias = cmd.Alias, Alias = cmd.Alias,
MinimumPermission = cmd.Permission, MinimumPermission = cmd.Permission,
AllowImpersonation = cmd.AllowImpersonation AllowImpersonation = cmd.AllowImpersonation,
SupportedGames = cmd.SupportedGames
}); });
} }

View File

@ -255,6 +255,7 @@ namespace IW4MAdmin.Application.EventParsers
ClientNumber = Convert.ToInt32(match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()), ClientNumber = Convert.ToInt32(match.Values[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginClientNumber]].ToString()),
State = EFClient.ClientState.Connecting, State = EFClient.ClientState.Connecting,
}, },
Extra = originIdString,
RequiredEntity = GameEvent.EventRequiredEntity.None, RequiredEntity = GameEvent.EventRequiredEntity.None,
IsBlocking = true, IsBlocking = true,
GameTime = gameTime, GameTime = gameTime,

View File

@ -816,6 +816,7 @@ namespace IW4MAdmin
Origin = client, Origin = client,
Owner = this, Owner = this,
IsBlocking = true, IsBlocking = true,
Extra = client.GetAdditionalProperty<string>("BotGuid"),
Source = GameEvent.EventSource.Status Source = GameEvent.EventSource.Status
}; };

View File

@ -249,6 +249,7 @@ namespace IW4MAdmin.Application
.AddSingleton<IEntityService<EFClient>, ClientService>() .AddSingleton<IEntityService<EFClient>, ClientService>()
.AddSingleton<IMetaService, MetaService>() .AddSingleton<IMetaService, MetaService>()
.AddSingleton<IMetaRegistration, MetaRegistration>() .AddSingleton<IMetaRegistration, MetaRegistration>()
.AddSingleton<IScriptPluginServiceResolver, ScriptPluginServiceResolver>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>, ReceivedPenaltyResourceQueryHelper>() .AddSingleton<IResourceQueryHelper<ClientPaginationRequest, ReceivedPenaltyResponse>, ReceivedPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>, AdministeredPenaltyResourceQueryHelper>() .AddSingleton<IResourceQueryHelper<ClientPaginationRequest, AdministeredPenaltyResponse>, AdministeredPenaltyResourceQueryHelper>()
.AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>, UpdatedAliasResourceQueryHelper>() .AddSingleton<IResourceQueryHelper<ClientPaginationRequest, UpdatedAliasResponse>, UpdatedAliasResourceQueryHelper>()

View File

@ -61,7 +61,7 @@ namespace IW4MAdmin.Application.Misc
_onProcessing.Dispose(); _onProcessing.Dispose();
} }
public async Task Initialize(IManager manager, IScriptCommandFactory scriptCommandFactory) public async Task Initialize(IManager manager, IScriptCommandFactory scriptCommandFactory, IScriptPluginServiceResolver serviceResolver)
{ {
await _onProcessing.WaitAsync(); await _onProcessing.WaitAsync();
@ -114,6 +114,7 @@ namespace IW4MAdmin.Application.Misc
_scriptEngine.Execute(script); _scriptEngine.Execute(script);
_scriptEngine.SetValue("_localization", Utilities.CurrentLocalization); _scriptEngine.SetValue("_localization", Utilities.CurrentLocalization);
_scriptEngine.SetValue("_serviceResolver", serviceResolver);
dynamic pluginObject = _scriptEngine.GetValue("plugin").ToObject(); dynamic pluginObject = _scriptEngine.GetValue("plugin").ToObject();
Author = pluginObject.author; Author = pluginObject.author;
@ -164,6 +165,11 @@ namespace IW4MAdmin.Application.Misc
successfullyLoaded = true; successfullyLoaded = true;
} }
catch (JavaScriptException ex)
{
throw new PluginException($"An error occured while initializing script plugin: {ex.Error} (Line: {ex.Location.Start.Line}, Character: {ex.Location.Start.Column})") { PluginFile = _fileName };
}
catch catch
{ {
throw; throw;

View File

@ -0,0 +1,31 @@
using SharedLibraryCore.Interfaces;
using System;
using System.Linq;
namespace IW4MAdmin.Application.Misc
{
/// <summary>
/// implementation of IScriptPluginServiceResolver
/// </summary>
public class ScriptPluginServiceResolver : IScriptPluginServiceResolver
{
private readonly IServiceProvider _serviceProvider;
public ScriptPluginServiceResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public object ResolveService(string serviceName)
{
var serviceType = typeof(IScriptPluginServiceResolver).Assembly.GetTypes().FirstOrDefault(_type => _type.Name == serviceName);
if (serviceType == null)
{
throw new InvalidOperationException($"No service type '{serviceName}' defined in IW4MAdmin assembly");
}
return _serviceProvider.GetService(serviceType);
}
}
}

View File

@ -203,10 +203,11 @@ namespace IW4MAdmin.Application.RconParsers
long networkId; long networkId;
string name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine(); string name = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].TrimNewLine();
string networkIdString;
try try
{ {
string networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]]; networkIdString = match.Values[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]];
networkId = networkIdString.IsBotGuid() ? networkId = networkIdString.IsBotGuid() ?
name.GenerateGuidFromString() : name.GenerateGuidFromString() :
@ -234,6 +235,8 @@ namespace IW4MAdmin.Application.RconParsers
State = EFClient.ClientState.Connecting State = EFClient.ClientState.Connecting
}; };
client.SetAdditionalProperty("BotGuid", networkIdString);
StatusPlayers.Add(client); StatusPlayers.Add(client);
} }
} }

View File

@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
GameFiles\IW4x\userraw\scripts\_commands.gsc = GameFiles\IW4x\userraw\scripts\_commands.gsc GameFiles\IW4x\userraw\scripts\_commands.gsc = GameFiles\IW4x\userraw\scripts\_commands.gsc
GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc = GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc = GameFiles\IW4x\userraw\scripts\_customcallbacks.gsc
PostPublish.ps1 = PostPublish.ps1 PostPublish.ps1 = PostPublish.ps1
pre-release-pipeline.yml = pre-release-pipeline.yml
README.md = README.md README.md = README.md
RunPublishPre.cmd = RunPublishPre.cmd RunPublishPre.cmd = RunPublishPre.cmd
RunPublishRelease.cmd = RunPublishRelease.cmd RunPublishRelease.cmd = RunPublishRelease.cmd

View File

@ -74,14 +74,14 @@ namespace LiveRadar.Web.Controllers
[Route("Radar/Update")] [Route("Radar/Update")]
public IActionResult Update(string payload) public IActionResult Update(string payload)
{ {
var radarUpdate = RadarEvent.Parse(payload); /*var radarUpdate = RadarEvent.Parse(payload);
var client = _manager.GetActiveClients().FirstOrDefault(_client => _client.NetworkId == radarUpdate.Guid); var client = _manager.GetActiveClients().FirstOrDefault(_client => _client.NetworkId == radarUpdate.Guid);
if (client != null) if (client != null)
{ {
radarUpdate.Name = client.Name.StripColors(); radarUpdate.Name = client.Name.StripColors();
client.SetAdditionalProperty("LiveRadar", radarUpdate); client.SetAdditionalProperty("LiveRadar", radarUpdate);
} }*/
return Ok(); return Ok();
} }

View File

@ -0,0 +1,33 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using EventGeneratorCallback = System.ValueTuple<string, string,
System.Func<string, SharedLibraryCore.Interfaces.IEventParserConfiguration,
SharedLibraryCore.GameEvent,
SharedLibraryCore.GameEvent>>;
namespace LiveRadar.Events
{
public class Script : IRegisterEvent
{
private const string EVENT_LIVERADAR = "LiveRadar";
private EventGeneratorCallback LiveRadar()
{
return (EVENT_LIVERADAR, EVENT_LIVERADAR, (string eventLine, IEventParserConfiguration config, GameEvent autoEvent) =>
{
string[] lineSplit = eventLine.Split(";");
autoEvent.Type = GameEvent.EventType.Other;
autoEvent.Subtype = EVENT_LIVERADAR;
autoEvent.Origin = new EFClient() { NetworkId = 0 };
autoEvent.Extra = lineSplit[1]; // guid
return autoEvent;
}
);
}
public IEnumerable<EventGeneratorCallback> Events => new[] { LiveRadar() };
}
}

View File

@ -3,6 +3,7 @@ using SharedLibraryCore;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -17,12 +18,14 @@ namespace LiveRadar
public string Author => "RaidMax"; public string Author => "RaidMax";
private readonly IConfigurationHandler<LiveRadarConfiguration> _configurationHandler; private readonly IConfigurationHandler<LiveRadarConfiguration> _configurationHandler;
private readonly Dictionary<string, long> _botGuidLookups;
private bool addedPage; private bool addedPage;
private readonly object lockObject = new object(); private readonly object lockObject = new object();
public Plugin(IConfigurationHandlerFactory configurationHandlerFactory) public Plugin(IConfigurationHandlerFactory configurationHandlerFactory)
{ {
_configurationHandler = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration"); _configurationHandler = configurationHandlerFactory.GetConfigurationHandler<LiveRadarConfiguration>("LiveRadarConfiguration");
_botGuidLookups = new Dictionary<string, long>();
} }
public Task OnEventAsync(GameEvent E, Server S) public Task OnEventAsync(GameEvent E, Server S)
@ -41,28 +44,45 @@ namespace LiveRadar
} }
} }
if (E.Type == GameEvent.EventType.Unknown) if (E.Type == GameEvent.EventType.PreConnect && E.Origin.IsBot)
{ {
if (E.Data?.StartsWith("LiveRadar") ?? false) string botKey = $"BotGuid_{E.Extra}";
lock (lockObject)
{ {
try if (!_botGuidLookups.ContainsKey(botKey))
{ {
var radarUpdate = RadarEvent.Parse(E.Data); _botGuidLookups.Add(botKey, E.Origin.NetworkId);
var client = S.Manager.GetActiveClients().FirstOrDefault(_client => _client.NetworkId == radarUpdate.Guid); }
}
}
if (client != null) if (E.Type == GameEvent.EventType.Other && E.Subtype == "LiveRadar")
{ {
radarUpdate.Name = client.Name.StripColors(); try
client.SetAdditionalProperty("LiveRadar", radarUpdate); {
} string botKey = $"BotGuid_{E.Extra}";
long generatedBotGuid;
lock (lockObject)
{
generatedBotGuid = _botGuidLookups.ContainsKey(botKey) ? _botGuidLookups[botKey] : 0;
} }
catch (Exception e) var radarUpdate = RadarEvent.Parse(E.Data, generatedBotGuid);
var client = S.Manager.GetActiveClients().FirstOrDefault(_client => _client.NetworkId == radarUpdate.Guid);
if (client != null)
{ {
S.Logger.WriteWarning($"Could not parse live radar output: {e.Data}"); radarUpdate.Name = client.Name.StripColors();
S.Logger.WriteDebug(e.GetExceptionInfo()); client.SetAdditionalProperty("LiveRadar", radarUpdate);
} }
} }
catch (Exception e)
{
S.Logger.WriteWarning($"Could not parse live radar output: {e.Data}");
S.Logger.WriteDebug(e.GetExceptionInfo());
}
} }
return Task.CompletedTask; return Task.CompletedTask;

View File

@ -1,9 +1,7 @@
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Helpers; using SharedLibraryCore.Helpers;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
namespace LiveRadar namespace LiveRadar
{ {
@ -39,13 +37,13 @@ namespace LiveRadar
return false; return false;
} }
public static RadarEvent Parse(string input) public static RadarEvent Parse(string input, long generatedBotGuid)
{ {
var items = input.Split(';').Skip(1).ToList(); var items = input.Split(';').Skip(1).ToList();
var parsedEvent = new RadarEvent() var parsedEvent = new RadarEvent()
{ {
Guid = items[0].ConvertGuidToLong(System.Globalization.NumberStyles.HexNumber), Guid = generatedBotGuid,
Location = Vector3.Parse(items[1]), Location = Vector3.Parse(items[1]),
ViewAngles = Vector3.Parse(items[2]).FixIW4Angles(), ViewAngles = Vector3.Parse(items[2]).FixIW4Angles(),
Team = items[3], Team = items[3],

View File

@ -44,6 +44,8 @@ let plugin = {
}, },
onLoadAsync: function (manager) { onLoadAsync: function (manager) {
this.logger = _serviceResolver.ResolveService("ILogger");
this.logger.WriteDebug("sample plugin loaded");
}, },
onUnloadAsync: function () { onUnloadAsync: function () {

View File

@ -1232,6 +1232,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
return 886229536; return 886229536;
} }
// todo: this is not stable and will need to be migrated again...
long id = HashCode.Combine(server.IP, server.Port); long id = HashCode.Combine(server.IP, server.Port);
id = id < 0 ? Math.Abs(id) : id; id = id < 0 ? Math.Abs(id) : id;
long? serverId; long? serverId;

View File

@ -5,6 +5,7 @@ using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration; using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {
@ -13,7 +14,7 @@ namespace SharedLibraryCore
/// </summary> /// </summary>
public abstract class Command : IManagerCommand public abstract class Command : IManagerCommand
{ {
private readonly CommandConfiguration _config; protected readonly CommandConfiguration _config;
protected readonly ITranslationLookup _translationLookup; protected readonly ITranslationLookup _translationLookup;
protected ILogger logger; protected ILogger logger;
@ -113,6 +114,25 @@ namespace SharedLibraryCore
} }
private EFClient.Permission permission; private EFClient.Permission permission;
public Game[] SupportedGames
{
get => supportedGames;
protected set
{
try
{
var savedGames = _config?.Commands[GetType().Name].SupportedGames;
supportedGames = savedGames?.Length != 0 ? savedGames : value;
}
catch (KeyNotFoundException)
{
supportedGames = value;
}
}
}
private Game[] supportedGames;
/// <summary> /// <summary>
/// Argument list for the command /// Argument list for the command

View File

@ -0,0 +1,37 @@
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System;
using System.Linq;
using System.Threading.Tasks;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore.Commands
{
public class PrivateMessageAdminsCommand : Command
{
public PrivateMessageAdminsCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
Name = "privatemessageadmin";
Description = lookup["COMMANDS_PMADMINS_DESC"];
Alias = "pma";
Permission = EFClient.Permission.Moderator;
SupportedGames = new[] { Game.IW4, Game.IW5 };
}
public override Task ExecuteAsync(GameEvent E)
{
bool isGameSupported = _config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames.Length > 0 &&
_config.Commands[nameof(PrivateMessageAdminsCommand)].SupportedGames.Contains(E.Owner.GameName);
if (!isGameSupported)
{
E.Origin.Tell(_translationLookup["COMMANDS_GAME_NOT_SUPPORTED"].FormatExt(nameof(PrivateMessageAdminsCommand)));
return Task.CompletedTask;
}
E.Owner.ToAdmins(E.Data);
return Task.CompletedTask;
}
}
}

View File

@ -1,6 +1,7 @@
using Newtonsoft.Json.Converters; using Newtonsoft.Json;
using System.Text.Json.Serialization; using Newtonsoft.Json.Converters;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore.Configuration namespace SharedLibraryCore.Configuration
{ {
@ -29,5 +30,11 @@ namespace SharedLibraryCore.Configuration
/// Indicates if the command can be run by another user (impersonation) /// Indicates if the command can be run by another user (impersonation)
/// </summary> /// </summary>
public bool AllowImpersonation { get; set; } public bool AllowImpersonation { get; set; }
/// <summary>
/// Specifies the games supporting the functionality of the command
/// </summary>
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
public Game[] SupportedGames { get; set; } = new Game[0];
} }
} }

View File

@ -1,6 +1,4 @@
using System; namespace SharedLibraryCore.Dtos.Meta.Responses
namespace SharedLibraryCore.Dtos.Meta.Responses
{ {
public class UpdatedAliasResponse : BaseMetaResponse public class UpdatedAliasResponse : BaseMetaResponse
{ {
@ -17,6 +15,6 @@ namespace SharedLibraryCore.Dtos.Meta.Responses
return false; return false;
} }
public override int GetHashCode() => HashCode.Combine(Name.StripColors(), IPAddress); public override int GetHashCode() => $"{Name.StripColors()}{IPAddress}".GetStableHashCode();
} }
} }

View File

@ -1,5 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using static SharedLibraryCore.Database.Models.EFClient; using static SharedLibraryCore.Database.Models.EFClient;
using static SharedLibraryCore.Server;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
@ -35,6 +36,11 @@ namespace SharedLibraryCore.Interfaces
/// </summary> /// </summary>
Permission Permission { get; } Permission Permission { get; }
/// <summary>
/// Games the command is supported on
/// </summary>
Game[] SupportedGames { get; }
/// <summary> /// <summary>
/// Syntax for using the command /// Syntax for using the command
/// </summary> /// </summary>

View File

@ -0,0 +1,10 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// interface used to dynamically resolve services by string name
/// </summary>
public interface IScriptPluginServiceResolver
{
object ResolveService(string serviceName);
}
}

View File

@ -369,7 +369,27 @@ namespace SharedLibraryCore
/// </summary> /// </summary>
/// <param name="value">value string</param> /// <param name="value">value string</param>
/// <returns></returns> /// <returns></returns>
public static long GenerateGuidFromString(this string value) => string.IsNullOrEmpty(value) ? -1 : HashCode.Combine(value.StripColors()); public static long GenerateGuidFromString(this string value) => string.IsNullOrEmpty(value) ? -1 : GetStableHashCode(value.StripColors());
/// https://stackoverflow.com/questions/36845430/persistent-hashcode-for-strings
public static int GetStableHashCode(this string str)
{
unchecked
{
int hash1 = 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length && str[i] != '\0'; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1 || str[i + 1] == '\0')
break;
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return hash1 + (hash2 * 1566083941);
}
}
public static int? ConvertToIP(this string str) public static int? ConvertToIP(this string str)
{ {
@ -773,6 +793,8 @@ namespace SharedLibraryCore
byte[] bytes = toTest.GetAddressBytes(); byte[] bytes = toTest.GetAddressBytes();
switch (bytes[0]) switch (bytes[0])
{ {
case 0:
return bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0;
case 10: case 10:
return true; return true;
case 172: case 172:
@ -954,7 +976,7 @@ namespace SharedLibraryCore
/// <summary> /// <summary>
/// wrapper method for humanizee that uses current current culture /// wrapper method for humanizee that uses current current culture
/// </summary> /// </summary>
public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, TimeUnit maxUnit = TimeUnit.Week, public static string HumanizeForCurrentCulture(this TimeSpan timeSpan, int precision = 1, TimeUnit maxUnit = TimeUnit.Week,
TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false) TimeUnit minUnit = TimeUnit.Millisecond, string collectionSeparator = ", ", bool toWords = false)
{ {
return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, toWords); return timeSpan.Humanize(precision, CurrentLocalization.Culture, maxUnit, minUnit, collectionSeparator, toWords);

View File

@ -537,5 +537,46 @@ namespace ApplicationTests
Assert.IsTrue(result.IsBroadcast); Assert.IsTrue(result.IsBroadcast);
} }
#endregion #endregion
#region PMADMINS
[Test]
public async Task Test_PrivateMessageAdmins_HappyPath()
{
var cmd = new PrivateMessageAdminsCommand(cmdConfig, transLookup);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var origin = ClientGenerators.CreateDatabaseClient();
origin.Level = Permission.Administrator;
origin.CurrentServer = server;
var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "", server);
cmdConfig.Commands.Add(nameof(PrivateMessageAdminsCommand), new CommandProperties { SupportedGames = new[] { server.GameName } });
server.Clients[0] = origin;
server.Clients[1] = origin;
await cmd.ExecuteAsync(gameEvent);
int expectedEvents = 2;
Assert.AreEqual(expectedEvents, mockEventHandler.Events.Count(_event => _event.Type == GameEvent.EventType.Tell));
}
[Test]
public async Task Test_PrivateMessageAdmins_GameNotSupported()
{
var cmd = new PrivateMessageAdminsCommand(cmdConfig, transLookup);
var server = serviceProvider.GetRequiredService<IW4MServer>();
var origin = ClientGenerators.CreateDatabaseClient();
origin.Level = Permission.Administrator;
origin.CurrentServer = server;
var gameEvent = EventGenerators.GenerateEvent(GameEvent.EventType.Command, "", server);
gameEvent.Origin = origin;
cmdConfig.Commands.Add(nameof(PrivateMessageAdminsCommand), new CommandProperties());
server.Clients[0] = origin;
server.Clients[1] = origin;
await cmd.ExecuteAsync(gameEvent);
int expectedEvents = 1;
Assert.AreEqual(expectedEvents, mockEventHandler.Events.Count(_event => _event.Type == GameEvent.EventType.Tell));
}
#endregion
} }
} }

View File

@ -2,12 +2,7 @@ trigger:
batch: true batch: true
branches: branches:
include: include:
- releases/* - release/pre
- 2.4-pr
paths:
exclude:
- azure-pipelines.yml
- Master/*
pr: none pr: none