Compare commits
48 Commits
release/pr
...
2022.01.24
Author | SHA1 | Date | |
---|---|---|---|
|
8c71278e0a | ||
|
1dfc07369a | ||
|
c8775b2fb6 | ||
|
4ea989dbb6 | ||
|
94e396c575 | ||
|
83ab1b1a0a | ||
|
3c53bb17ba | ||
|
e6a055a18c | ||
|
604aa79249 | ||
|
971a53bb03 | ||
|
e2affb7635 | ||
|
cd37bc5c11 | ||
|
26228e35fd | ||
|
e1b81eab93 | ||
|
b3c534f3e2 | ||
|
e355992d53 | ||
|
18d5b11be1 | ||
|
8fd0bd0dac | ||
|
f03666c98d | ||
|
8baa85c7c0 | ||
|
ecfcd760d6 | ||
|
8af40a7772 | ||
|
697a1884cb | ||
|
4899ef86cc | ||
|
794e1ee87b | ||
|
b025115cf8 | ||
|
f7c3db3712 | ||
|
f32ac3f45e | ||
|
3716255740 | ||
|
d36e19a077 | ||
|
12cc5f1820 | ||
|
61a131be9d | ||
|
d93bfc11d0 | ||
|
21f290ca58 | ||
|
d3df9623aa | ||
|
1b9ca676dc | ||
|
58616e18fe | ||
|
0dcbafd0f2 | ||
|
f8723e6a8c | ||
|
3ebdbde33d | ||
|
769faaa31b | ||
|
2734a3f138 | ||
|
914b37b20a | ||
|
755c149495 | ||
|
bbcbc4c042 | ||
|
f3bead8eb5 | ||
|
7eb45f2bc9 | ||
|
5837885653 |
@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
public int Uptime { get; set; }
|
public int Uptime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifices the version of the instance
|
/// Specifies the version of the instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("version")]
|
[JsonProperty("version")]
|
||||||
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
||||||
@ -33,5 +33,11 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("servers")]
|
[JsonProperty("servers")]
|
||||||
public List<ApiServer> Servers { get; set; }
|
public List<ApiServer> Servers { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Url IW4MAdmin is listening on
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("webfront_url")]
|
||||||
|
public string WebfrontUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@ -26,6 +27,7 @@ using IW4MAdmin.Application.Migration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
|
using SharedLibraryCore.Formatting;
|
||||||
using static SharedLibraryCore.GameEvent;
|
using static SharedLibraryCore.GameEvent;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using ObsoleteLogger = SharedLibraryCore.Interfaces.ILogger;
|
using ObsoleteLogger = SharedLibraryCore.Interfaces.ILogger;
|
||||||
@ -217,6 +219,8 @@ namespace IW4MAdmin.Application
|
|||||||
return _commands;
|
return _commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<IManagerCommand> Commands => _commands.ToImmutableList();
|
||||||
|
|
||||||
public async Task UpdateServerStates()
|
public async Task UpdateServerStates()
|
||||||
{
|
{
|
||||||
// store the server hash code and task for it
|
// store the server hash code and task for it
|
||||||
@ -452,6 +456,17 @@ namespace IW4MAdmin.Application
|
|||||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||||
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(_appConfig.CustomParserEncoding) ? _appConfig.CustomParserEncoding : "windows-1252");
|
Utilities.EncodingType = Encoding.GetEncoding(!string.IsNullOrEmpty(_appConfig.CustomParserEncoding) ? _appConfig.CustomParserEncoding : "windows-1252");
|
||||||
|
|
||||||
|
foreach (var parser in AdditionalRConParsers)
|
||||||
|
{
|
||||||
|
if (!parser.Configuration.ColorCodeMapping.ContainsKey(ColorCodes.Accent.ToString()))
|
||||||
|
{
|
||||||
|
parser.Configuration.ColorCodeMapping.Add(ColorCodes.Accent.ToString(),
|
||||||
|
parser.Configuration.ColorCodeMapping.TryGetValue(_appConfig.IngameAccentColorKey, out var colorCode)
|
||||||
|
? colorCode
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region COMMANDS
|
#region COMMANDS
|
||||||
@ -477,13 +492,17 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
// this is because I want to store the command prefix in IW4MAdminSettings, but can't easily
|
// 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
|
// inject it to all the places that need it
|
||||||
cmdConfig.CommandPrefix = _appConfig.CommandPrefix;
|
cmdConfig.CommandPrefix = _appConfig?.CommandPrefix ?? "!";
|
||||||
cmdConfig.BroadcastCommandPrefix = _appConfig.BroadcastCommandPrefix;
|
cmdConfig.BroadcastCommandPrefix = _appConfig?.BroadcastCommandPrefix ?? "@";
|
||||||
|
|
||||||
foreach (var cmd in commandsToAddToConfig)
|
foreach (var cmd in commandsToAddToConfig)
|
||||||
{
|
{
|
||||||
|
if (cmdConfig.Commands.ContainsKey(cmd.CommandConfigNameForType()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
cmdConfig.Commands.Add(cmd.CommandConfigNameForType(),
|
cmdConfig.Commands.Add(cmd.CommandConfigNameForType(),
|
||||||
new CommandProperties()
|
new CommandProperties
|
||||||
{
|
{
|
||||||
Name = cmd.Name,
|
Name = cmd.Name,
|
||||||
Alias = cmd.Alias,
|
Alias = cmd.Alias,
|
||||||
@ -605,6 +624,11 @@ namespace IW4MAdmin.Application
|
|||||||
return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList();
|
return _servers.SelectMany(s => s.Clients).ToList().Where(p => p != null).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EFClient FindActiveClient(EFClient client) =>client.ClientNumber < 0 ?
|
||||||
|
GetActiveClients()
|
||||||
|
.FirstOrDefault(c => c.NetworkId == client.NetworkId) ?? client :
|
||||||
|
client;
|
||||||
|
|
||||||
public ClientService GetClientService()
|
public ClientService GetClientService()
|
||||||
{
|
{
|
||||||
return ClientSvc;
|
return ClientSvc;
|
||||||
|
59
Application/Commands/FindPlayerCommand.cs
Normal file
59
Application/Commands/FindPlayerCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Finds player by name
|
||||||
|
/// </summary>
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
Application/Commands/HelpCommand.cs
Normal file
93
Application/Commands/HelpCommand.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prints help information
|
||||||
|
/// </summary>
|
||||||
|
public class HelpCommand : Command
|
||||||
|
{
|
||||||
|
public HelpCommand(CommandConfiguration config, ITranslationLookup translationLookup) :
|
||||||
|
base(config, translationLookup)
|
||||||
|
{
|
||||||
|
Name = "help";
|
||||||
|
Description = translationLookup["COMMANDS_HELP_DESC"];
|
||||||
|
Alias = "h";
|
||||||
|
Permission = EFClient.Permission.User;
|
||||||
|
RequiresTarget = false;
|
||||||
|
Arguments = new[]
|
||||||
|
{
|
||||||
|
new CommandArgument
|
||||||
|
{
|
||||||
|
Name = translationLookup["COMMANDS_ARGS_COMMANDS"],
|
||||||
|
Required = false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task ExecuteAsync(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
var searchTerm = gameEvent.Data.Trim();
|
||||||
|
var availableCommands = gameEvent.Owner.Manager.Commands.Distinct().Where(command =>
|
||||||
|
command.SupportedGames == null || !command.SupportedGames.Any() ||
|
||||||
|
command.SupportedGames.Contains(gameEvent.Owner.GameName))
|
||||||
|
.Where(command => gameEvent.Origin.Level >= command.Permission);
|
||||||
|
|
||||||
|
if (searchTerm.Length > 2)
|
||||||
|
{
|
||||||
|
var matchingCommand = availableCommands.FirstOrDefault(command =>
|
||||||
|
command.Name.Equals(searchTerm, StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
command.Alias.Equals(searchTerm, StringComparison.InvariantCultureIgnoreCase));
|
||||||
|
|
||||||
|
if (matchingCommand != null)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_HELP_SEARCH_RESULT"]
|
||||||
|
.FormatExt(matchingCommand.Name, matchingCommand.Alias));
|
||||||
|
gameEvent.Origin.Tell(matchingCommand.Syntax);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_HELP_NOTFOUND"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var commandStrings = availableCommands.Select((command, index) =>
|
||||||
|
new
|
||||||
|
{
|
||||||
|
response = $" {_translationLookup["COMMANDS_HELP_LIST_FORMAT"].FormatExt(command.Name)} ",
|
||||||
|
index
|
||||||
|
});
|
||||||
|
|
||||||
|
var helpResponse = new StringBuilder();
|
||||||
|
|
||||||
|
foreach (var item in commandStrings)
|
||||||
|
{
|
||||||
|
helpResponse.Append(item.response);
|
||||||
|
if (item.index == 0 || item.index % 4 != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameEvent.Origin.Tell(helpResponse.ToString());
|
||||||
|
helpResponse = new StringBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
gameEvent.Origin.Tell(helpResponse.ToString());
|
||||||
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_HELP_MOREINFO"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
Application/Commands/ListAdminsCommand.cs
Normal file
50
Application/Commands/ListAdminsCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists all unmasked admins
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
57
Application/Commands/ListAliasesCommand.cs
Normal file
57
Application/Commands/ListAliasesCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists alises of specified client
|
||||||
|
/// </summary>
|
||||||
|
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<string>(gameEvent.Target.AliasLink.Children.Select(a => a.Name));
|
||||||
|
var ips = new List<string>(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
37
Application/Commands/ListClientsCommand.cs
Normal file
37
Application/Commands/ListClientsCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List online clients
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
Application/Commands/ListPluginsCommand.cs
Normal file
41
Application/Commands/ListPluginsCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Lists the loaded plugins
|
||||||
|
/// </summary>
|
||||||
|
public class ListPluginsCommand : Command
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IPlugin> _plugins;
|
||||||
|
|
||||||
|
public ListPluginsCommand(CommandConfiguration config, ITranslationLookup translationLookup,
|
||||||
|
IEnumerable<IPlugin> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
Application/Commands/ListReportsCommand.cs
Normal file
59
Application/Commands/ListReportsCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List all reports on the server
|
||||||
|
/// </summary>
|
||||||
|
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<Report>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
128
Application/Commands/MapAndGameTypeCommand.cs
Normal file
128
Application/Commands/MapAndGameTypeCommand.cs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Data.Models.Client;
|
||||||
|
using IW4MAdmin.Application.Extensions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Commands;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Commands
|
||||||
|
{
|
||||||
|
public class MapAndGameTypeCommand : Command
|
||||||
|
{
|
||||||
|
private const string ArgumentRegexPattern = "(?:\"([^\"]+)\"|([^\\s]+)) (?:\"([^\"]+)\"|([^\\s]+))";
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly DefaultSettings _defaultSettings;
|
||||||
|
|
||||||
|
public MapAndGameTypeCommand(ILogger<MapAndGameTypeCommand> logger, CommandConfiguration config,
|
||||||
|
DefaultSettings defaultSettings, ITranslationLookup layout) : base(config, layout)
|
||||||
|
{
|
||||||
|
Name = "mapandgametype";
|
||||||
|
Description = _translationLookup["COMMANDS_MAG_DESCRIPTION"];
|
||||||
|
Alias = "mag";
|
||||||
|
Permission = EFClient.Permission.Administrator;
|
||||||
|
RequiresTarget = false;
|
||||||
|
Arguments = new[]
|
||||||
|
{
|
||||||
|
new CommandArgument
|
||||||
|
{
|
||||||
|
Name = _translationLookup["COMMADS_MAG_ARG_1"],
|
||||||
|
Required = true
|
||||||
|
},
|
||||||
|
new CommandArgument
|
||||||
|
{
|
||||||
|
Name = _translationLookup["COMMADS_MAG_ARG_2"],
|
||||||
|
Required = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_logger = logger;
|
||||||
|
_defaultSettings = defaultSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
var match = Regex.Match(gameEvent.Data.Trim(), ArgumentRegexPattern,
|
||||||
|
RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(Syntax);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string map;
|
||||||
|
string gametype;
|
||||||
|
|
||||||
|
if (match.Groups.Count > 3)
|
||||||
|
{
|
||||||
|
map = match.Groups[2].ToString();
|
||||||
|
gametype = match.Groups[4].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
map = match.Groups[1].ToString();
|
||||||
|
gametype = match.Groups[3].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingMaps = gameEvent.Owner.FindMap(map);
|
||||||
|
var matchingGametypes = _defaultSettings.FindGametype(gametype, gameEvent.Owner.GameName);
|
||||||
|
|
||||||
|
if (matchingMaps.Count > 1)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_MAG_MULTIPLE_MAPS"]);
|
||||||
|
|
||||||
|
foreach (var matchingMap in matchingMaps)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(
|
||||||
|
$"[(Color::Yellow){matchingMap.Alias}(Color::White)] [(Color::Yellow){matchingMap.Name}(Color::White)]");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingGametypes.Count > 1)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_MAG_MULTIPLE_GAMETYPES"]);
|
||||||
|
|
||||||
|
foreach (var matchingGametype in matchingGametypes)
|
||||||
|
{
|
||||||
|
gameEvent.Origin.Tell(
|
||||||
|
$"[(Color::Yellow){matchingGametype.Alias}(Color::White)] [(Color::Yellow){matchingGametype.Name}(Color::White)]");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
map = matchingMaps.FirstOrDefault()?.Name ?? map;
|
||||||
|
gametype = matchingGametypes.FirstOrDefault()?.Name ?? gametype;
|
||||||
|
var hasMatchingGametype = matchingGametypes.Any();
|
||||||
|
|
||||||
|
_logger.LogDebug("Changing map to {Map} and gametype {Gametype}", map, gametype);
|
||||||
|
|
||||||
|
await gameEvent.Owner.SetDvarAsync("g_gametype", gametype);
|
||||||
|
gameEvent.Owner.Broadcast(_translationLookup["COMMANDS_MAP_SUCCESS"].FormatExt(map));
|
||||||
|
await Task.Delay(gameEvent.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds);
|
||||||
|
|
||||||
|
switch (gameEvent.Owner.GameName)
|
||||||
|
{
|
||||||
|
case Server.Game.IW5:
|
||||||
|
await gameEvent.Owner.ExecuteCommandAsync(
|
||||||
|
$"load_dsr {(hasMatchingGametype ? gametype.ToUpper() + "_default" : gametype)}");
|
||||||
|
await gameEvent.Owner.ExecuteCommandAsync($"map {map}");
|
||||||
|
break;
|
||||||
|
case Server.Game.T6:
|
||||||
|
await gameEvent.Owner.ExecuteCommandAsync($"exec {gametype}.cfg");
|
||||||
|
await gameEvent.Owner.ExecuteCommandAsync($"map {map}");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await gameEvent.Owner.ExecuteCommandAsync($"map {map}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
Application/Commands/PrivateMessageCommand.cs
Normal file
45
Application/Commands/PrivateMessageCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sends a private message to another player
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
77
Application/Commands/ReportClientCommand.cs
Normal file
77
Application/Commands/ReportClientCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Report client for given reason
|
||||||
|
/// </summary>
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
Application/Commands/SayAllCommand.cs
Normal file
46
Application/Commands/SayAllCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prints out a message to all clients on all servers
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
Application/Commands/SayCommand.cs
Normal file
42
Application/Commands/SayCommand.cs
Normal file
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prints out a message to all clients on the server
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
Application/Commands/WhoAmICommand.cs
Normal file
38
Application/Commands/WhoAmICommand.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Data.Models.Client;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Commands
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prints client information
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,13 @@
|
|||||||
"rollingInterval": "Day",
|
"rollingInterval": "Day",
|
||||||
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}"
|
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Console",
|
||||||
|
"Args": {
|
||||||
|
"outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff} {Server} {Level:u3}] {Message:lj}{NewLine}{Exception}",
|
||||||
|
"RestrictedToMinimumLevel": "Fatal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Enrich": [
|
"Enrich": [
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"AutoMessagePeriod": 60,
|
"AutoMessagePeriod": 60,
|
||||||
"AutoMessages": [
|
"AutoMessages": [
|
||||||
"This server uses ^5IW4M Admin v{{VERSION}} ^7get it at ^5raidmax.org/IW4MAdmin",
|
"This server uses (Color::Accent)IW4M Admin v{{VERSION}} (Color::White)get it at (Color::Accent)raidmax.org/IW4MAdmin",
|
||||||
"^5IW4M Admin ^7sees ^5YOU!",
|
"(Color::Accent)IW4M Admin (Color::White)sees (Color::Accent)YOU!",
|
||||||
"{{TOPSTATS}}",
|
"{{TOPSTATS}}",
|
||||||
"This server has seen a total of ^5{{TOTALPLAYERS}} ^7players!",
|
"This server has seen a total of (Color::Accent){{TOTALPLAYERS}} (Color::White)players!",
|
||||||
"Cheaters are ^1unwelcome ^7 on this server",
|
"Cheaters are (Color::Red)unwelcome (Color::White)on this server",
|
||||||
"Did you know 8/10 people agree with unverified statistics?"
|
"Did you know 8/10 people agree with unverified statistics?"
|
||||||
],
|
],
|
||||||
"GlobalRules": [
|
"GlobalRules": [
|
||||||
@ -68,6 +68,504 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Game": "IW4",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "arena",
|
||||||
|
"Alias": "Arena"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dd",
|
||||||
|
"Alias": "Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gtnw",
|
||||||
|
"Alias": "Global Thermo-Nuclear War"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "oneflag",
|
||||||
|
"Alias": "One-Flag CTF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sab",
|
||||||
|
"Alias": "Sabotage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "war",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "T4",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tdm",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sab",
|
||||||
|
"Alias": "Sabotage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "twar",
|
||||||
|
"Alias": "War"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "IW5",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "tdm",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dd",
|
||||||
|
"Alias": "Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dz",
|
||||||
|
"Alias": "Drop Zone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ffa",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gg",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hq",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "inf",
|
||||||
|
"Alias": "Infected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "jug",
|
||||||
|
"Alias": "Juggernaut"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "kc",
|
||||||
|
"Alias": "Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "oic",
|
||||||
|
"Alias": "One In The Chamber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sab",
|
||||||
|
"Alias": "Sabotage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tdef",
|
||||||
|
"Alias": "Team Defender"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tj",
|
||||||
|
"Alias": "Team Juggernaut"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "T5",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dem",
|
||||||
|
"Alias": "Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gun",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hlnd",
|
||||||
|
"Alias": "Sticks & Stones"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "oic",
|
||||||
|
"Alias": "One In The Chamber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sab",
|
||||||
|
"Alias": "Sabotage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "shrp",
|
||||||
|
"Alias": "Sharpshooter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tdm",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "IW6",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "blitz",
|
||||||
|
"Alias": "Blitz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "conf",
|
||||||
|
"Alias": "Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "cranked",
|
||||||
|
"Alias": "Cranked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "grind",
|
||||||
|
"Alias": "Grind"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "grnd",
|
||||||
|
"Alias": "Drop Zone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gun",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "horde",
|
||||||
|
"Alias": "Safeguard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "infect",
|
||||||
|
"Alias": "Infected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "siege",
|
||||||
|
"Alias": "Reinforce"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sotf",
|
||||||
|
"Alias": "Hunted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sotf_ffa",
|
||||||
|
"Alias": "Hunted FFA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sr",
|
||||||
|
"Alias": "Search & Rescue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "war",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "T6",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "conf",
|
||||||
|
"Alias": "Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dem",
|
||||||
|
"Alias": "Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gun",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hq",
|
||||||
|
"Alias": "Headquarters"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Hardpoint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "oic",
|
||||||
|
"Alias": "One In The Chamber"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "oneflag",
|
||||||
|
"Alias": "One-Flag CTF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sas",
|
||||||
|
"Alias": "Sticks & Stones"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "shrp",
|
||||||
|
"Alias": "Sharpshooter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tdm",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "T7",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "ball",
|
||||||
|
"Alias": "Uplink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "clean",
|
||||||
|
"Alias": "Fracture"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "conf",
|
||||||
|
"Alias": "Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dem",
|
||||||
|
"Alias": "Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "escort",
|
||||||
|
"Alias": "Safeguard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gun",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "koth",
|
||||||
|
"Alias": "Hardpoint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "tdm",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_ball",
|
||||||
|
"Alias": "Hardcore Uplink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_clean",
|
||||||
|
"Alias": "Hardcore Fracture"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_conf",
|
||||||
|
"Alias": "Hardcore Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_ctf",
|
||||||
|
"Alias": "Hardcore Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_dom",
|
||||||
|
"Alias": "Hardcore Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_dem",
|
||||||
|
"Alias": "Hardcore Demolition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_dm",
|
||||||
|
"Alias": "Hardcore Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_escort",
|
||||||
|
"Alias": "Hardcore Safeguard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_gun",
|
||||||
|
"Alias": "Hardcore Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_koth",
|
||||||
|
"Alias": "Hardcore Hardpoint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_sd",
|
||||||
|
"Alias": "Hardcore Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hc_tdm",
|
||||||
|
"Alias": "Hardcore Team Deathmatch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Game": "SHG1",
|
||||||
|
"Gametypes": [
|
||||||
|
{
|
||||||
|
"Name": "ball",
|
||||||
|
"Alias": "Uplink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "conf",
|
||||||
|
"Alias": "Kill Confirmed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ctf",
|
||||||
|
"Alias": "Capture The Flag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dom",
|
||||||
|
"Alias": "Domination"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "dm",
|
||||||
|
"Alias": "Free For All"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "gun",
|
||||||
|
"Alias": "Gun Game"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hp",
|
||||||
|
"Alias": "Hardpoint"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "infect",
|
||||||
|
"Alias": "Infected"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sd",
|
||||||
|
"Alias": "Search & Destroy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "sr",
|
||||||
|
"Alias": "Search & Rescue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "war",
|
||||||
|
"Alias": "Team Deathmatch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "twar",
|
||||||
|
"Alias": "Momentum"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"Maps": [
|
"Maps": [
|
||||||
{
|
{
|
||||||
"Game": "IW3",
|
"Game": "IW3",
|
||||||
@ -703,6 +1201,18 @@
|
|||||||
{
|
{
|
||||||
"Alias": "Highrise",
|
"Alias": "Highrise",
|
||||||
"Name": "mp_highrise"
|
"Name": "mp_highrise"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Favela",
|
||||||
|
"Name": "mp_favela"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Nuketown",
|
||||||
|
"Name": "mp_nuked"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Alias": "Skidrow",
|
||||||
|
"Name": "mp_nightshift"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -1517,7 +2027,7 @@
|
|||||||
"winchester1200": "W1200",
|
"winchester1200": "W1200",
|
||||||
"concussion": "Stun",
|
"concussion": "Stun",
|
||||||
"melee": "Knife",
|
"melee": "Knife",
|
||||||
"Frag" : "Grenade",
|
"Frag": "Grenade",
|
||||||
"airstrike": "Airstrike",
|
"airstrike": "Airstrike",
|
||||||
"helicopter": "Attack Helicopter",
|
"helicopter": "Attack Helicopter",
|
||||||
"player": "",
|
"player": "",
|
||||||
@ -1572,7 +2082,7 @@
|
|||||||
"30cal": "Browning M1919",
|
"30cal": "Browning M1919",
|
||||||
"type99rifle": "Arisaka",
|
"type99rifle": "Arisaka",
|
||||||
"mosinrifle": "Mosin-Nagant",
|
"mosinrifle": "Mosin-Nagant",
|
||||||
"ptrs41":"PTRS-41"
|
"ptrs41": "PTRS-41"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||||
|
|
||||||
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||||
@ -65,7 +65,7 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
||||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
||||||
|
|
||||||
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,24})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0);(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+|0)?;(-?[0-9]+);(axis|allies|world|none)?;([^;]{1,32})?;((?:[0-9]+|[a-z]+|_|\+)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
using IW4MAdmin.Application.Misc;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using IW4MAdmin.Application.Misc;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Extensions
|
namespace IW4MAdmin.Application.Extensions
|
||||||
{
|
{
|
||||||
@ -13,9 +17,19 @@ namespace IW4MAdmin.Application.Extensions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string CommandConfigNameForType(this IManagerCommand command)
|
public static string CommandConfigNameForType(this IManagerCommand command)
|
||||||
{
|
{
|
||||||
return command.GetType() == typeof(ScriptCommand) ?
|
return command.GetType() == typeof(ScriptCommand)
|
||||||
$"{char.ToUpper(command.Name[0])}{command.Name.Substring(1)}Command" :
|
? $"{char.ToUpper(command.Name[0])}{command.Name.Substring(1)}Command"
|
||||||
command.GetType().Name;
|
: command.GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IList<Map> FindMap(this Server server, string mapName) => server.Maps.Where(map =>
|
||||||
|
map.Name.Equals(mapName, StringComparison.InvariantCultureIgnoreCase) ||
|
||||||
|
map.Alias.Equals(mapName, StringComparison.InvariantCultureIgnoreCase)).ToList();
|
||||||
|
|
||||||
|
public static IList<Gametype> FindGametype(this DefaultSettings settings, string gameType, Server.Game? game = null) =>
|
||||||
|
settings.Gametypes?.Where(gt => game == null || gt.Game == game)
|
||||||
|
.SelectMany(gt => gt.Gametypes).Where(gt =>
|
||||||
|
gt.Alias.Contains(gameType, StringComparison.CurrentCultureIgnoreCase) ||
|
||||||
|
gt.Name.Contains(gameType, StringComparison.CurrentCultureIgnoreCase)).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,9 @@ using Serilog.Context;
|
|||||||
using static SharedLibraryCore.Database.Models.EFClient;
|
using static SharedLibraryCore.Database.Models.EFClient;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
|
using IW4MAdmin.Application.Commands;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SharedLibraryCore.Formatting;
|
||||||
using static Data.Models.Client.EFClient;
|
using static Data.Models.Client.EFClient;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
@ -55,7 +57,7 @@ namespace IW4MAdmin
|
|||||||
serverConfiguration,
|
serverConfiguration,
|
||||||
serviceProvider.GetRequiredService<IManager>(),
|
serviceProvider.GetRequiredService<IManager>(),
|
||||||
serviceProvider.GetRequiredService<IRConConnectionFactory>(),
|
serviceProvider.GetRequiredService<IRConConnectionFactory>(),
|
||||||
serviceProvider.GetRequiredService<IGameLogReaderFactory>())
|
serviceProvider.GetRequiredService<IGameLogReaderFactory>(), serviceProvider)
|
||||||
{
|
{
|
||||||
_translationLookup = lookup;
|
_translationLookup = lookup;
|
||||||
_metaService = metaService;
|
_metaService = metaService;
|
||||||
@ -242,11 +244,11 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await (plugin.OnEventAsync(gameEvent, this)).WithWaitCancellation(tokenSource.Token);
|
await plugin.OnEventAsync(gameEvent, this).WithWaitCancellation(tokenSource.Token);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(loc["SERVER_PLUGIN_ERROR"]);
|
Console.WriteLine(loc["SERVER_PLUGIN_ERROR"].FormatExt(plugin.Name, ex.GetType().Name));
|
||||||
ServerLogger.LogError(ex, "Could not execute {methodName} for plugin {plugin}",
|
ServerLogger.LogError(ex, "Could not execute {methodName} for plugin {plugin}",
|
||||||
nameof(plugin.OnEventAsync), plugin.Name);
|
nameof(plugin.OnEventAsync), plugin.Name);
|
||||||
}
|
}
|
||||||
@ -433,7 +435,7 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
if (E.Origin.Level > Permission.Moderator)
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -725,11 +727,11 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
private async Task OnClientUpdate(EFClient origin)
|
private async Task OnClientUpdate(EFClient origin)
|
||||||
{
|
{
|
||||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(origin));
|
var client = Manager.GetActiveClients().FirstOrDefault(c => c.NetworkId == origin.NetworkId);
|
||||||
|
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
ServerLogger.LogWarning("{origin} expected to exist in client list for update, but they do not", origin.ToString());
|
ServerLogger.LogWarning("{Origin} expected to exist in client list for update, but they do not", origin.ToString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,10 +757,10 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ((client.IPAddress != null && client.State == ClientState.Disconnecting) ||
|
else if (client.IPAddress != null && client.State == ClientState.Disconnecting ||
|
||||||
client.Level == Permission.Banned)
|
client.Level == Permission.Banned)
|
||||||
{
|
{
|
||||||
ServerLogger.LogWarning("{client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString());
|
ServerLogger.LogWarning("{Client} state is Unknown (probably kicked), but they are still connected. trying to kick again...", origin.ToString());
|
||||||
await client.CanConnect(client.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
await client.CanConnect(client.IPAddress, Manager.GetApplicationSettings().Configuration().EnableImplicitAccountLinking);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1321,12 +1323,9 @@ namespace IW4MAdmin
|
|||||||
public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin)
|
public override async Task Warn(string reason, EFClient targetClient, EFClient targetOrigin)
|
||||||
{
|
{
|
||||||
// ensure player gets warned if command not performed on them in game
|
// ensure player gets warned if command not performed on them in game
|
||||||
targetClient = targetClient.ClientNumber < 0 ?
|
var activeClient = Manager.FindActiveClient(targetClient);
|
||||||
Manager.GetActiveClients()
|
|
||||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
|
||||||
targetClient;
|
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Warning,
|
Type = EFPenalty.PenaltyType.Warning,
|
||||||
Expires = DateTime.UtcNow,
|
Expires = DateTime.UtcNow,
|
||||||
@ -1336,31 +1335,28 @@ namespace IW4MAdmin
|
|||||||
Link = targetClient.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerLogger.LogDebug("Creating warn penalty for {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Creating warn penalty for {TargetClient}", targetClient.ToString());
|
||||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||||
|
|
||||||
if (targetClient.IsIngame)
|
if (activeClient.IsIngame)
|
||||||
{
|
{
|
||||||
if (targetClient.Warnings >= 4)
|
if (activeClient.Warnings >= 4)
|
||||||
{
|
{
|
||||||
targetClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
activeClient.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient(this));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: move to translation sheet
|
var message = loc["COMMANDS_WARNING_FORMAT_V2"]
|
||||||
string message = $"^1{loc["SERVER_WARNING"]} ^7[^3{targetClient.Warnings}^7]: ^3{targetClient.Name}^7, {reason}";
|
.FormatExt(activeClient.Warnings, activeClient.Name, reason);
|
||||||
targetClient.CurrentServer.Broadcast(message);
|
activeClient.CurrentServer.Broadcast(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty)
|
public override async Task Kick(string reason, EFClient targetClient, EFClient originClient, EFPenalty previousPenalty)
|
||||||
{
|
{
|
||||||
targetClient = targetClient.ClientNumber < 0 ?
|
var activeClient = Manager.FindActiveClient(targetClient);
|
||||||
Manager.GetActiveClients()
|
|
||||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
|
||||||
targetClient;
|
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Kick,
|
Type = EFPenalty.PenaltyType.Kick,
|
||||||
Expires = DateTime.UtcNow,
|
Expires = DateTime.UtcNow,
|
||||||
@ -1370,77 +1366,64 @@ namespace IW4MAdmin
|
|||||||
Link = targetClient.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerLogger.LogDebug("Creating kick penalty for {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Creating kick penalty for {TargetClient}", targetClient.ToString());
|
||||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||||
|
|
||||||
if (targetClient.IsIngame)
|
if (activeClient.IsIngame)
|
||||||
{
|
{
|
||||||
var e = new GameEvent()
|
var gameEvent = new GameEvent
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.PreDisconnect,
|
Type = GameEvent.EventType.PreDisconnect,
|
||||||
Origin = targetClient,
|
Origin = activeClient,
|
||||||
Owner = this
|
Owner = this
|
||||||
};
|
};
|
||||||
|
|
||||||
Manager.AddEvent(e);
|
Manager.AddEvent(gameEvent);
|
||||||
|
|
||||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
|
||||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
|
||||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
|
||||||
|
|
||||||
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||||
clientNumber,
|
activeClient.TemporalClientNumber,
|
||||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration,
|
_messageFormatter.BuildFormattedMessage(RconParser.Configuration,
|
||||||
newPenalty,
|
newPenalty,
|
||||||
previousPenalty));
|
previousPenalty));
|
||||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
|
||||||
|
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task TempBan(string Reason, TimeSpan length, EFClient targetClient, EFClient originClient)
|
public override async Task TempBan(string reason, TimeSpan length, EFClient targetClient, EFClient originClient)
|
||||||
{
|
{
|
||||||
// ensure player gets kicked if command not performed on them in the same server
|
// ensure player gets kicked if command not performed on them in the same server
|
||||||
targetClient = targetClient.ClientNumber < 0 ?
|
var activeClient = Manager.FindActiveClient(targetClient);
|
||||||
Manager.GetActiveClients()
|
|
||||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
|
||||||
targetClient;
|
|
||||||
|
|
||||||
var newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.TempBan,
|
Type = EFPenalty.PenaltyType.TempBan,
|
||||||
Expires = DateTime.UtcNow + length,
|
Expires = DateTime.UtcNow + length,
|
||||||
Offender = targetClient,
|
Offender = targetClient,
|
||||||
Offense = Reason,
|
Offense = reason,
|
||||||
Punisher = originClient,
|
Punisher = originClient,
|
||||||
Link = targetClient.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerLogger.LogDebug("Creating tempban penalty for {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Creating tempban penalty for {TargetClient}", targetClient.ToString());
|
||||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||||
|
|
||||||
if (targetClient.IsIngame)
|
if (activeClient.IsIngame)
|
||||||
{
|
{
|
||||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
|
||||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
|
||||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
|
||||||
|
|
||||||
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
var formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||||
clientNumber,
|
activeClient.TemporalClientNumber,
|
||||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
||||||
ServerLogger.LogDebug("Executing tempban kick command for {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Executing tempban kick command for {ActiveClient}", activeClient.ToString());
|
||||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
await activeClient.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
|
public override async Task Ban(string reason, EFClient targetClient, EFClient originClient, bool isEvade = false)
|
||||||
{
|
{
|
||||||
// ensure player gets kicked if command not performed on them in the same server
|
// ensure player gets kicked if command not performed on them in the same server
|
||||||
targetClient = targetClient.ClientNumber < 0 ?
|
var activeClient = Manager.FindActiveClient(targetClient);
|
||||||
Manager.GetActiveClients()
|
|
||||||
.FirstOrDefault(c => c.ClientId == targetClient?.ClientId) ?? targetClient :
|
|
||||||
targetClient;
|
|
||||||
|
|
||||||
EFPenalty newPenalty = new EFPenalty()
|
var newPenalty = new EFPenalty
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Ban,
|
Type = EFPenalty.PenaltyType.Ban,
|
||||||
Expires = null,
|
Expires = null,
|
||||||
@ -1451,51 +1434,47 @@ namespace IW4MAdmin
|
|||||||
IsEvadedOffense = isEvade
|
IsEvadedOffense = isEvade
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerLogger.LogDebug("Creating ban penalty for {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Creating ban penalty for {TargetClient}", targetClient.ToString());
|
||||||
targetClient.SetLevel(Permission.Banned, originClient);
|
activeClient.SetLevel(Permission.Banned, originClient);
|
||||||
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
await newPenalty.TryCreatePenalty(Manager.GetPenaltyService(), ServerLogger);
|
||||||
|
|
||||||
if (targetClient.IsIngame)
|
if (activeClient.IsIngame)
|
||||||
{
|
{
|
||||||
ServerLogger.LogDebug("Attempting to kicking newly banned client {targetClient}", targetClient.ToString());
|
ServerLogger.LogDebug("Attempting to kicking newly banned client {ActiveClient}", activeClient.ToString());
|
||||||
|
|
||||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
|
||||||
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?)null : int.Parse(temporalClientId);
|
|
||||||
var clientNumber = parsedClientId ?? targetClient.ClientNumber;
|
|
||||||
|
|
||||||
var formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
var formattedString = string.Format(RconParser.Configuration.CommandPrefixes.Kick,
|
||||||
clientNumber,
|
activeClient.TemporalClientNumber,
|
||||||
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
_messageFormatter.BuildFormattedMessage(RconParser.Configuration, newPenalty));
|
||||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
await activeClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override public async Task Unban(string reason, EFClient Target, EFClient Origin)
|
public override async Task Unban(string reason, EFClient targetClient, EFClient originClient)
|
||||||
{
|
{
|
||||||
var unbanPenalty = new EFPenalty()
|
var unbanPenalty = new EFPenalty
|
||||||
{
|
{
|
||||||
Type = EFPenalty.PenaltyType.Unban,
|
Type = EFPenalty.PenaltyType.Unban,
|
||||||
Expires = DateTime.Now,
|
Expires = DateTime.Now,
|
||||||
Offender = Target,
|
Offender = targetClient,
|
||||||
Offense = reason,
|
Offense = reason,
|
||||||
Punisher = Origin,
|
Punisher = originClient,
|
||||||
When = DateTime.UtcNow,
|
When = DateTime.UtcNow,
|
||||||
Active = true,
|
Active = true,
|
||||||
Link = Target.AliasLink
|
Link = targetClient.AliasLink
|
||||||
};
|
};
|
||||||
|
|
||||||
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", Target.ToString());
|
ServerLogger.LogDebug("Creating unban penalty for {targetClient}", targetClient.ToString());
|
||||||
Target.SetLevel(Permission.User, Origin);
|
targetClient.SetLevel(Permission.User, originClient);
|
||||||
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId);
|
await Manager.GetPenaltyService().RemoveActivePenalties(targetClient.AliasLink.AliasLinkId);
|
||||||
await Manager.GetPenaltyService().Create(unbanPenalty);
|
await Manager.GetPenaltyService().Create(unbanPenalty);
|
||||||
}
|
}
|
||||||
|
|
||||||
override public void InitializeTokens()
|
public override void InitializeTokens()
|
||||||
{
|
{
|
||||||
Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
|
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("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("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))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ using SharedLibraryCore.Services;
|
|||||||
using Stats.Dtos;
|
using Stats.Dtos;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -342,7 +343,12 @@ namespace IW4MAdmin.Application
|
|||||||
var masterUri = Utilities.IsDevelopment
|
var masterUri = Utilities.IsDevelopment
|
||||||
? new Uri("http://127.0.0.1:8080")
|
? new Uri("http://127.0.0.1:8080")
|
||||||
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
|
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
|
||||||
var masterRestClient = RestClient.For<IMasterApi>(masterUri);
|
var httpClient = new HttpClient
|
||||||
|
{
|
||||||
|
BaseAddress = masterUri,
|
||||||
|
Timeout = TimeSpan.FromSeconds(15)
|
||||||
|
};
|
||||||
|
var masterRestClient = RestClient.For<IMasterApi>(httpClient);
|
||||||
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
|
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
|
||||||
|
|
||||||
if (appConfig == null)
|
if (appConfig == null)
|
||||||
|
@ -179,7 +179,8 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
Id = s.EndPoint,
|
Id = s.EndPoint,
|
||||||
Port = (short)s.Port,
|
Port = (short)s.Port,
|
||||||
IPAddress = s.IP
|
IPAddress = s.IP
|
||||||
}).ToList()
|
}).ToList(),
|
||||||
|
WebfrontUrl = _appConfig.WebfrontUrl
|
||||||
};
|
};
|
||||||
|
|
||||||
Response<ResultMessage> response = null;
|
Response<ResultMessage> response = null;
|
||||||
|
@ -24,7 +24,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
private readonly IDataValueCache<EFServerSnapshot, List<ClientHistoryInfo>> _clientHistoryCache;
|
||||||
|
|
||||||
private readonly TimeSpan? _cacheTimeSpan =
|
private readonly TimeSpan? _cacheTimeSpan =
|
||||||
Utilities.IsDevelopment ? TimeSpan.FromSeconds(1) : (TimeSpan?) TimeSpan.FromMinutes(1);
|
Utilities.IsDevelopment ? TimeSpan.FromSeconds(30) : (TimeSpan?) TimeSpan.FromMinutes(10);
|
||||||
|
|
||||||
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
public ServerDataViewer(ILogger<ServerDataViewer> logger, IDataValueCache<EFServerSnapshot, (int?, DateTime?)> snapshotCache,
|
||||||
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
IDataValueCache<EFClient, (int, int)> serverStatsCache,
|
||||||
@ -36,7 +36,8 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
_clientHistoryCache = clientHistoryCache;
|
_clientHistoryCache = clientHistoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(int?, DateTime?)> MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null,
|
public async Task<(int?, DateTime?)>
|
||||||
|
MaxConcurrentClientsAsync(long? serverId = null, TimeSpan? overPeriod = null,
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
{
|
||||||
_snapshotCache.SetCacheItem(async (snapshots, cancellationToken) =>
|
_snapshotCache.SetCacheItem(async (snapshots, cancellationToken) =>
|
||||||
@ -83,7 +84,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
_logger.LogDebug("Max concurrent clients since {Start} is {Clients}", oldestEntry, maxClients);
|
||||||
|
|
||||||
return (maxClients, maxClientsTime);
|
return (maxClients, maxClientsTime);
|
||||||
}, nameof(MaxConcurrentClientsAsync), _cacheTimeSpan);
|
}, nameof(MaxConcurrentClientsAsync), _cacheTimeSpan, true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -107,7 +108,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
cancellationToken);
|
cancellationToken);
|
||||||
|
|
||||||
return (count, recentCount);
|
return (count, recentCount);
|
||||||
}, nameof(_serverStatsCache), _cacheTimeSpan);
|
}, nameof(_serverStatsCache), _cacheTimeSpan, true);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using SharedLibraryCore.RCon;
|
using SharedLibraryCore.RCon;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using SharedLibraryCore.Formatting;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.RConParsers
|
namespace IW4MAdmin.Application.RConParsers
|
||||||
{
|
{
|
||||||
@ -28,6 +29,24 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
public int NoticeMaximumLines { get; set; } = 8;
|
public int NoticeMaximumLines { get; set; } = 8;
|
||||||
public int NoticeMaxCharactersPerLine { get; set; } = 50;
|
public int NoticeMaxCharactersPerLine { get; set; } = 50;
|
||||||
public string NoticeLineSeparator { get; set; } = Environment.NewLine;
|
public string NoticeLineSeparator { get; set; } = Environment.NewLine;
|
||||||
|
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)
|
public DynamicRConParserConfiguration(IParserRegexFactory parserRegexFactory)
|
||||||
{
|
{
|
||||||
|
@ -5,9 +5,10 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace Data.Abstractions
|
namespace Data.Abstractions
|
||||||
{
|
{
|
||||||
public interface IDataValueCache<T, V> where T : class
|
public interface IDataValueCache<TEntityType, TReturnType> where TEntityType : class
|
||||||
{
|
{
|
||||||
void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> itemGetter, string keyName, TimeSpan? expirationTime = null);
|
void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> itemGetter, string keyName,
|
||||||
Task<V> GetCacheItem(string keyName, CancellationToken token = default);
|
TimeSpan? expirationTime = null, bool autoRefresh = false);
|
||||||
|
Task<TReturnType> GetCacheItem(string keyName, CancellationToken token = default);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -118,7 +118,7 @@ namespace Data.Context
|
|||||||
ent.HasIndex(a => a.Name);
|
ent.HasIndex(a => a.Name);
|
||||||
ent.Property(_alias => _alias.SearchableName).HasMaxLength(24);
|
ent.Property(_alias => _alias.SearchableName).HasMaxLength(24);
|
||||||
ent.HasIndex(_alias => _alias.SearchableName);
|
ent.HasIndex(_alias => _alias.SearchableName);
|
||||||
ent.HasIndex(_alias => new {_alias.Name, _alias.IPAddress}).IsUnique();
|
ent.HasIndex(_alias => new {_alias.Name, _alias.IPAddress});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity<EFMeta>(ent =>
|
modelBuilder.Entity<EFMeta>(ent =>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Data</PackageId>
|
||||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||||
<Authors />
|
<Authors />
|
||||||
<PackageVersion>1.0.7</PackageVersion>
|
<PackageVersion>1.1.0</PackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -1,58 +1,84 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Data.Abstractions;
|
using Data.Abstractions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Timer = System.Timers.Timer;
|
||||||
|
|
||||||
namespace Data.Helpers
|
namespace Data.Helpers
|
||||||
{
|
{
|
||||||
public class DataValueCache<T, V> : IDataValueCache<T, V> where T : class
|
public class DataValueCache<TEntityType, TReturnType> : IDataValueCache<TEntityType, TReturnType>
|
||||||
|
where TEntityType : class
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IDatabaseContextFactory _contextFactory;
|
private readonly IDatabaseContextFactory _contextFactory;
|
||||||
private readonly Dictionary<string, CacheState> _cacheStates = new Dictionary<string, CacheState>();
|
|
||||||
private const int DefaultExpireMinutes = 15;
|
|
||||||
|
|
||||||
private class CacheState
|
private readonly ConcurrentDictionary<string, CacheState<TReturnType>> _cacheStates =
|
||||||
|
new ConcurrentDictionary<string, CacheState<TReturnType>>();
|
||||||
|
|
||||||
|
private bool _autoRefresh;
|
||||||
|
private const int DefaultExpireMinutes = 15;
|
||||||
|
private Timer _timer;
|
||||||
|
|
||||||
|
private class CacheState<TCacheType>
|
||||||
{
|
{
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public DateTime LastRetrieval { get; set; }
|
public DateTime LastRetrieval { get; set; }
|
||||||
public TimeSpan ExpirationTime { get; set; }
|
public TimeSpan ExpirationTime { get; set; }
|
||||||
public Func<DbSet<T>, CancellationToken, Task<V>> Getter { get; set; }
|
public Func<DbSet<TEntityType>, CancellationToken, Task<TCacheType>> Getter { get; set; }
|
||||||
public V Value { get; set; }
|
public TCacheType Value { get; set; }
|
||||||
|
public bool IsSet { get; set; }
|
||||||
|
|
||||||
public bool IsExpired => ExpirationTime != TimeSpan.MaxValue &&
|
public bool IsExpired => ExpirationTime != TimeSpan.MaxValue &&
|
||||||
(DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
(DateTime.Now - LastRetrieval.Add(ExpirationTime)).TotalSeconds > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataValueCache(ILogger<DataValueCache<T, V>> logger, IDatabaseContextFactory contextFactory)
|
public DataValueCache(ILogger<DataValueCache<TEntityType, TReturnType>> logger,
|
||||||
|
IDatabaseContextFactory contextFactory)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_contextFactory = contextFactory;
|
_contextFactory = contextFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetCacheItem(Func<DbSet<T>, CancellationToken, Task<V>> getter, string key,
|
~DataValueCache()
|
||||||
TimeSpan? expirationTime = null)
|
{
|
||||||
|
_timer?.Stop();
|
||||||
|
_timer?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCacheItem(Func<DbSet<TEntityType>, CancellationToken, Task<TReturnType>> getter, string key,
|
||||||
|
TimeSpan? expirationTime = null, bool autoRefresh = false)
|
||||||
{
|
{
|
||||||
if (_cacheStates.ContainsKey(key))
|
if (_cacheStates.ContainsKey(key))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Cache key {key} is already added", key);
|
_logger.LogDebug("Cache key {Key} is already added", key);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = new CacheState()
|
var state = new CacheState<TReturnType>
|
||||||
{
|
{
|
||||||
Key = key,
|
Key = key,
|
||||||
Getter = getter,
|
Getter = getter,
|
||||||
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
ExpirationTime = expirationTime ?? TimeSpan.FromMinutes(DefaultExpireMinutes)
|
||||||
};
|
};
|
||||||
|
|
||||||
_cacheStates.Add(key, state);
|
_autoRefresh = autoRefresh;
|
||||||
|
|
||||||
|
_cacheStates.TryAdd(key, state);
|
||||||
|
|
||||||
|
if (!_autoRefresh || expirationTime == TimeSpan.MaxValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<V> GetCacheItem(string keyName, CancellationToken cancellationToken = default)
|
_timer = new Timer(state.ExpirationTime.TotalMilliseconds);
|
||||||
|
_timer.Elapsed += async (sender, args) => await RunCacheUpdate(state, CancellationToken.None);
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TReturnType> GetCacheItem(string keyName, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (!_cacheStates.ContainsKey(keyName))
|
if (!_cacheStates.ContainsKey(keyName))
|
||||||
{
|
{
|
||||||
@ -61,7 +87,9 @@ namespace Data.Helpers
|
|||||||
|
|
||||||
var state = _cacheStates[keyName];
|
var state = _cacheStates[keyName];
|
||||||
|
|
||||||
if (state.IsExpired || state.Value == null)
|
// when auto refresh is off we want to check the expiration and value
|
||||||
|
// when auto refresh is on, we want to only check the value, because it'll be refreshed automatically
|
||||||
|
if ((state.IsExpired || !state.IsSet) && !_autoRefresh || _autoRefresh && !state.IsSet)
|
||||||
{
|
{
|
||||||
await RunCacheUpdate(state, cancellationToken);
|
await RunCacheUpdate(state, cancellationToken);
|
||||||
}
|
}
|
||||||
@ -69,19 +97,21 @@ namespace Data.Helpers
|
|||||||
return state.Value;
|
return state.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunCacheUpdate(CacheState state, CancellationToken token)
|
private async Task RunCacheUpdate(CacheState<TReturnType> state, CancellationToken token)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_logger.LogDebug("Running update for {ClassName} {@State}", GetType().Name, state);
|
||||||
await using var context = _contextFactory.CreateContext(false);
|
await using var context = _contextFactory.CreateContext(false);
|
||||||
var set = context.Set<T>();
|
var set = context.Set<TEntityType>();
|
||||||
var value = await state.Getter(set, token);
|
var value = await state.Getter(set, token);
|
||||||
state.Value = value;
|
state.Value = value;
|
||||||
|
state.IsSet = true;
|
||||||
state.LastRetrieval = DateTime.Now;
|
state.LastRetrieval = DateTime.Now;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Could not get cached value for {key}", state.Key);
|
_logger.LogError(ex, "Could not get cached value for {Key}", state.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1449
Data/Migrations/MySql/20220102221729_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
1449
Data/Migrations/MySql/20220102221729_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Data.Migrations.MySql
|
||||||
|
{
|
||||||
|
public partial class RemoveUniqueAliasIndexConstraint : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
columns: new[] { "Name", "IPAddress" });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
columns: new[] { "Name", "IPAddress" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -816,8 +816,7 @@ namespace Data.Migrations.MySql
|
|||||||
|
|
||||||
b.HasIndex("SearchableName");
|
b.HasIndex("SearchableName");
|
||||||
|
|
||||||
b.HasIndex("Name", "IPAddress")
|
b.HasIndex("Name", "IPAddress");
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("EFAlias");
|
b.ToTable("EFAlias");
|
||||||
});
|
});
|
||||||
|
1477
Data/Migrations/Postgresql/20220102221801_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
1477
Data/Migrations/Postgresql/20220102221801_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Data.Migrations.Postgresql
|
||||||
|
{
|
||||||
|
public partial class RemoveUniqueAliasIndexConstraint : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
columns: new[] { "Name", "IPAddress" });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_EFAlias_Name_IPAddress",
|
||||||
|
table: "EFAlias",
|
||||||
|
columns: new[] { "Name", "IPAddress" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -836,8 +836,7 @@ namespace Data.Migrations.Postgresql
|
|||||||
|
|
||||||
b.HasIndex("SearchableName");
|
b.HasIndex("SearchableName");
|
||||||
|
|
||||||
b.HasIndex("Name", "IPAddress")
|
b.HasIndex("Name", "IPAddress");
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("EFAlias");
|
b.ToTable("EFAlias");
|
||||||
});
|
});
|
||||||
|
1448
Data/Migrations/Sqlite/20220102214442_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
1448
Data/Migrations/Sqlite/20220102214442_RemoveUniqueAliasIndexConstraint.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,15 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Data.Migrations.Sqlite
|
||||||
|
{
|
||||||
|
public partial class RemoveUniqueAliasIndexConstraint : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -815,8 +815,7 @@ namespace Data.Migrations.Sqlite
|
|||||||
|
|
||||||
b.HasIndex("SearchableName");
|
b.HasIndex("SearchableName");
|
||||||
|
|
||||||
b.HasIndex("Name", "IPAddress")
|
b.HasIndex("Name", "IPAddress");
|
||||||
.IsUnique();
|
|
||||||
|
|
||||||
b.ToTable("EFAlias");
|
b.ToTable("EFAlias");
|
||||||
});
|
});
|
||||||
|
@ -57,10 +57,11 @@ namespace Data.Models.Client.Stats
|
|||||||
public double MaxStrain { get; set; }
|
public double MaxStrain { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public float AverageHitOffset
|
public float AverageHitOffset =>
|
||||||
{
|
(float) Math.Round(
|
||||||
get => (float)Math.Round(HitLocations.Sum(c => c.HitOffsetAverage) / Math.Max(1, HitLocations.Where(c => c.HitOffsetAverage > 0).Count()), 4);
|
HitLocations.Sum(c => c.HitOffsetAverage) /
|
||||||
}
|
Math.Max(1, HitLocations.Count(c => c.HitOffsetAverage > 0)), 4);
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int SessionKills { get; set; }
|
public int SessionKills { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
@ -82,26 +83,26 @@ namespace Data.Models.Client.Stats
|
|||||||
KillStreak = 0;
|
KillStreak = 0;
|
||||||
DeathStreak = 0;
|
DeathStreak = 0;
|
||||||
LastScore = 0;
|
LastScore = 0;
|
||||||
SessionScores.Add(0);
|
_sessionScores.Add(0);
|
||||||
Team = 0;
|
Team = 0;
|
||||||
}
|
}
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int SessionScore
|
public int SessionScore
|
||||||
{
|
{
|
||||||
set => SessionScores[SessionScores.Count - 1] = value;
|
set => _sessionScores[^1] = value;
|
||||||
|
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
lock (SessionScores)
|
lock (_sessionScores)
|
||||||
{
|
{
|
||||||
return new List<int>(SessionScores).Sum();
|
return new List<int>(_sessionScores).Sum();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int RoundScore => SessionScores[SessionScores.Count - 1];
|
public int RoundScore => _sessionScores[^1];
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
private readonly List<int> SessionScores = new List<int>() { 0 };
|
private readonly List<int> _sessionScores = new List<int> { 0 };
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public int Team { get; set; }
|
public int Team { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
@ -109,6 +110,21 @@ namespace Data.Models.Client.Stats
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public double SessionSPM { get; set; }
|
public double SessionSPM { get; set; }
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public SemaphoreSlim ProcessingHit { get; private set; }
|
public SemaphoreSlim ProcessingHit { get; }
|
||||||
|
|
||||||
|
[NotMapped] public MatchData MatchData { get; } = new MatchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MatchData
|
||||||
|
{
|
||||||
|
public int Kills { get; set; }
|
||||||
|
public int Deaths { get; set; }
|
||||||
|
public double Kdr => Deaths == 0 ? Kills : Math.Round(Kills / (double) Deaths, 2);
|
||||||
|
|
||||||
|
public void StartNewMatch()
|
||||||
|
{
|
||||||
|
Kills = 0;
|
||||||
|
Deaths = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ waitForFrameThread()
|
|||||||
|
|
||||||
waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount )
|
waitForAdditionalAngles( logString, beforeFrameCount, afterFrameCount )
|
||||||
{
|
{
|
||||||
|
self endon( "disconnect" );
|
||||||
currentIndex = self.currentAnglePosition;
|
currentIndex = self.currentAnglePosition;
|
||||||
wait( 0.05 * afterFrameCount );
|
wait( 0.05 * afterFrameCount );
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug
|
|||||||
Plugins\ScriptPlugins\ParserS1x.js = Plugins\ScriptPlugins\ParserS1x.js
|
Plugins\ScriptPlugins\ParserS1x.js = Plugins\ScriptPlugins\ParserS1x.js
|
||||||
Plugins\ScriptPlugins\ParserCSGO.js = Plugins\ScriptPlugins\ParserCSGO.js
|
Plugins\ScriptPlugins\ParserCSGO.js = Plugins\ScriptPlugins\ParserCSGO.js
|
||||||
Plugins\ScriptPlugins\ParserCSGOSM.js = Plugins\ScriptPlugins\ParserCSGOSM.js
|
Plugins\ScriptPlugins\ParserCSGOSM.js = Plugins\ScriptPlugins\ParserCSGOSM.js
|
||||||
|
Plugins\ScriptPlugins\ParserPlutoniumT4COZM.js = Plugins\ScriptPlugins\ParserPlutoniumT4COZM.js
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
||||||
|
@ -66,6 +66,7 @@ namespace Integrations.Source
|
|||||||
// ignored
|
// ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await Task.Delay(ConnectionTimeout);
|
||||||
_rconClient = _rconClientFactory.CreateClient(_ipEndPoint);
|
_rconClient = _rconClientFactory.CreateClient(_ipEndPoint);
|
||||||
_authenticated = false;
|
_authenticated = false;
|
||||||
_needNewSocket = false;
|
_needNewSocket = false;
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<LangVersion>7.1</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<LangVersion>7.1</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
<Version>0.1.0.0</Version>
|
<Version>0.1.0.0</Version>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<LangVersion>7.1</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
@ -23,7 +23,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
@foreach (SharedLibraryCore.Dtos.ServerInfo server in ViewBag.Servers)
|
@foreach (SharedLibraryCore.Dtos.ServerInfo server in ViewBag.Servers)
|
||||||
{
|
{
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a asp-controller="Radar" asp-action="Index" asp-route-serverId="@server.ID" class="nav-link @(server.ID == ViewBag.ActiveServerId ? "active": "")" aria-selected="@(server.ID == ViewBag.ActiveServerId ? "true": "false")"><color-code value="@server.Name" allow="@ViewBag.EnableColorCodes"></color-code></a>
|
<a asp-controller="Radar" asp-action="Index" asp-route-serverId="@server.ID" class="nav-link @(server.ID == ViewBag.ActiveServerId ? "active": "")" aria-selected="@(server.ID == ViewBag.ActiveServerId ? "true": "false")"><color-code value="@server.Name"></color-code></a>
|
||||||
</li>
|
</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Product>Login Plugin for IW4MAdmin</Product>
|
<Product>Login Plugin for IW4MAdmin</Product>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<LangVersion>7.1</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -12,11 +12,11 @@
|
|||||||
<Description>Warns and kicks players for using profanity</Description>
|
<Description>Warns and kicks players for using profanity</Description>
|
||||||
<Copyright>2018</Copyright>
|
<Copyright>2018</Copyright>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<LangVersion>7.1</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -3,7 +3,7 @@ let eventParser;
|
|||||||
|
|
||||||
const plugin = {
|
const plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.3,
|
version: 0.4,
|
||||||
name: 'CS:GO Parser',
|
name: 'CS:GO Parser',
|
||||||
engine: 'Source',
|
engine: 'Source',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
@ -57,7 +57,27 @@ const plugin = {
|
|||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_game', 'game_mode');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_game', 'game_mode');
|
||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password');
|
||||||
|
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Clear();
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('White', '\x01');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Red', '\x07');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightRed', '\x0F');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkRed', '\x02');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Blue', '\x0B');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkBlue', '\x0C');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Purple', '\x03');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Orchid', '\x0E');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Yellow', '\x09');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Gold', '\x10');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightGreen', '\x05');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Green', '\x04');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Lime', '\x06');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey', '\x08');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey2', '\x0D');
|
||||||
|
// only adding there here for the default accent color
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Cyan', '\x0B');
|
||||||
|
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. ';
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27015;
|
||||||
rconParser.CanGenerateLogPath = false;
|
rconParser.CanGenerateLogPath = false;
|
||||||
|
|
||||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
@ -3,7 +3,7 @@ let eventParser;
|
|||||||
|
|
||||||
const plugin = {
|
const plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.3,
|
version: 0.5,
|
||||||
name: 'CS:GO (SourceMod) Parser',
|
name: 'CS:GO (SourceMod) Parser',
|
||||||
engine: 'Source',
|
engine: 'Source',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
@ -57,7 +57,27 @@ const plugin = {
|
|||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_game', 'game_mode');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_game', 'game_mode');
|
||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('g_password', 'sv_password');
|
||||||
|
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Clear();
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('White', '\x01');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Red', '\x07');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightRed', '\x0F');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkRed', '\x02');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Blue', '\x0B');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('DarkBlue', '\x0C');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Purple', '\x03');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Orchid', '\x0E');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Yellow', '\x09');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Gold', '\x10');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('LightGreen', '\x05');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Green', '\x04');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Lime', '\x06');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey', '\x08');
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Grey2', '\x0D');
|
||||||
|
// only adding there here for the default accent color
|
||||||
|
rconParser.Configuration.ColorCodeMapping.Add('Cyan', '\x0B');
|
||||||
|
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. ';
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27015;
|
||||||
rconParser.CanGenerateLogPath = false;
|
rconParser.CanGenerateLogPath = false;
|
||||||
|
|
||||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'FrenchFry, RaidMax',
|
author: 'FrenchFry, RaidMax',
|
||||||
version: 0.7,
|
version: 0.8,
|
||||||
name: 'CoD4x Parser',
|
name: 'CoD4x Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -25,6 +25,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.Dvar.AddMapping(110, 4); // dvar info
|
rconParser.Configuration.Dvar.AddMapping(110, 4); // dvar info
|
||||||
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. '; // CoD4x does not support \n in the client notice
|
rconParser.Configuration.NoticeLineSeparator = '. '; // CoD4x does not support \n in the client notice
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
rconParser.Version = 'CoD4 X - win_mingw-x86 build 1056 Dec 12 2020';
|
rconParser.Version = 'CoD4 X - win_mingw-x86 build 1056 Dec 12 2020';
|
||||||
rconParser.GameName = 1; // IW3
|
rconParser.GameName = 1; // IW3
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.4,
|
version: 0.5,
|
||||||
name: 'IW4x Parser',
|
name: 'IW4x Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -19,6 +19,10 @@ var plugin = {
|
|||||||
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0} "{1}"';
|
||||||
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0} "{1}"';
|
||||||
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.TempBan = 'tempbanclient {0} "{1}"';
|
||||||
|
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
|
rconParser.Configuration.DefaultInstallationDirectoryHint = 'HKEY_CURRENT_USER\\Software\\Classes\\iw4x\\shell\\open\\command';
|
||||||
|
|
||||||
eventParser.Configuration.GameDirectory = 'userraw';
|
eventParser.Configuration.GameDirectory = 'userraw';
|
||||||
|
|
||||||
rconParser.Version = 'IW4x (v0.6.0)';
|
rconParser.Version = 'IW4x (v0.6.0)';
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'Xerxes, RaidMax, st0rm',
|
author: 'Xerxes, RaidMax, st0rm',
|
||||||
version: 0.3,
|
version: 0.4,
|
||||||
name: 'IW6x Parser',
|
name: 'IW6x Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
||||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
||||||
rconParser.Configuration.WaitForResponse = false;
|
rconParser.Configuration.WaitForResponse = false;
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
rconParser.Configuration.Status.AddMapping(102, 4);
|
rconParser.Configuration.Status.AddMapping(102, 4);
|
||||||
rconParser.Configuration.Status.AddMapping(103, 5);
|
rconParser.Configuration.Status.AddMapping(103, 5);
|
||||||
rconParser.Configuration.Status.AddMapping(104, 6);
|
rconParser.Configuration.Status.AddMapping(104, 6);
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.9,
|
version: 1.0,
|
||||||
name: 'Plutonium IW5 Parser',
|
name: 'Plutonium IW5 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -28,6 +28,9 @@ var plugin = {
|
|||||||
rconParser.Configuration.WaitForResponse = true;
|
rconParser.Configuration.WaitForResponse = true;
|
||||||
rconParser.Configuration.CanGenerateLogPath = true;
|
rconParser.Configuration.CanGenerateLogPath = true;
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. ';
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27016;
|
||||||
|
|
||||||
|
rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/iw5';
|
||||||
|
|
||||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
||||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(0|1) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(0|1) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax, Xerxes',
|
author: 'RaidMax, Xerxes',
|
||||||
version: 1.0,
|
version: 1.2,
|
||||||
name: 'Plutonium T6 Parser',
|
name: 'Plutonium T6 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ var plugin = {
|
|||||||
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
||||||
rconParser.Configuration.WaitForResponse = false;
|
rconParser.Configuration.WaitForResponse = false;
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. ';
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 4976;
|
||||||
|
rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/t6';
|
||||||
|
|
||||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +lastmsg +address +qport +rate *';
|
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +lastmsg +address +qport +rate *';
|
||||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+|0) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +([0-9]+) +(?:[0-1]{1}) +([0-9]+) +([A-F0-9]+|0) +(.+?) +(?:[0-9]+) +(\\d+\\.\\d+\\.\\d+\\.\\d+\\:-?\\d{1,5}|0+\\.0+:-?\\d{1,5}|loopback) +(?:-?[0-9]+) +(?:[0-9]+) *$';
|
||||||
@ -37,6 +39,21 @@ var plugin = {
|
|||||||
rconParser.Configuration.Status.AddMapping(104, 5);
|
rconParser.Configuration.Status.AddMapping(104, 5);
|
||||||
rconParser.Configuration.Status.AddMapping(105, 6);
|
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.GameDirectory = '';
|
||||||
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax, Chase',
|
author: 'RaidMax, Chase',
|
||||||
version: 0.2,
|
version: 0.4,
|
||||||
name: 'Plutonium T4 Parser',
|
name: 'Plutonium T4 MP Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
onEventAsync: function (gameEvent, server) {
|
onEventAsync: function (gameEvent, server) {
|
||||||
@ -19,12 +19,16 @@ var plugin = {
|
|||||||
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0}';
|
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0}';
|
||||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
||||||
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_homepath', 'fs_localAppData');
|
||||||
|
|
||||||
|
rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/t4';
|
||||||
|
|
||||||
rconParser.Version = 'Plutonium T4';
|
rconParser.Version = 'Plutonium T4';
|
||||||
rconParser.GameName = 5; // T4
|
rconParser.GameName = 5; // T4
|
||||||
|
|
||||||
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
eventParser.Configuration.GameDirectory = 'raw';
|
eventParser.Configuration.GameDirectory = 'main';
|
||||||
|
|
||||||
eventParser.Version = 'Plutonium T4';
|
eventParser.Version = 'Plutonium T4';
|
||||||
},
|
},
|
||||||
|
43
Plugins/ScriptPlugins/ParserPlutoniumT4COZM.js
Normal file
43
Plugins/ScriptPlugins/ParserPlutoniumT4COZM.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
var rconParser;
|
||||||
|
var eventParser;
|
||||||
|
|
||||||
|
var plugin = {
|
||||||
|
author: 'RaidMax',
|
||||||
|
version: 0.1,
|
||||||
|
name: 'Plutonium T4 CO-OP/Zombies Parser',
|
||||||
|
isParser: true,
|
||||||
|
|
||||||
|
onEventAsync: function (gameEvent, server) {
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoadAsync: function (manager) {
|
||||||
|
rconParser = manager.GenerateDynamicRConParser(this.name);
|
||||||
|
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||||
|
|
||||||
|
rconParser.Configuration.CommandPrefixes.Kick = 'clientkick {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.Ban = 'clientkick {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.TempBan = 'clientkick {0}';
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
||||||
|
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
|
||||||
|
|
||||||
|
rconParser.Configuration.DefaultInstallationDirectoryHint = '{LocalAppData}/Plutonium/storage/t4';
|
||||||
|
rconParser.Configuration.OverrideDvarNameMapping.Add('fs_homepath', 'fs_localAppData');
|
||||||
|
|
||||||
|
rconParser.Version = 'Plutonium T4 Singleplayer';
|
||||||
|
rconParser.GameName = 5; // T4
|
||||||
|
|
||||||
|
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
eventParser.Configuration.GameDirectory = 'main';
|
||||||
|
|
||||||
|
eventParser.Version = 'Plutonium T4 Singleplayer';
|
||||||
|
},
|
||||||
|
|
||||||
|
onUnloadAsync: function () {
|
||||||
|
},
|
||||||
|
|
||||||
|
onTickAsync: function (server) {
|
||||||
|
}
|
||||||
|
};
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.3,
|
version: 0.4,
|
||||||
name: 'RektT5m Parser',
|
name: 'RektT5m Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -19,6 +19,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff\x01print\n';
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff\x01print\n';
|
||||||
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
||||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
|
|
||||||
rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T5_S MP build 7.0.189 CL(1022875) CODPCAB-V64 CEG Wed Nov 02 18:02:23 2011 win-x86';
|
rconParser.Version = 'Call of Duty Multiplayer - Ship COD_T5_S MP build 7.0.189 CL(1022875) CODPCAB-V64 CEG Wed Nov 02 18:02:23 2011 win-x86';
|
||||||
rconParser.GameName = 6; // T5
|
rconParser.GameName = 6; // T5
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'Diavolo, RaidMax',
|
author: 'Diavolo, RaidMax',
|
||||||
version: 0.1,
|
version: 0.2,
|
||||||
name: 'S1x Parser',
|
name: 'S1x Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.Status.AddMapping(103, 5);
|
rconParser.Configuration.Status.AddMapping(103, 5);
|
||||||
rconParser.Configuration.Status.AddMapping(104, 6);
|
rconParser.Configuration.Status.AddMapping(104, 6);
|
||||||
rconParser.Configuration.WaitForResponse = false;
|
rconParser.Configuration.WaitForResponse = false;
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27016;
|
||||||
|
|
||||||
eventParser.Configuration.GameDirectory = '';
|
eventParser.Configuration.GameDirectory = '';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.1,
|
version: 0.2,
|
||||||
name: 'Call of Duty 5: World at War Parser',
|
name: 'Call of Duty 5: World at War Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ var plugin = {
|
|||||||
eventParser = manager.GenerateDynamicEventParser(this.name);
|
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
||||||
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
rconParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
rconParser.Configuration.DefaultRConPort = 28960;
|
||||||
rconParser.Version = 'Call of Duty Multiplayer COD_WaW MP build 1.7.1263 CL(350073) JADAMS2 Thu Oct 29 15:43:55 2009 win-x86';
|
rconParser.Version = 'Call of Duty Multiplayer COD_WaW MP build 1.7.1263 CL(350073) JADAMS2 Thu Oct 29 15:43:55 2009 win-x86';
|
||||||
|
|
||||||
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
eventParser.Configuration.GuidNumberStyle = 7; // Integer
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.3,
|
version: 0.4,
|
||||||
name: 'Black Ops 3 Parser',
|
name: 'Black Ops 3 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -27,6 +27,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.MapStatus.Pattern = 'Map: (.+)';
|
rconParser.Configuration.MapStatus.Pattern = 'Map: (.+)';
|
||||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; // disables this, because it's useless on T7
|
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined; // disables this, because it's useless on T7
|
||||||
rconParser.Configuration.ServerNotRunningResponse = 'this is here to prevent a hibernating server from being detected as not running';
|
rconParser.Configuration.ServerNotRunningResponse = 'this is here to prevent a hibernating server from being detected as not running';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 27016;
|
||||||
|
|
||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('sv_hostname', 'live_steam_server_name');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('sv_hostname', 'live_steam_server_name');
|
||||||
rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1');
|
rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1');
|
||||||
|
@ -3,7 +3,7 @@ var eventParser;
|
|||||||
|
|
||||||
var plugin = {
|
var plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: 0.8,
|
version: 0.9,
|
||||||
name: 'Tekno MW3 Parser',
|
name: 'Tekno MW3 Parser',
|
||||||
isParser: true,
|
isParser: true,
|
||||||
|
|
||||||
@ -28,6 +28,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.Dvar.AddMapping(107, 1); // RCon DvarValue
|
rconParser.Configuration.Dvar.AddMapping(107, 1); // RCon DvarValue
|
||||||
rconParser.Configuration.Dvar.Pattern = '^(.*)$';
|
rconParser.Configuration.Dvar.Pattern = '^(.*)$';
|
||||||
rconParser.Configuration.NoticeLineSeparator = '. ';
|
rconParser.Configuration.NoticeLineSeparator = '. ';
|
||||||
|
rconParser.Configuration.DefaultRConPort = 8766;
|
||||||
|
|
||||||
rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1');
|
rconParser.Configuration.DefaultDvarValues.Add('sv_running', '1');
|
||||||
rconParser.Configuration.OverrideDvarNameMapping.Add('_website', 'sv_clanWebsite');
|
rconParser.Configuration.OverrideDvarNameMapping.Add('_website', 'sv_clanWebsite');
|
||||||
|
@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using Stats.Client.Abstractions;
|
using Stats.Client.Abstractions;
|
||||||
|
using Stats.Config;
|
||||||
using Stats.Helpers;
|
using Stats.Helpers;
|
||||||
|
|
||||||
namespace Stats.Client
|
namespace Stats.Client
|
||||||
@ -62,7 +63,7 @@ namespace Stats.Client
|
|||||||
.Where(s => s.Client.Level != EFClient.Permission.Banned)
|
.Where(s => s.Client.Level != EFClient.Permission.Banned)
|
||||||
.Where(s => s.TimePlayed >= validPlayTime)
|
.Where(s => s.TimePlayed >= validPlayTime)
|
||||||
.Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo())
|
.Where(s => s.UpdatedAt >= Extensions.FifteenDaysAgo())
|
||||||
.Select(s => s.EloRating * 1/3.0 + s.Skill * 2/3.0).ToListAsync();
|
.Select(s => s.EloRating * 1 / 3.0 + s.Skill * 2 / 3.0).ToListAsync();
|
||||||
var distributionParams = performance.GenerateDistributionParameters();
|
var distributionParams = performance.GenerateDistributionParameters();
|
||||||
distributions.Add(serverId, distributionParams);
|
distributions.Add(serverId, distributionParams);
|
||||||
}
|
}
|
||||||
@ -78,7 +79,10 @@ namespace Stats.Client
|
|||||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime))
|
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(validPlayTime))
|
||||||
.Where(s => s.Skill > 0)
|
.Where(s => s.Skill > 0)
|
||||||
.Where(s => s.EloRating > 0)
|
.Where(s => s.EloRating > 0)
|
||||||
.MaxAsync(s => (double?)s.ZScore, token);
|
.GroupBy(stat => stat.ClientId)
|
||||||
|
.Select(group =>
|
||||||
|
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed))
|
||||||
|
.MaxAsync(avgZScore => (double?) avgZScore, token);
|
||||||
return zScore ?? 0;
|
return zScore ?? 0;
|
||||||
}, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30));
|
}, MaxZScoreCacheKey, Utilities.IsDevelopment ? TimeSpan.FromMinutes(5) : TimeSpan.FromMinutes(30));
|
||||||
|
|
||||||
@ -137,6 +141,7 @@ namespace Stats.Client
|
|||||||
{
|
{
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma;
|
var zScore = (Math.Log(value) - sdParams.Mean) / sdParams.Sigma;
|
||||||
return zScore;
|
return zScore;
|
||||||
}
|
}
|
||||||
@ -144,7 +149,7 @@ namespace Stats.Client
|
|||||||
public async Task<double?> GetRatingForZScore(double? value)
|
public async Task<double?> GetRatingForZScore(double? value)
|
||||||
{
|
{
|
||||||
var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey);
|
var maxZScore = await _maxZScoreCache.GetCacheItem(MaxZScoreCacheKey);
|
||||||
return maxZScore == 0 ? 0 : value.GetRatingForZScore(maxZScore);
|
return maxZScore == 0 ? null : value.GetRatingForZScore(maxZScore);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,8 +9,8 @@ using Data.Models.Client.Stats;
|
|||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using IW4MAdmin.Plugins.Stats.Config;
|
|
||||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
|
using Stats.Config;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
@ -76,7 +76,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
|
|
||||||
var iqList = await iqStats.ToListAsync();
|
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))
|
.FormatExt(index + 1, stats.Name, stats.Kills))
|
||||||
.Prepend(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTKILLS_HEADER"]);
|
.Prepend(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTKILLS_HEADER"]);
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,9 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
{
|
{
|
||||||
var serverId = StatManager.GetIdForServer(s);
|
var serverId = StatManager.GetIdForServer(s);
|
||||||
|
|
||||||
var mostPlayed = new List<string>()
|
var mostPlayed = new List<string>
|
||||||
{
|
{
|
||||||
$"^5--{translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
$"(Color::Accent)--{translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT"]}--"
|
||||||
};
|
};
|
||||||
|
|
||||||
await using var context = contextFactory.CreateContext(false);
|
await using var context = contextFactory.CreateContext(false);
|
||||||
@ -48,7 +48,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
var iqList = await iqStats.ToListAsync();
|
var iqList = await iqStats.ToListAsync();
|
||||||
|
|
||||||
mostPlayed.AddRange(iqList.Select((stats, index) =>
|
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))
|
(DateTime.UtcNow - DateTime.UtcNow.AddSeconds(-stats.TimePlayed))
|
||||||
.HumanizeForCurrentCulture())));
|
.HumanizeForCurrentCulture())));
|
||||||
|
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using System.Linq;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Data.Abstractions;
|
|
||||||
using Data.Models.Client.Stats;
|
|
||||||
using SharedLibraryCore.Database.Models;
|
|
||||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using EFClient = Data.Models.Client.EFClient;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Commands
|
namespace IW4MAdmin.Plugins.Stats.Commands
|
||||||
{
|
{
|
||||||
class TopStats : Command
|
public class TopStats : Command
|
||||||
{
|
{
|
||||||
public static async Task<List<string>> GetTopStats(Server s, ITranslationLookup translationLookup)
|
public static async Task<List<string>> GetTopStats(Server s, ITranslationLookup translationLookup)
|
||||||
{
|
{
|
||||||
long serverId = StatManager.GetIdForServer(s);
|
var serverId = StatManager.GetIdForServer(s);
|
||||||
var topStatsText = new List<string>()
|
var topStatsText = new List<string>()
|
||||||
{
|
{
|
||||||
$"^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 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);
|
topStatsText.AddRange(statsList);
|
||||||
|
|
||||||
@ -41,11 +38,10 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
return topStatsText;
|
return topStatsText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly CommandConfiguration _config;
|
private new readonly CommandConfiguration _config;
|
||||||
private readonly IDatabaseContextFactory _contextFactory;
|
|
||||||
|
|
||||||
public TopStats(CommandConfiguration config, ITranslationLookup translationLookup,
|
public TopStats(CommandConfiguration config, ITranslationLookup translationLookup) : base(config,
|
||||||
IDatabaseContextFactory contextFactory) : base(config, translationLookup)
|
translationLookup)
|
||||||
{
|
{
|
||||||
Name = "topstats";
|
Name = "topstats";
|
||||||
Description = translationLookup["PLUGINS_STATS_COMMANDS_TOP_DESC"];
|
Description = translationLookup["PLUGINS_STATS_COMMANDS_TOP_DESC"];
|
||||||
@ -54,7 +50,6 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
RequiresTarget = false;
|
RequiresTarget = false;
|
||||||
|
|
||||||
_config = config;
|
_config = config;
|
||||||
_contextFactory = contextFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
|
@ -24,9 +24,9 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
Alias = "xlrstats";
|
Alias = "xlrstats";
|
||||||
Permission = EFClient.Permission.User;
|
Permission = EFClient.Permission.User;
|
||||||
RequiresTarget = false;
|
RequiresTarget = false;
|
||||||
Arguments = new CommandArgument[]
|
Arguments = new []
|
||||||
{
|
{
|
||||||
new CommandArgument()
|
new CommandArgument
|
||||||
{
|
{
|
||||||
Name = "player",
|
Name = "player",
|
||||||
Required = false
|
Required = false
|
||||||
@ -61,7 +61,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Target.ClientId, serverId);
|
var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Target.ClientId, serverId);
|
||||||
var performanceRankingString = performanceRanking == 0
|
var performanceRankingString = performanceRanking == 0
|
||||||
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
||||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}/{totalRankedPlayers}";
|
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking}/{totalRankedPlayers}";
|
||||||
|
|
||||||
// target is currently connected so we want their cached stats if they exist
|
// target is currently connected so we want their cached stats if they exist
|
||||||
if (E.Owner.GetClientsAsList().Any(client => client.Equals(E.Target)))
|
if (E.Owner.GetClientsAsList().Any(client => client.Equals(E.Target)))
|
||||||
@ -73,14 +73,15 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
if (pStats == null)
|
if (pStats == null)
|
||||||
{
|
{
|
||||||
await using var context = _contextFactory.CreateContext(false);
|
await using var context = _contextFactory.CreateContext(false);
|
||||||
pStats = (await context.Set<EFClientStatistics>()
|
pStats = await context.Set<EFClientStatistics>()
|
||||||
.FirstOrDefaultAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
|
.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
|
// if it's still null then they've not gotten a kill or death yet
|
||||||
statLine = pStats == null
|
statLine = pStats == null
|
||||||
? _translationLookup["PLUGINS_STATS_COMMANDS_NOTAVAILABLE"]
|
? _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
|
// getting self stats
|
||||||
@ -89,7 +90,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Origin.ClientId, serverId);
|
var performanceRanking = await Plugin.Manager.GetClientOverallRanking(E.Origin.ClientId, serverId);
|
||||||
var performanceRankingString = performanceRanking == 0
|
var performanceRankingString = performanceRanking == 0
|
||||||
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"]
|
||||||
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}/{totalRankedPlayers}";
|
: $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} (Color::Accent)#{performanceRanking}/{totalRankedPlayers}";
|
||||||
|
|
||||||
// check if current client is connected to the server
|
// check if current client is connected to the server
|
||||||
if (E.Owner.GetClientsAsList().Any(client => client.Equals(E.Origin)))
|
if (E.Owner.GetClientsAsList().Any(client => client.Equals(E.Origin)))
|
||||||
@ -108,7 +109,8 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
|||||||
// if it's still null then they've not gotten a kill or death yet
|
// if it's still null then they've not gotten a kill or death yet
|
||||||
statLine = pStats == null
|
statLine = pStats == null
|
||||||
? _translationLookup["PLUGINS_STATS_COMMANDS_NOTAVAILABLE"]
|
? _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))
|
if (E.Message.IsBroadcastCommand(_config.BroadcastCommandPrefix))
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using SharedLibraryCore;
|
using System;
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using Stats.Config;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using IW4MAdmin.Plugins.Stats.Config;
|
||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
using static IW4MAdmin.Plugins.Stats.Cheat.Detection;
|
using static IW4MAdmin.Plugins.Stats.Cheat.Detection;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats.Config
|
namespace Stats.Config
|
||||||
{
|
{
|
||||||
public class StatsConfiguration : IBaseConfiguration
|
public class StatsConfiguration : IBaseConfiguration
|
||||||
{
|
{
|
||||||
@ -74,28 +74,28 @@ namespace IW4MAdmin.Plugins.Stats.Config
|
|||||||
public IBaseConfiguration Generate()
|
public IBaseConfiguration Generate()
|
||||||
{
|
{
|
||||||
AnticheatConfiguration.Enable =
|
AnticheatConfiguration.Enable =
|
||||||
Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"]);
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_SETUP_ENABLEAC"].PromptBool();
|
||||||
KillstreakMessages = new List<StreakMessageConfiguration>()
|
KillstreakMessages = new List<StreakMessageConfiguration>
|
||||||
{
|
{
|
||||||
new StreakMessageConfiguration()
|
new StreakMessageConfiguration
|
||||||
{
|
{
|
||||||
Count = -1,
|
Count = -1,
|
||||||
Message = "Try not to kill yourself anymore"
|
Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_SUICIDE"]
|
||||||
},
|
},
|
||||||
new StreakMessageConfiguration()
|
new StreakMessageConfiguration
|
||||||
{
|
{
|
||||||
Count = 5,
|
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,
|
Count = 10,
|
||||||
Message = "Amazing! ^510 kills ^7without dying!"
|
Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_STREAK_MESSAGE_10"]
|
||||||
},
|
},
|
||||||
new StreakMessageConfiguration()
|
new StreakMessageConfiguration
|
||||||
{
|
{
|
||||||
Count = 25,
|
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()
|
new StreakMessageConfiguration()
|
||||||
{
|
{
|
||||||
Count = 5,
|
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()
|
new StreakMessageConfiguration()
|
||||||
{
|
{
|
||||||
Count = 10,
|
Count = 10,
|
||||||
Message = "Seriously? ^510 deaths ^7without getting a kill?"
|
Message = Utilities.CurrentLocalization.LocalizationIndex["STATS_DEATH_STREAK_MESSAGE_10"]
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,8 +19,12 @@ using Data.Models.Client;
|
|||||||
using Data.Models.Client.Stats;
|
using Data.Models.Client.Stats;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
using Humanizer.Localisation;
|
using Humanizer.Localisation;
|
||||||
|
using Microsoft.Data.Sqlite;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MySql.Data.MySqlClient;
|
||||||
|
using Npgsql;
|
||||||
using Stats.Client.Abstractions;
|
using Stats.Client.Abstractions;
|
||||||
|
using Stats.Config;
|
||||||
using Stats.Helpers;
|
using Stats.Helpers;
|
||||||
using static IW4MAdmin.Plugins.Stats.Cheat.Detection;
|
using static IW4MAdmin.Plugins.Stats.Cheat.Detection;
|
||||||
using EFClient = SharedLibraryCore.Database.Models.EFClient;
|
using EFClient = SharedLibraryCore.Database.Models.EFClient;
|
||||||
@ -117,7 +121,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
return (ranking) => ranking.ServerId == serverId
|
return (ranking) => ranking.ServerId == serverId
|
||||||
&& ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned
|
&& ranking.Client.Level != Data.Models.Client.EFClient.Permission.Banned
|
||||||
&& ranking.Client.LastConnection >= Extensions.FifteenDaysAgo()
|
&& ranking.CreatedDateTime >= Extensions.FifteenDaysAgo()
|
||||||
&& ranking.ZScore != null
|
&& ranking.ZScore != null
|
||||||
&& ranking.PerformanceMetric != null
|
&& ranking.PerformanceMetric != null
|
||||||
&& ranking.Newest
|
&& ranking.Newest
|
||||||
@ -431,6 +435,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pl.ClientId <= 0)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Stats for {Client} are not yet initialized", pl.ToString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// get the client's stats from the database if it exists, otherwise create and attach a new one
|
// get the client's stats from the database if it exists, otherwise create and attach a new one
|
||||||
// if this fails we want to throw an exception
|
// if this fails we want to throw an exception
|
||||||
|
|
||||||
@ -510,6 +520,15 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
return clientStats;
|
return clientStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
catch (DbUpdateException updateException) when (
|
||||||
|
updateException.InnerException is PostgresException {SqlState: "23503"}
|
||||||
|
|| updateException.InnerException is SqliteException {SqliteErrorCode: 787}
|
||||||
|
|| updateException.InnerException is MySqlException {SqlState: "23503"})
|
||||||
|
{
|
||||||
|
_log.LogWarning("Trying to add {Client} to stats before they have been added to the database",
|
||||||
|
pl.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_log.LogError(ex, "Could not add client to stats {@client}", pl.ToString());
|
_log.LogError(ex, "Could not add client to stats {@client}", pl.ToString());
|
||||||
@ -652,6 +671,12 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
var clientDetection = attacker.GetAdditionalProperty<Detection>(CLIENT_DETECTIONS_KEY);
|
var clientDetection = attacker.GetAdditionalProperty<Detection>(CLIENT_DETECTIONS_KEY);
|
||||||
var clientStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
var clientStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
||||||
|
|
||||||
|
if (clientDetection == null || clientStats?.ClientId == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Client stats state for {Client} is not yet initialized", attacker.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
waiter = clientStats.ProcessingHit;
|
waiter = clientStats.ProcessingHit;
|
||||||
await waiter.WaitAsync(Utilities.DefaultCommandTimeout, Plugin.ServerManager.CancellationToken);
|
await waiter.WaitAsync(Utilities.DefaultCommandTimeout, Plugin.ServerManager.CancellationToken);
|
||||||
|
|
||||||
@ -870,7 +895,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
public async Task AddStandardKill(EFClient attacker, EFClient victim)
|
public async Task AddStandardKill(EFClient attacker, EFClient victim)
|
||||||
{
|
{
|
||||||
long serverId = GetIdForServer(attacker.CurrentServer);
|
var serverId = GetIdForServer(attacker.CurrentServer);
|
||||||
|
|
||||||
var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
var attackerStats = attacker.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
||||||
var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
var victimStats = victim.GetAdditionalProperty<EFClientStatistics>(CLIENT_STATS_KEY);
|
||||||
@ -878,6 +903,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// update the total stats
|
// update the total stats
|
||||||
_servers[serverId].ServerStatistics.TotalKills += 1;
|
_servers[serverId].ServerStatistics.TotalKills += 1;
|
||||||
|
|
||||||
|
if (attackerStats == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Stats for {Client} are not yet initialized", attacker.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (victimStats == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Stats for {Client} are not yet initialized", victim.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// this happens when the round has changed
|
// this happens when the round has changed
|
||||||
if (attackerStats.SessionScore == 0)
|
if (attackerStats.SessionScore == 0)
|
||||||
{
|
{
|
||||||
@ -936,7 +973,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
|
|
||||||
// update their performance
|
// update their performance
|
||||||
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >=
|
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >=
|
||||||
(Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 10.0 : 2.5))
|
(Utilities.IsDevelopment ? 0.5 : _configHandler.Configuration().EnableAdvancedMetrics ? 5.0 : 2.5))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -1153,16 +1190,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId)
|
public async Task UpdateHistoricalRanking(int clientId, EFClientStatistics clientStats, long serverId)
|
||||||
{
|
{
|
||||||
await using var context = _contextFactory.CreateContext();
|
await using var context = _contextFactory.CreateContext();
|
||||||
|
var minPlayTime = _configHandler.Configuration().TopPlayersMinPlayTime;
|
||||||
|
|
||||||
var performances = await context.Set<EFClientStatistics>()
|
var performances = await context.Set<EFClientStatistics>()
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(stat => stat.ClientId == clientId)
|
.Where(stat => stat.ClientId == clientId)
|
||||||
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
|
.Where(stat => stat.ServerId != serverId) // ignore the one we're currently tracking
|
||||||
.Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo())
|
.Where(stats => stats.UpdatedAt >= Extensions.FifteenDaysAgo())
|
||||||
.Where(stats => stats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime)
|
.Where(stats => stats.TimePlayed >= minPlayTime)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
if (clientStats.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime)
|
if (clientStats.TimePlayed >= minPlayTime)
|
||||||
{
|
{
|
||||||
clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId,
|
clientStats.ZScore = await _serverDistributionCalculator.GetZScoreForServer(serverId,
|
||||||
clientStats.Performance);
|
clientStats.Performance);
|
||||||
@ -1173,7 +1211,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
_configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId))
|
_configHandler.Configuration().TopPlayersMinPlayTime, clientStats.ZScore, serverId))
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
var serverRankingSnapshot = new EFClientRankingHistory()
|
var serverRankingSnapshot = new EFClientRankingHistory
|
||||||
{
|
{
|
||||||
ClientId = clientId,
|
ClientId = clientId,
|
||||||
ServerId = serverId,
|
ServerId = serverId,
|
||||||
@ -1190,14 +1228,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
performances.Add(clientStats);
|
performances.Add(clientStats);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (performances.Any(performance => performance.TimePlayed >= _configHandler.Configuration().TopPlayersMinPlayTime))
|
if (performances.Any(performance => performance.TimePlayed >= minPlayTime))
|
||||||
{
|
{
|
||||||
var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), _configHandler.Configuration().TopPlayersMinPlayTime);
|
var aggregateZScore = performances.WeightValueByPlaytime(nameof(EFClientStatistics.ZScore), minPlayTime);
|
||||||
|
|
||||||
int? aggregateRanking = await context.Set<EFClientStatistics>()
|
int? aggregateRanking = await context.Set<EFClientStatistics>()
|
||||||
.Where(stat => stat.ClientId != clientId)
|
.Where(stat => stat.ClientId != clientId)
|
||||||
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(_configHandler.Configuration()
|
.Where(AdvancedClientStatsResourceQueryHelper.GetRankingFunc(minPlayTime))
|
||||||
.TopPlayersMinPlayTime))
|
|
||||||
.GroupBy(stat => stat.ClientId)
|
.GroupBy(stat => stat.ClientId)
|
||||||
.Where(group =>
|
.Where(group =>
|
||||||
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) >
|
group.Sum(stat => stat.ZScore * stat.TimePlayed) / group.Sum(stat => stat.TimePlayed) >
|
||||||
@ -1205,12 +1242,21 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
.Select(c => c.Key)
|
.Select(c => c.Key)
|
||||||
.CountAsync();
|
.CountAsync();
|
||||||
|
|
||||||
var aggregateRankingSnapshot = new EFClientRankingHistory()
|
var newPerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore);
|
||||||
|
|
||||||
|
if (newPerformanceMetric == null)
|
||||||
|
{
|
||||||
|
_log.LogWarning("Could not determine performance metric for {Client} {AggregateZScore}",
|
||||||
|
clientStats.Client?.ToString(), aggregateZScore);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aggregateRankingSnapshot = new EFClientRankingHistory
|
||||||
{
|
{
|
||||||
ClientId = clientId,
|
ClientId = clientId,
|
||||||
ZScore = aggregateZScore,
|
ZScore = aggregateZScore,
|
||||||
Ranking = aggregateRanking,
|
Ranking = aggregateRanking,
|
||||||
PerformanceMetric = await _serverDistributionCalculator.GetRatingForZScore(aggregateZScore),
|
PerformanceMetric = newPerformanceMetric,
|
||||||
Newest = true,
|
Newest = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1264,12 +1310,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
if (!suicide)
|
if (!suicide)
|
||||||
{
|
{
|
||||||
attackerStats.Kills += 1;
|
attackerStats.Kills += 1;
|
||||||
|
attackerStats.MatchData.Kills += 1;
|
||||||
attackerStats.SessionKills += 1;
|
attackerStats.SessionKills += 1;
|
||||||
attackerStats.KillStreak += 1;
|
attackerStats.KillStreak += 1;
|
||||||
attackerStats.DeathStreak = 0;
|
attackerStats.DeathStreak = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
victimStats.Deaths += 1;
|
victimStats.Deaths += 1;
|
||||||
|
victimStats.MatchData.Deaths += 1;
|
||||||
victimStats.SessionDeaths += 1;
|
victimStats.SessionDeaths += 1;
|
||||||
victimStats.DeathStreak += 1;
|
victimStats.DeathStreak += 1;
|
||||||
victimStats.KillStreak = 0;
|
victimStats.KillStreak = 0;
|
||||||
@ -1419,6 +1467,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
{
|
{
|
||||||
session.stat?.StartNewSession();
|
session.stat?.StartNewSession();
|
||||||
session.detection?.OnMapChange();
|
session.detection?.OnMapChange();
|
||||||
|
session.stat?.MatchData?.StartNewMatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using IW4MAdmin.Plugins.Stats.Config;
|
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Dtos.Meta.Responses;
|
using SharedLibraryCore.Dtos.Meta.Responses;
|
||||||
@ -15,11 +14,10 @@ using Data.Abstractions;
|
|||||||
using Data.Models.Client;
|
using Data.Models.Client;
|
||||||
using Data.Models.Client.Stats;
|
using Data.Models.Client.Stats;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
using Humanizer;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SharedLibraryCore.Commands;
|
|
||||||
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
||||||
using Stats.Client.Abstractions;
|
using Stats.Client.Abstractions;
|
||||||
|
using Stats.Config;
|
||||||
using EFClient = SharedLibraryCore.Database.Models.EFClient;
|
using EFClient = SharedLibraryCore.Database.Models.EFClient;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Stats
|
namespace IW4MAdmin.Plugins.Stats
|
||||||
@ -88,7 +86,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Command:
|
case GameEvent.EventType.Command:
|
||||||
var shouldPersist = !string.IsNullOrEmpty(E.Data) &&
|
var shouldPersist = !string.IsNullOrEmpty(E.Data) &&
|
||||||
E.Extra is SayCommand;
|
E.Extra?.GetType().Name == "SayCommand";
|
||||||
if (shouldPersist)
|
if (shouldPersist)
|
||||||
{
|
{
|
||||||
await Manager.AddMessageAsync(E.Origin.ClientId, StatManager.GetIdForServer(S), false, E.Data);
|
await Manager.AddMessageAsync(E.Origin.ClientId, StatManager.GetIdForServer(S), false, E.Data);
|
||||||
|
@ -12,12 +12,12 @@
|
|||||||
<Description>Client Statistics Plugin for IW4MAdmin</Description>
|
<Description>Client Statistics Plugin for IW4MAdmin</Description>
|
||||||
<Copyright>2018</Copyright>
|
<Copyright>2018</Copyright>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
@ -45,16 +45,16 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
|
|
||||||
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
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;
|
var newPlayer = gameEvent.Origin;
|
||||||
if ((newPlayer.Level >= Permission.Trusted && !E.Origin.Masked) ||
|
if (newPlayer.Level >= Permission.Trusted && !gameEvent.Origin.Masked||
|
||||||
(!string.IsNullOrEmpty(newPlayer.GetAdditionalProperty<string>("ClientTag")) &&
|
!string.IsNullOrEmpty(newPlayer.GetAdditionalProperty<string>("ClientTag")) &&
|
||||||
newPlayer.Level != Permission.Flagged && newPlayer.Level != Permission.Banned &&
|
newPlayer.Level != Permission.Flagged && newPlayer.Level != Permission.Banned &&
|
||||||
!newPlayer.Masked))
|
!newPlayer.Masked)
|
||||||
E.Owner.Broadcast(
|
gameEvent.Owner.Broadcast(
|
||||||
await ProcessAnnouncement(_configHandler.Configuration().PrivilegedAnnouncementMessage,
|
await ProcessAnnouncement(_configHandler.Configuration().PrivilegedAnnouncementMessage,
|
||||||
newPlayer));
|
newPlayer));
|
||||||
|
|
||||||
@ -73,10 +73,11 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
.FirstOrDefaultAsync();
|
.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
|
else
|
||||||
E.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().UserAnnouncementMessage,
|
gameEvent.Owner.Broadcast(await ProcessAnnouncement(_configHandler.Configuration().UserAnnouncementMessage,
|
||||||
newPlayer));
|
newPlayer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +86,7 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
{
|
{
|
||||||
msg = msg.Replace("{{ClientName}}", joining.Name);
|
msg = msg.Replace("{{ClientName}}", joining.Name);
|
||||||
msg = msg.Replace("{{ClientLevel}}",
|
msg = msg.Replace("{{ClientLevel}}",
|
||||||
$"{Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name)}{(string.IsNullOrEmpty(joining.GetAdditionalProperty<string>("ClientTag")) ? "" : $" ^7({joining.GetAdditionalProperty<string>("ClientTag")}^7)")}");
|
$"{Utilities.ConvertLevelToColor(joining.Level, joining.ClientPermission.Name)}{(string.IsNullOrEmpty(joining.GetAdditionalProperty<string>("ClientTag")) ? "" : $" (Color::White)({joining.GetAdditionalProperty<string>("ClientTag")}(Color::White))")}");
|
||||||
// this prevents it from trying to evaluate it every message
|
// this prevents it from trying to evaluate it every message
|
||||||
if (msg.Contains("{{ClientLocation}}"))
|
if (msg.Contains("{{ClientLocation}}"))
|
||||||
{
|
{
|
||||||
@ -104,14 +105,13 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<string> GetCountryName(string ip)
|
private async Task<string> GetCountryName(string ip)
|
||||||
{
|
{
|
||||||
using (var wc = new WebClient())
|
using var wc = new WebClient();
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string response =
|
var response =
|
||||||
await wc.DownloadStringTaskAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}"));
|
await wc.DownloadStringTaskAsync(new Uri($"http://extreme-ip-lookup.com/json/{ip}?key=demo"));
|
||||||
var responseObj = JObject.Parse(response);
|
var responseObj = JObject.Parse(response);
|
||||||
response = responseObj["country"].ToString();
|
response = responseObj["country"]?.ToString();
|
||||||
|
|
||||||
return string.IsNullOrEmpty(response)
|
return string.IsNullOrEmpty(response)
|
||||||
? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"]
|
? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"]
|
||||||
@ -124,5 +124,4 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
@ -20,7 +20,7 @@
|
|||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2021.8.31.1" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2022.1.20.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -11,9 +11,9 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
|
|
||||||
public IBaseConfiguration Generate()
|
public IBaseConfiguration Generate()
|
||||||
{
|
{
|
||||||
UserAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERANNOUNCE"];
|
UserAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERANNOUNCE_V2"];
|
||||||
UserWelcomeMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERWELCOME"];
|
UserWelcomeMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_USERWELCOME_V2"];
|
||||||
PrivilegedAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_PRIVANNOUNCE"];
|
PrivilegedAnnouncementMessage = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_PRIVANNOUNCE_V2"];
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_translationLookup = layout;
|
_translationLookup = layout;
|
||||||
|
SupportedGames = new Game[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -61,7 +62,7 @@ namespace SharedLibraryCore
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper property to provide the syntax of the command
|
/// Helper property to provide the syntax of the command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} {_config.CommandPrefix}{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>
|
/// <summary>
|
||||||
/// Alternate name for this command to be executed by
|
/// Alternate name for this command to be executed by
|
||||||
|
@ -83,9 +83,10 @@ namespace SharedLibraryCore.Commands
|
|||||||
var found = await Manager.GetClientService().Get(dbID);
|
var found = await Manager.GetClientService().Get(dbID);
|
||||||
if (found != null)
|
if (found != null)
|
||||||
{
|
{
|
||||||
|
found = Manager.FindActiveClient(found);
|
||||||
E.Target = found;
|
E.Target = found;
|
||||||
E.Target.CurrentServer = E.Owner;
|
E.Target.CurrentServer = found.CurrentServer ?? E.Owner;
|
||||||
E.Data = String.Join(" ", Args.Skip(1));
|
E.Data = string.Join(" ", Args.Skip(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
|
E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
|
||||||
foreach (var p in matchingPlayers)
|
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}");
|
throw new CommandException($"{E.Origin} had multiple players found for {C.Name}");
|
||||||
}
|
}
|
||||||
|
@ -219,72 +219,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints out a message to all clients on the server
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints out a message to all clients on all servers
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Temporarily bans a client
|
/// Temporarily bans a client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -452,131 +386,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints client information
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List online clients
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints help information
|
|
||||||
/// </summary>
|
|
||||||
public class HelpCommand : Command
|
|
||||||
{
|
|
||||||
public HelpCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
|
|
||||||
{
|
|
||||||
Name = "help";
|
|
||||||
Description = _translationLookup["COMMANDS_HELP_DESC"];
|
|
||||||
Alias = "h";
|
|
||||||
Permission = Permission.User;
|
|
||||||
RequiresTarget = false;
|
|
||||||
Arguments = new[]
|
|
||||||
{
|
|
||||||
new CommandArgument()
|
|
||||||
{
|
|
||||||
Name = _translationLookup["COMMANDS_ARGS_COMMANDS"],
|
|
||||||
Required = false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task ExecuteAsync(GameEvent E)
|
|
||||||
{
|
|
||||||
string cmd = E.Data.Trim();
|
|
||||||
|
|
||||||
if (cmd.Length > 2)
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
foreach (var command in E.Owner.Manager.GetCommands())
|
|
||||||
{
|
|
||||||
if (command.Name == cmd.ToLower() ||
|
|
||||||
command.Alias == cmd.ToLower())
|
|
||||||
{
|
|
||||||
E.Origin.Tell($"[^3{command.Name}^7] {command.Description}");
|
|
||||||
E.Origin.Tell(command.Syntax);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
E.Origin.Tell(_translationLookup["COMMANDS_HELP_NOTFOUND"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
StringBuilder helpResponse = new StringBuilder();
|
|
||||||
var CommandList = E.Owner.Manager.GetCommands();
|
|
||||||
|
|
||||||
foreach (Command C in CommandList)
|
|
||||||
{
|
|
||||||
if (E.Origin.Level >= C.Permission)
|
|
||||||
{
|
|
||||||
helpResponse.Append(" [^3" + C.Name + "^7] ");
|
|
||||||
if (count >= 4)
|
|
||||||
{
|
|
||||||
E.Origin.Tell(helpResponse.ToString());
|
|
||||||
helpResponse = new StringBuilder();
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
E.Origin.Tell(helpResponse.ToString());
|
|
||||||
E.Origin.Tell(_translationLookup["COMMANDS_HELP_MOREINFO"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fast restarts the map
|
/// Fast restarts the map
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -596,7 +405,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
await E.Owner.ExecuteCommandAsync("fast_restart");
|
await E.Owner.ExecuteCommandAsync("fast_restart");
|
||||||
|
|
||||||
var _ = !E.Origin.Masked ?
|
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"]);
|
E.Owner.Broadcast(_translationLookup["COMMANDS_FASTRESTART_MASKED"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -618,7 +427,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
_ = !E.Origin.Masked ?
|
_ = !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);
|
E.Owner.Broadcast(_translationLookup["COMMANDS_MAPROTATE"], E.Origin);
|
||||||
|
|
||||||
await Task.Delay(E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000);
|
await Task.Delay(E.Owner.Manager.GetApplicationSettings().Configuration().MapChangeDelaySeconds * 1000);
|
||||||
@ -692,13 +501,13 @@ namespace SharedLibraryCore.Commands
|
|||||||
else if (gameEvent.Origin.Level < Permission.Owner && !steppedPrivileges)
|
else if (gameEvent.Origin.Level < Permission.Owner && !steppedPrivileges)
|
||||||
{
|
{
|
||||||
// only the owner is allowed to set levels
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (gameEvent.Target.Level == Permission.Flagged)
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -707,8 +516,8 @@ namespace SharedLibraryCore.Commands
|
|||||||
{
|
{
|
||||||
// can't promote a client to higher than your current perms
|
// can't promote a client to higher than your current perms
|
||||||
// or your peer
|
// or your peer
|
||||||
gameEvent.Origin.Tell(string.Format(_translationLookup["COMMANDS_SETLEVEL_LEVELTOOHIGH"], gameEvent.Target.Name, (gameEvent.Origin.Level - 1).ToLocalizedLevelName()));
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_LEVELTOOHIGH_V2"]
|
||||||
return;
|
.FormatExt(gameEvent.Target.Name, (gameEvent.Origin.Level - 1).ToLocalizedLevelName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// valid
|
// valid
|
||||||
@ -728,7 +537,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
if (result.FailReason == GameEvent.EventFailReason.Invalid)
|
if (result.FailReason == GameEvent.EventFailReason.Invalid)
|
||||||
{
|
{
|
||||||
gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_INVALID"]
|
gameEvent.Origin.Tell(_translationLookup["COMMANDS_SETLEVEL_INVALID"]
|
||||||
.FormatExt(gameEvent.Target.Name, newPerm.ToString()));
|
.FormatExt(gameEvent.Target.Name + "(Color::White)", newPerm.ToString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,42 +616,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lists all unmasked admins
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to load the specified map
|
/// Attempts to load the specified map
|
||||||
@ -883,50 +656,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds player by name
|
|
||||||
/// </summary>
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lists server and global rules
|
/// Lists server and global rules
|
||||||
@ -976,40 +705,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a private message to another player
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flag given client for specified reason
|
/// Flag given client for specified reason
|
||||||
@ -1105,116 +801,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Report client for given reason
|
|
||||||
/// </summary>
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List all reports on the server
|
|
||||||
/// </summary>
|
|
||||||
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<Report>();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Masks client from announcements and online admin list
|
/// Masks client from announcements and online admin list
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1292,49 +878,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lists alises of specified client
|
|
||||||
/// </summary>
|
|
||||||
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<string>(E.Target.AliasLink.Children.Select(a => a.Name));
|
|
||||||
var IPs = new List<string>(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Executes RCon command
|
/// Executes RCon command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1372,33 +915,6 @@ namespace SharedLibraryCore.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lists the loaded plugins
|
|
||||||
/// </summary>
|
|
||||||
/*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;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lists external IP
|
/// Lists external IP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -1558,11 +1074,11 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
if (E.Target == null)
|
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
|
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;
|
return Task.CompletedTask;
|
||||||
|
@ -108,7 +108,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
public string ConnectionString { get; set; }
|
public string ConnectionString { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_RCON_POLLRATE")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_RCON_POLLRATE")]
|
||||||
public int RConPollRate { get; set; } = 5000;
|
public int RConPollRate { get; set; } = 8000;
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MAX_TB")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MAX_TB")]
|
||||||
public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0);
|
public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0);
|
||||||
@ -116,6 +116,9 @@ namespace SharedLibraryCore.Configuration
|
|||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_COLOR_CODES")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_COLOR_CODES")]
|
||||||
public bool EnableColorCodes { get; set; }
|
public bool EnableColorCodes { get; set; }
|
||||||
|
|
||||||
|
[ConfigurationIgnore]
|
||||||
|
public string IngameAccentColorKey { get; set; } = "Cyan";
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGE_PERIOD")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGE_PERIOD")]
|
||||||
public int AutoMessagePeriod { get; set; }
|
public int AutoMessagePeriod { get; set; }
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
public string[] AutoMessages { get; set; }
|
public string[] AutoMessages { get; set; }
|
||||||
public string[] GlobalRules { get; set; }
|
public string[] GlobalRules { get; set; }
|
||||||
public MapConfiguration[] Maps { get; set; }
|
public MapConfiguration[] Maps { get; set; }
|
||||||
|
public GametypeConfiguration[] Gametypes { get; set; }
|
||||||
public QuickMessageConfiguration[] QuickMessages {get; set;}
|
public QuickMessageConfiguration[] QuickMessages {get; set;}
|
||||||
public string[] DisallowedClientNames { get; set; }
|
public string[] DisallowedClientNames { get; set; }
|
||||||
public GameStringConfiguration GameStrings { get; set; }
|
public GameStringConfiguration GameStrings { get; set; }
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Configuration.Extensions
|
||||||
|
{
|
||||||
|
public static class ConfigurationExtensions
|
||||||
|
{
|
||||||
|
public static void TrySetIpAddress(this ServerConfiguration config)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var interfaces = NetworkInterface.GetAllNetworkInterfaces().Where(nic =>
|
||||||
|
nic.OperationalStatus == OperationalStatus.Up &&
|
||||||
|
(nic.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
|
||||||
|
nic.NetworkInterfaceType == NetworkInterfaceType.Ethernet && nic.GetIPProperties().UnicastAddresses
|
||||||
|
.Any(addr => addr.Address.AddressFamily == AddressFamily.InterNetwork))).ToList();
|
||||||
|
|
||||||
|
var publicInterfaces = interfaces.Where(nic =>
|
||||||
|
nic.GetIPProperties().UnicastAddresses.Any(info =>
|
||||||
|
info.Address.AddressFamily == AddressFamily.InterNetwork && !info.Address.IsInternal()))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
config.IPAddress = publicInterfaces.Any()
|
||||||
|
? publicInterfaces.First().GetIPProperties().UnicastAddresses.First().Address.ToString()
|
||||||
|
: IPAddress.Loopback.ToString();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
config.IPAddress = IPAddress.Loopback.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (string, string)[] TryGetRConPasswords(this IRConParser parser)
|
||||||
|
{
|
||||||
|
string searchPath = null;
|
||||||
|
var isRegistryKey = parser.Configuration.DefaultInstallationDirectoryHint.Contains("HKEY_");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isRegistryKey)
|
||||||
|
{
|
||||||
|
var result = Registry.GetValue(parser.Configuration.DefaultInstallationDirectoryHint, null, null);
|
||||||
|
|
||||||
|
if (result == null)
|
||||||
|
{
|
||||||
|
return new (string, string)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
searchPath = Path.Combine(result.ToString().Split(Path.DirectorySeparatorChar)
|
||||||
|
.Where(p => !p.Contains(".exe"))
|
||||||
|
.Select(p => p.Replace("\"", "")).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var path = parser.Configuration.DefaultInstallationDirectoryHint.Replace("{LocalAppData}",
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData));
|
||||||
|
|
||||||
|
if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
searchPath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(searchPath))
|
||||||
|
{
|
||||||
|
return new (string, string)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var possibleFiles = Directory.GetFiles(searchPath, "*.cfg", SearchOption.AllDirectories);
|
||||||
|
|
||||||
|
if (!possibleFiles.Any())
|
||||||
|
{
|
||||||
|
return new (string, string)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var possiblePasswords = possibleFiles.SelectMany(File.ReadAllLines)
|
||||||
|
.Select(line => Regex.Match(line, "^(\\/\\/)?.*rcon_password +\"?([^\\/\"\n]+)\"?"))
|
||||||
|
.Where(match => match.Success)
|
||||||
|
.Select(match =>
|
||||||
|
!string.IsNullOrEmpty(match.Groups[1].ToString())
|
||||||
|
? (match.Groups[2].ToString(),
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["SETUP_RCON_PASSWORD_COMMENTED"])
|
||||||
|
: (match.Groups[2].ToString(), null));
|
||||||
|
|
||||||
|
return possiblePasswords.ToArray();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new (string, string)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
SharedLibraryCore/Configuration/GametypeConfiguration.cs
Normal file
8
SharedLibraryCore/Configuration/GametypeConfiguration.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace SharedLibraryCore.Configuration
|
||||||
|
{
|
||||||
|
public class GametypeConfiguration
|
||||||
|
{
|
||||||
|
public Server.Game Game { get; set; }
|
||||||
|
public Gametype[] Gametypes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +1,8 @@
|
|||||||
using System;
|
namespace SharedLibraryCore.Configuration
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using static SharedLibraryCore.Server;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore.Configuration
|
|
||||||
{
|
{
|
||||||
public class MapConfiguration
|
public class MapConfiguration
|
||||||
{
|
{
|
||||||
public Game Game { get; set; }
|
public Server.Game Game { get; set; }
|
||||||
public List<Map> Maps { get; set; }
|
public Map[] Maps { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using SharedLibraryCore.Configuration.Extensions;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Configuration
|
namespace SharedLibraryCore.Configuration
|
||||||
{
|
{
|
||||||
@ -10,96 +11,145 @@ namespace SharedLibraryCore.Configuration
|
|||||||
{
|
{
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")]
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PORT")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PORT")]
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PASSWORD")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PASSWORD")]
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")]
|
||||||
public string[] Rules { get; set; } = new string[0];
|
public string[] Rules { get; set; } = new string[0];
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")]
|
||||||
public string[] AutoMessages { get; set; } = new string[0];
|
public string[] AutoMessages { get; set; } = new string[0];
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")]
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
public string ManualLogPath { get; set; }
|
public string ManualLogPath { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RCON_PARSER")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RCON_PARSER")]
|
||||||
public string RConParserVersion { get; set; }
|
public string RConParserVersion { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_EVENT_PARSER")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_EVENT_PARSER")]
|
||||||
public string EventParserVersion { get; set; }
|
public string EventParserVersion { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RESERVED_SLOT")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RESERVED_SLOT")]
|
||||||
public int ReservedSlotNumber { get; set; }
|
public int ReservedSlotNumber { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_GAME_LOG_SERVER")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_GAME_LOG_SERVER")]
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
public Uri GameLogServerUrl { get; set; }
|
public Uri GameLogServerUrl { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_CUSTOM_HOSTNAME")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_CUSTOM_HOSTNAME")]
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
public string CustomHostname { get; set; }
|
public string CustomHostname { get; set; }
|
||||||
|
|
||||||
private readonly IList<IRConParser> rconParsers;
|
private readonly IList<IRConParser> _rconParsers;
|
||||||
private readonly IList<IEventParser> eventParsers;
|
private IRConParser _selectedParser;
|
||||||
|
|
||||||
public ServerConfiguration()
|
public ServerConfiguration()
|
||||||
{
|
{
|
||||||
rconParsers = new List<IRConParser>();
|
_rconParsers = new List<IRConParser>();
|
||||||
eventParsers = new List<IEventParser>();
|
|
||||||
Rules = new string[0];
|
Rules = new string[0];
|
||||||
AutoMessages = new string[0];
|
AutoMessages = new string[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRConParser(IRConParser parser)
|
public void AddRConParser(IRConParser parser)
|
||||||
{
|
{
|
||||||
rconParsers.Add(parser);
|
_rconParsers.Add(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEventParser(IEventParser parser)
|
public void AddEventParser(IEventParser parser)
|
||||||
{
|
{
|
||||||
eventParsers.Add(parser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ModifyParsers()
|
public void ModifyParsers()
|
||||||
{
|
{
|
||||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||||
var parserVersions = rconParsers.Select(_parser => _parser.Name).ToArray();
|
var parserVersions = _rconParsers.Select(p => p.Name).ToArray();
|
||||||
var selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_RCON_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
|
var (index, parser) = loc["SETUP_SERVER_RCON_PARSER_VERSION"].PromptSelection(parserVersions[0],
|
||||||
|
null, parserVersions);
|
||||||
|
|
||||||
if (selection.Item1 >= 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
RConParserVersion = rconParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (selection.Item1 > 0 && !rconParsers[selection.Item1].CanGenerateLogPath)
|
_selectedParser = _rconParsers.FirstOrDefault(p => p.Name == parser);
|
||||||
|
RConParserVersion = _selectedParser?.Version;
|
||||||
|
EventParserVersion = _selectedParser?.Version;
|
||||||
|
|
||||||
|
if (index <= 0 || _rconParsers[index].CanGenerateLogPath)
|
||||||
{
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine(loc["SETUP_SERVER_NO_LOG"]);
|
Console.WriteLine(loc["SETUP_SERVER_NO_LOG"]);
|
||||||
ManualLogPath = Utilities.PromptString(loc["SETUP_SERVER_LOG_PATH"]);
|
ManualLogPath = loc["SETUP_SERVER_LOG_PATH"].PromptString();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parserVersions = eventParsers.Select(_parser => _parser.Name).ToArray();
|
|
||||||
selection = Utilities.PromptSelection($"{loc["SETUP_SERVER_EVENT_PARSER_VERSION"]} ({IPAddress}:{Port})", parserVersions[0], null, parserVersions);
|
|
||||||
|
|
||||||
if (selection.Item1 >= 0)
|
|
||||||
{
|
|
||||||
EventParserVersion = eventParsers.FirstOrDefault(_parser => _parser.Name == selection.Item2)?.Version;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBaseConfiguration Generate()
|
public IBaseConfiguration Generate()
|
||||||
{
|
{
|
||||||
|
ModifyParsers();
|
||||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||||
|
var shouldTryFindIp = loc["SETUP_SERVER_IP_AUTO"].PromptBool(defaultValue: true);
|
||||||
|
|
||||||
while (string.IsNullOrEmpty(IPAddress))
|
if (shouldTryFindIp)
|
||||||
{
|
{
|
||||||
var input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
this.TrySetIpAddress();
|
||||||
IPAddress = input;
|
Console.WriteLine(loc["SETUP_SERVER_IP_AUTO_RESULT"].FormatExt(IPAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (string.IsNullOrEmpty(IPAddress))
|
||||||
|
{
|
||||||
|
var input = loc["SETUP_SERVER_IP"].PromptString();
|
||||||
|
IPAddress = input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultPort = _selectedParser.Configuration.DefaultRConPort;
|
||||||
|
Port = loc["SETUP_SERVER_PORT"].PromptInt(null, 1, ushort.MaxValue, defaultValue:defaultPort);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(_selectedParser.Configuration.DefaultInstallationDirectoryHint))
|
||||||
|
{
|
||||||
|
var shouldTryFindPassword = loc["SETUP_RCON_PASSWORD_AUTO"].PromptBool(defaultValue: true);
|
||||||
|
|
||||||
|
if (shouldTryFindPassword)
|
||||||
|
{
|
||||||
|
var passwords = _selectedParser.TryGetRConPasswords();
|
||||||
|
if (passwords.Length > 1)
|
||||||
|
{
|
||||||
|
var (index, value) =
|
||||||
|
loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection(loc["SETUP_RCON_PASSWORD_MANUAL"], null,
|
||||||
|
passwords.Select(pw =>
|
||||||
|
$"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}").ToArray());
|
||||||
|
|
||||||
|
if (index > 0)
|
||||||
|
{
|
||||||
|
Password = passwords[index - 1].Item1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (passwords.Length > 0)
|
||||||
|
{
|
||||||
|
Password = passwords[0].Item1;
|
||||||
|
Console.WriteLine(loc["SETUP_RCON_PASSWORD_RESULT"].FormatExt(Password));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Password))
|
||||||
|
{
|
||||||
|
Password = loc["SETUP_SERVER_RCON"].PromptString();
|
||||||
}
|
}
|
||||||
|
|
||||||
Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue);
|
|
||||||
Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]);
|
|
||||||
AutoMessages = new string[0];
|
AutoMessages = new string[0];
|
||||||
Rules = new string[0];
|
Rules = new string[0];
|
||||||
ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32);
|
|
||||||
ManualLogPath = null;
|
ManualLogPath = null;
|
||||||
|
|
||||||
ModifyParsers();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
SharedLibraryCore/Formatting/ColorCodes.cs
Normal file
26
SharedLibraryCore/Formatting/ColorCodes.cs
Normal file
@ -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<string, string>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
8
SharedLibraryCore/Game/Gametype.cs
Normal file
8
SharedLibraryCore/Game/Gametype.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace SharedLibraryCore
|
||||||
|
{
|
||||||
|
public class Gametype
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Alias { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
SharedLibraryCore/Game/Map.cs
Normal file
10
SharedLibraryCore/Game/Map.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace SharedLibraryCore
|
||||||
|
{
|
||||||
|
public class Map
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Alias { get; set; }
|
||||||
|
|
||||||
|
public override string ToString() => Alias;
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore.Helpers
|
namespace SharedLibraryCore.Helpers
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using System.Threading;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore.Interfaces
|
namespace SharedLibraryCore.Interfaces
|
||||||
{
|
{
|
||||||
@ -21,8 +20,10 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
ILogger GetLogger(long serverId);
|
ILogger GetLogger(long serverId);
|
||||||
IList<Server> GetServers();
|
IList<Server> GetServers();
|
||||||
IList<IManagerCommand> GetCommands();
|
IList<IManagerCommand> GetCommands();
|
||||||
|
IReadOnlyList<IManagerCommand> Commands { get; }
|
||||||
IList<Helpers.MessageToken> GetMessageTokens();
|
IList<Helpers.MessageToken> GetMessageTokens();
|
||||||
IList<EFClient> GetActiveClients();
|
IList<EFClient> GetActiveClients();
|
||||||
|
EFClient FindActiveClient(EFClient client);
|
||||||
IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings();
|
IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings();
|
||||||
ClientService GetClientService();
|
ClientService GetClientService();
|
||||||
PenaltyService GetPenaltyService();
|
PenaltyService GetPenaltyService();
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore.Interfaces
|
namespace SharedLibraryCore.Interfaces
|
||||||
{
|
{
|
||||||
@ -10,9 +9,9 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
Task OnEventAsync(GameEvent E, Server S);
|
Task OnEventAsync(GameEvent E, Server S);
|
||||||
Task OnTickAsync(Server S);
|
Task OnTickAsync(Server S);
|
||||||
|
|
||||||
//for logging purposes
|
string Name { get; }
|
||||||
String Name { get; }
|
|
||||||
float Version { get; }
|
float Version { get; }
|
||||||
String Author { get; }
|
string Author { get; }
|
||||||
|
bool IsParser => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using SharedLibraryCore.RCon;
|
using SharedLibraryCore.RCon;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using SharedLibraryCore.Formatting;
|
||||||
|
using SharedLibraryCore.Localization;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Interfaces
|
namespace SharedLibraryCore.Interfaces
|
||||||
{
|
{
|
||||||
@ -87,5 +89,17 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// specifies the characters used to split a line
|
/// specifies the characters used to split a line
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string NoticeLineSeparator { get; }
|
string NoticeLineSeparator { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default port the game listens to RCon requests on
|
||||||
|
/// </summary>
|
||||||
|
int? DefaultRConPort { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default Indicator of where the game is installed (ex file path or registry entry)
|
||||||
|
/// </summary>
|
||||||
|
string DefaultInstallationDirectoryHint { get; }
|
||||||
|
|
||||||
|
ColorCodeMapping ColorCodeMapping { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
|
||||||
{
|
|
||||||
public class Map
|
|
||||||
{
|
|
||||||
public String Name { get; set; }
|
|
||||||
public String Alias { get; set; }
|
|
||||||
|
|
||||||
public override string ToString() => Alias;
|
|
||||||
}
|
|
||||||
}
|
|
@ -101,7 +101,8 @@ namespace SharedLibraryCore.Database.Models
|
|||||||
.FirstOrDefault(ev => ev.Type == GameEvent.EventType.Command && (ev.Origin?.ClientId == ClientId || ev.ImpersonationOrigin?.ClientId == ClientId))?.CorrelationId ?? Guid.NewGuid()
|
.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);
|
CurrentServer?.Manager.AddEvent(e);
|
||||||
return e;
|
return e;
|
||||||
@ -683,6 +684,17 @@ namespace SharedLibraryCore.Database.Models
|
|||||||
set => SetAdditionalProperty(EFMeta.ClientTag, value);
|
set => SetAdditionalProperty(EFMeta.ClientTag, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public int TemporalClientNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var temporalClientId = GetAdditionalProperty<string>("ConnectionClientId");
|
||||||
|
var parsedClientId = string.IsNullOrEmpty(temporalClientId) ? (int?) null : int.Parse(temporalClientId);
|
||||||
|
return parsedClientId ?? ClientNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
private readonly SemaphoreSlim _processingEvent;
|
private readonly SemaphoreSlim _processingEvent;
|
||||||
|
|
||||||
|
@ -13,11 +13,13 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
{
|
{
|
||||||
public abstract class Server : IGameServer
|
public abstract class Server : IGameServer
|
||||||
{
|
{
|
||||||
|
protected readonly DefaultSettings DefaultSettings;
|
||||||
public enum Game
|
public enum Game
|
||||||
{
|
{
|
||||||
COD = -1,
|
COD = -1,
|
||||||
@ -36,7 +38,7 @@ namespace SharedLibraryCore
|
|||||||
|
|
||||||
public Server(ILogger<Server> logger, SharedLibraryCore.Interfaces.ILogger deprecatedLogger,
|
public Server(ILogger<Server> logger, SharedLibraryCore.Interfaces.ILogger deprecatedLogger,
|
||||||
ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory,
|
ServerConfiguration config, IManager mgr, IRConConnectionFactory rconConnectionFactory,
|
||||||
IGameLogReaderFactory gameLogReaderFactory)
|
IGameLogReaderFactory gameLogReaderFactory, IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
Password = config.Password;
|
Password = config.Password;
|
||||||
IP = config.IPAddress;
|
IP = config.IPAddress;
|
||||||
@ -55,6 +57,7 @@ namespace SharedLibraryCore
|
|||||||
this.gameLogReaderFactory = gameLogReaderFactory;
|
this.gameLogReaderFactory = gameLogReaderFactory;
|
||||||
RConConnectionFactory = rconConnectionFactory;
|
RConConnectionFactory = rconConnectionFactory;
|
||||||
ServerLogger = logger;
|
ServerLogger = logger;
|
||||||
|
DefaultSettings = serviceProvider.GetRequiredService<DefaultSettings>();
|
||||||
InitializeTokens();
|
InitializeTokens();
|
||||||
InitializeAutoMessages();
|
InitializeAutoMessages();
|
||||||
}
|
}
|
||||||
@ -132,10 +135,12 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="message">Message to be sent to all players</param>
|
/// <param name="message">Message to be sent to all players</param>
|
||||||
public GameEvent Broadcast(string message, EFClient sender = null)
|
public GameEvent Broadcast(string message, EFClient sender = null)
|
||||||
{
|
{
|
||||||
string formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "", $"{(CustomSayEnabled && GameName == Game.IW4 ? $"{CustomSayName}: " : "")}{message.FixIW4ForwardSlash()}");
|
var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Say ?? "",
|
||||||
ServerLogger.LogDebug("All->" + message.StripColors());
|
$"{(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,
|
Type = GameEvent.EventType.Broadcast,
|
||||||
Data = formattedMessage,
|
Data = formattedMessage,
|
||||||
@ -165,6 +170,8 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="targetClient">EFClient to send message to</param>
|
/// <param name="targetClient">EFClient to send message to</param>
|
||||||
protected async Task Tell(string message, EFClient targetClient)
|
protected async Task Tell(string message, EFClient targetClient)
|
||||||
{
|
{
|
||||||
|
var engineMessage = message.FormatMessageForEngine(RconParser.Configuration.ColorCodeMapping);
|
||||||
|
|
||||||
if (!Utilities.IsDevelopment)
|
if (!Utilities.IsDevelopment)
|
||||||
{
|
{
|
||||||
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
var temporalClientId = targetClient.GetAdditionalProperty<string>("ConnectionClientId");
|
||||||
@ -173,24 +180,25 @@ namespace SharedLibraryCore
|
|||||||
|
|
||||||
var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell,
|
var formattedMessage = string.Format(RconParser.Configuration.CommandPrefixes.Tell,
|
||||||
clientNumber,
|
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)
|
if (targetClient.ClientNumber > -1 && message.Length > 0 && targetClient.Level != EFClient.Permission.Console)
|
||||||
await this.ExecuteCommandAsync(formattedMessage);
|
await this.ExecuteCommandAsync(formattedMessage);
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (targetClient.Level == EFClient.Permission.Console)
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
using (LogContext.PushProperty("Server", ToString()))
|
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;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,9 +226,9 @@ namespace SharedLibraryCore
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Temporarily ban a player ( default 1 hour ) from the server
|
/// Temporarily ban a player ( default 1 hour ) from the server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Reason">Reason for banning the player</param>
|
/// <param name="reason">Reason for banning the player</param>
|
||||||
/// <param name="Target">The player to ban</param>
|
/// <param name="Target">The player to ban</param>
|
||||||
abstract public Task TempBan(String Reason, TimeSpan length, EFClient Target, EFClient Origin);
|
abstract public Task TempBan(String reason, TimeSpan length, EFClient Target, EFClient Origin);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Perm ban a player from the server
|
/// Perm ban a player from the server
|
||||||
@ -236,9 +244,9 @@ namespace SharedLibraryCore
|
|||||||
/// Unban a player by npID / GUID
|
/// Unban a player by npID / GUID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="npID">npID of the player</param>
|
/// <param name="npID">npID of the player</param>
|
||||||
/// <param name="Target">I don't remember what this is for</param>
|
/// <param name="targetClient">I don't remember what this is for</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
abstract public Task Unban(string reason, EFClient Target, EFClient Origin);
|
abstract public Task Unban(string reason, EFClient targetClient, EFClient originClient);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Change the current searver map
|
/// Change the current searver map
|
||||||
@ -302,6 +310,8 @@ namespace SharedLibraryCore
|
|||||||
public string Hostname { get => ServerConfig.CustomHostname ?? hostname; protected set => hostname = value; }
|
public string Hostname { get => ServerConfig.CustomHostname ?? hostname; protected set => hostname = value; }
|
||||||
public string Website { get; protected set; }
|
public string Website { get; protected set; }
|
||||||
public string Gametype { get; set; }
|
public string Gametype { get; set; }
|
||||||
|
public string GametypeName => DefaultSettings.Gametypes.FirstOrDefault(gt => gt.Game == GameName)?.Gametypes
|
||||||
|
?.FirstOrDefault(gt => gt.Name == Gametype)?.Alias ?? Gametype;
|
||||||
public string GamePassword { get; protected set; }
|
public string GamePassword { get; protected set; }
|
||||||
public Map CurrentMap { get; set; }
|
public Map CurrentMap { get; set; }
|
||||||
public int ClientNum
|
public int ClientNum
|
||||||
|
@ -153,7 +153,7 @@ namespace SharedLibraryCore.Services
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("[{Method}] creating new Link and Alias for {Entity}", nameof(HandleNewCreate), entity.ToString());
|
_logger.LogDebug("[{Method}] creating new Link and Alias for {Entity}", nameof(HandleNewCreate), entity.ToString());
|
||||||
var link = new EFAliasLink();
|
var link = new EFAliasLink();
|
||||||
var alias = new EFAlias()
|
var alias = new EFAlias
|
||||||
{
|
{
|
||||||
Name = entity.Name,
|
Name = entity.Name,
|
||||||
SearchableName = entity.Name.StripColors().ToLower(),
|
SearchableName = entity.Name.StripColors().ToLower(),
|
||||||
@ -167,9 +167,18 @@ namespace SharedLibraryCore.Services
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogDebug("[{Method}] associating new GUID {Guid} with existing alias id {aliasId} for {Entity}",
|
_logger.LogDebug("[{Method}] associating new GUID {Guid} with new exact alias match with linkId {LinkId} for {Entity}",
|
||||||
nameof(HandleNewCreate), entity.GuidString, existingAlias.AliasId, entity.ToString());
|
nameof(HandleNewCreate), entity.GuidString, existingAlias.LinkId, entity.ToString());
|
||||||
client.CurrentAliasId = existingAlias.AliasId;
|
|
||||||
|
var alias = new EFAlias
|
||||||
|
{
|
||||||
|
Name = existingAlias.Name,
|
||||||
|
SearchableName = entity.Name.StripColors().ToLower(),
|
||||||
|
DateAdded = DateTime.UtcNow,
|
||||||
|
IPAddress = entity.IPAddress,
|
||||||
|
LinkId = existingAlias.LinkId
|
||||||
|
};
|
||||||
|
client.CurrentAlias = alias;
|
||||||
client.AliasLinkId = existingAlias.LinkId;
|
client.AliasLinkId = existingAlias.LinkId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +347,7 @@ namespace SharedLibraryCore.Services
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (existingExactAlias != null)
|
if (existingExactAlias != null && entity.AliasLinkId == existingExactAlias.LinkId)
|
||||||
{
|
{
|
||||||
entity.CurrentAlias = existingExactAlias;
|
entity.CurrentAlias = existingExactAlias;
|
||||||
entity.CurrentAliasId = existingExactAlias.AliasId;
|
entity.CurrentAliasId = existingExactAlias.AliasId;
|
||||||
@ -716,12 +725,15 @@ namespace SharedLibraryCore.Services
|
|||||||
|
|
||||||
// we want to project our results
|
// we want to project our results
|
||||||
var iqClientProjection = iqClients.OrderByDescending(_client => _client.LastConnection)
|
var iqClientProjection = iqClients.OrderByDescending(_client => _client.LastConnection)
|
||||||
.Select(_client => new PlayerInfo()
|
.Select(_client => new PlayerInfo
|
||||||
{
|
{
|
||||||
Name = _client.CurrentAlias.Name,
|
Name = _client.CurrentAlias.Name,
|
||||||
LevelInt = (int)_client.Level,
|
LevelInt = (int) _client.Level,
|
||||||
LastConnection = _client.LastConnection,
|
LastConnection = _client.LastConnection,
|
||||||
ClientId = _client.ClientId,
|
ClientId = _client.ClientId,
|
||||||
|
IPAddress = _client.CurrentAlias.IPAddress.HasValue
|
||||||
|
? _client.CurrentAlias.IPAddress.Value.ToString()
|
||||||
|
: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
var clients = await iqClientProjection.ToListAsync();
|
var clients = await iqClientProjection.ToListAsync();
|
||||||
|
@ -162,15 +162,29 @@ namespace SharedLibraryCore.Services
|
|||||||
.Where(p => p.LinkId == linkId)
|
.Where(p => p.LinkId == linkId)
|
||||||
.Where(filter);
|
.Where(filter);
|
||||||
|
|
||||||
var iqIpPenalties = _appConfig.EnableImplicitAccountLinking
|
IQueryable<EFPenalty> iqIpPenalties;
|
||||||
? context.Aliases
|
|
||||||
|
if (_appConfig.EnableImplicitAccountLinking)
|
||||||
|
{
|
||||||
|
iqIpPenalties = context.Aliases
|
||||||
.Where(a => a.IPAddress != null && a.IPAddress == ip)
|
.Where(a => a.IPAddress != null && a.IPAddress == ip)
|
||||||
.SelectMany(a => a.Link.ReceivedPenalties)
|
.SelectMany(a => a.Link.ReceivedPenalties)
|
||||||
.Where(filter)
|
|
||||||
: context.Penalties.Where(penalty =>
|
|
||||||
penalty.Offender.CurrentAlias.IPAddress != null &&
|
|
||||||
penalty.Offender.CurrentAlias.IPAddress == ip)
|
|
||||||
.Where(filter);
|
.Where(filter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var aliasIps = await context.Aliases.Where(alias => alias.LinkId == linkId && alias.IPAddress != null)
|
||||||
|
.Select(alias => alias.IPAddress)
|
||||||
|
.ToListAsync();
|
||||||
|
if (ip != null)
|
||||||
|
{
|
||||||
|
aliasIps.Add(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
iqIpPenalties = context.Penalties
|
||||||
|
.Where(penalty => aliasIps.Contains(penalty.Offender.CurrentAlias.IPAddress))
|
||||||
|
.Where(filter);
|
||||||
|
}
|
||||||
|
|
||||||
var activePenalties = (await iqLinkPenalties.ToListAsync())
|
var activePenalties = (await iqLinkPenalties.ToListAsync())
|
||||||
.Union(await iqIpPenalties.ToListAsync())
|
.Union(await iqIpPenalties.ToListAsync())
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||||
<Version>2021.8.31.1</Version>
|
<Version>2022.01.20.1</Version>
|
||||||
<Authors>RaidMax</Authors>
|
<Authors>RaidMax</Authors>
|
||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
@ -13,13 +13,13 @@
|
|||||||
<PackageTags>IW4MAdmin</PackageTags>
|
<PackageTags>IW4MAdmin</PackageTags>
|
||||||
<RepositoryUrl>https://github.com/RaidMax/IW4M-Admin/</RepositoryUrl>
|
<RepositoryUrl>https://github.com/RaidMax/IW4M-Admin/</RepositoryUrl>
|
||||||
<PackageProjectUrl>https://www.raidmax.org/IW4MAdmin/</PackageProjectUrl>
|
<PackageProjectUrl>https://www.raidmax.org/IW4MAdmin/</PackageProjectUrl>
|
||||||
<Copyright>2021</Copyright>
|
<Copyright>2022</Copyright>
|
||||||
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<Description>Shared Library for IW4MAdmin</Description>
|
<Description>Shared Library for IW4MAdmin</Description>
|
||||||
<PackageVersion>2021.8.31.1</PackageVersion>
|
<PackageVersion>2022.01.20.1</PackageVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">
|
||||||
@ -28,29 +28,41 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="9.3.0" />
|
<PackageReference Include="FluentValidation" Version="9.3.0"/>
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core" Version="2.8.26"/>
|
||||||
<PackageReference Include="Humanizer.Core.ru" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core.ru" Version="2.8.26"/>
|
||||||
<PackageReference Include="Humanizer.Core.de" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core.de" Version="2.8.26"/>
|
||||||
<PackageReference Include="Humanizer.Core.es" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core.es" Version="2.8.26"/>
|
||||||
<PackageReference Include="Humanizer.Core.pt" Version="2.8.26" />
|
<PackageReference Include="Humanizer.Core.pt" Version="2.8.26"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.10"/>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.1.10"/>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3"/>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.Data" Version="1.0.7" />
|
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0"/>
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
|
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0"/>
|
||||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Data\Data.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
<Exec Command="if not exist "$(ProjectDir)..\BUILD" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD"
)
)
if not exist "$(ProjectDir)..\BUILD\Plugins" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD\Plugins"
)
)" />
|
<Exec Command="if not exist "$(ProjectDir)..\BUILD" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD"
)
)
if not exist "$(ProjectDir)..\BUILD\Plugins" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD\Plugins"
)
)"/>
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Target DependsOnTargets="BuildOnlySettings;ResolveReferences" Name="CopyProjectReferencesToPackage">
|
||||||
|
<ItemGroup>
|
||||||
|
<BuildOutputInPackage Include="@(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))"/>
|
||||||
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,28 +1,35 @@
|
|||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using System.Linq;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using SharedLibraryCore.Configuration;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
{
|
{
|
||||||
[HtmlTargetElement("color-code")]
|
[HtmlTargetElement("color-code")]
|
||||||
public class ColorCode : TagHelper
|
public class ColorCode : TagHelper
|
||||||
{
|
{
|
||||||
|
public ColorCode(ApplicationConfiguration appConfig)
|
||||||
|
{
|
||||||
|
_allow = appConfig?.EnableColorCodes ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
public bool Allow { get; set; } = false;
|
private readonly bool _allow;
|
||||||
|
|
||||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
{
|
{
|
||||||
output.TagName = "ColorCode";
|
output.TagName = "ColorCode";
|
||||||
output.TagMode = TagMode.StartTagAndEndTag;
|
output.TagMode = TagMode.StartTagAndEndTag;
|
||||||
|
|
||||||
if (Allow)
|
if (_allow)
|
||||||
{
|
{
|
||||||
var matches = Regex.Matches(Value, @"\^([0-9]|\:)([^\^]*)");
|
var matches = Regex.Matches(Value, @"\^([0-9]|\:)([^\^]*)");
|
||||||
foreach (Match match in matches)
|
foreach (Match match in matches)
|
||||||
{
|
{
|
||||||
char colorCode = match.Groups[1].ToString().Last();
|
var colorCode = match.Groups[1].ToString().Last();
|
||||||
output.PreContent.AppendHtml($"<span class='text-color-code-{(colorCode >= 48 && colorCode <= 57 ? colorCode.ToString() : ((int)colorCode).ToString())}'>");
|
output.PreContent.AppendHtml(
|
||||||
|
$"<span class='text-color-code-{(colorCode >= 48 && colorCode <= 57 ? colorCode.ToString() : ((int) colorCode).ToString())}'>");
|
||||||
output.PreContent.Append(match.Groups[2].ToString());
|
output.PreContent.Append(match.Groups[2].ToString());
|
||||||
output.PreContent.AppendHtml("</span>");
|
output.PreContent.AppendHtml("</span>");
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ using static SharedLibraryCore.Server;
|
|||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using static Data.Models.Client.EFClient;
|
using static Data.Models.Client.EFClient;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
|
using SharedLibraryCore.Formatting;
|
||||||
using static Data.Models.EFPenalty;
|
using static Data.Models.EFPenalty;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
@ -178,6 +179,24 @@ namespace SharedLibraryCore
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string FixIW4ForwardSlash(this string str) => str.Replace("//", "/ /");
|
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<string> _zmGameTypes = new[] { "zclassic", "zstandard", "zcleansed", "zgrief" };
|
private static readonly IList<string> _zmGameTypes = new[] { "zclassic", "zstandard", "zcleansed", "zgrief" };
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates if the given server is running a zombie game mode
|
/// 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";
|
public static bool IsCodGame(this Server server) => server.RconParser?.RConEngine == "COD";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the IW Engine color code corresponding to an admin level
|
/// Get the color key corresponding to a given user level
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="level">Specified player level</param>
|
/// <param name="level">Specified player level</param>
|
||||||
|
/// <param name="localizedLevel"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static String ConvertLevelToColor(EFClient.Permission level, string localizedLevel)
|
public static string ConvertLevelToColor(Permission level, string localizedLevel)
|
||||||
{
|
{
|
||||||
char colorCode = '6';
|
// todo: make configurable
|
||||||
// todo: maybe make this game independant?
|
var colorCode = level switch
|
||||||
switch (level)
|
|
||||||
{
|
{
|
||||||
case EFClient.Permission.Banned:
|
Permission.Banned => "Red",
|
||||||
colorCode = '1';
|
Permission.Flagged => "Map",
|
||||||
break;
|
Permission.Owner => "Accent",
|
||||||
case EFClient.Permission.Flagged:
|
Permission.User => "Yellow",
|
||||||
colorCode = '9';
|
Permission.Trusted => "Green",
|
||||||
break;
|
_ => "Pink"
|
||||||
case EFClient.Permission.Owner:
|
};
|
||||||
colorCode = '5';
|
|
||||||
break;
|
|
||||||
case EFClient.Permission.User:
|
|
||||||
colorCode = '2';
|
|
||||||
break;
|
|
||||||
case EFClient.Permission.Trusted:
|
|
||||||
colorCode = '3';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $"^{colorCode}{localizedLevel ?? level.ToString()}";
|
return $"(Color::{colorCode}){localizedLevel ?? level.ToString()}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ToLocalizedLevelName(this Permission permission)
|
public static string ToLocalizedLevelName(this Permission permission)
|
||||||
@ -546,7 +554,7 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="description">description of the question's value</param>
|
/// <param name="description">description of the question's value</param>
|
||||||
/// <param name="defaultValue">default value to set if no input is entered</param>
|
/// <param name="defaultValue">default value to set if no input is entered</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool PromptBool(string question, string description = null, bool defaultValue = true)
|
public static bool PromptBool(this string question, string description = null, bool defaultValue = true)
|
||||||
{
|
{
|
||||||
Console.Write($"{question}?{(string.IsNullOrEmpty(description) ? " " : $" ({description}) ")}[y/n]: ");
|
Console.Write($"{question}?{(string.IsNullOrEmpty(description) ? " " : $" ({description}) ")}[y/n]: ");
|
||||||
char response = Console.ReadLine().ToLower().FirstOrDefault();
|
char response = Console.ReadLine().ToLower().FirstOrDefault();
|
||||||
@ -562,7 +570,7 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="description">description of the question's value</param>
|
/// <param name="description">description of the question's value</param>
|
||||||
/// <param name="selections">array of possible selections (should be able to convert to string)</param>
|
/// <param name="selections">array of possible selections (should be able to convert to string)</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static Tuple<int, T> PromptSelection<T>(string question, T defaultValue, string description = null, params T[] selections)
|
public static Tuple<int, T> PromptSelection<T>(this string question, T defaultValue, string description = null, params T[] selections)
|
||||||
{
|
{
|
||||||
bool hasDefault = false;
|
bool hasDefault = false;
|
||||||
|
|
||||||
@ -634,7 +642,7 @@ namespace SharedLibraryCore
|
|||||||
/// <param name="description">description of the question's value</param>
|
/// <param name="description">description of the question's value</param>
|
||||||
/// <param name="defaultValue">default value to set the return value to</param>
|
/// <param name="defaultValue">default value to set the return value to</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string PromptString(string question, string description = null, string defaultValue = null)
|
public static string PromptString(this string question, string description = null, string defaultValue = null)
|
||||||
{
|
{
|
||||||
string inputOrDefault()
|
string inputOrDefault()
|
||||||
{
|
{
|
||||||
|
@ -46,7 +46,8 @@ namespace WebfrontCore.Controllers
|
|||||||
case nameof(UnbanCommand):
|
case nameof(UnbanCommand):
|
||||||
_unbanCommandName = cmd.Name;
|
_unbanCommandName = cmd.Name;
|
||||||
break;
|
break;
|
||||||
case nameof(SayCommand):
|
// todo: this should be flag driven
|
||||||
|
case "SayCommand":
|
||||||
_sayCommandName = cmd.Name;
|
_sayCommandName = cmd.Name;
|
||||||
break;
|
break;
|
||||||
case nameof(KickCommand):
|
case nameof(KickCommand):
|
||||||
|
@ -11,6 +11,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using IW4MAdmin.Plugins.Stats.Config;
|
using IW4MAdmin.Plugins.Stats.Config;
|
||||||
|
using Stats.Config;
|
||||||
using WebfrontCore.ViewComponents;
|
using WebfrontCore.ViewComponents;
|
||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
@ -113,7 +114,7 @@ namespace WebfrontCore.Controllers
|
|||||||
ViewBag.Title += " " + Localization["WEBFRONT_CLIENT_PROFILE_TITLE"];
|
ViewBag.Title += " " + Localization["WEBFRONT_CLIENT_PROFILE_TITLE"];
|
||||||
ViewBag.Description = $"Client information for {strippedName}";
|
ViewBag.Description = $"Client information for {strippedName}";
|
||||||
ViewBag.Keywords = $"IW4MAdmin, client, profile, {strippedName}";
|
ViewBag.Keywords = $"IW4MAdmin, client, profile, {strippedName}";
|
||||||
ViewBag.UseNewStats = _configurationHandler.Configuration().EnableAdvancedMetrics;
|
ViewBag.UseNewStats = _configurationHandler.Configuration()?.EnableAdvancedMetrics ?? true;
|
||||||
|
|
||||||
return View("Profile/Index", clientDto);
|
return View("Profile/Index", clientDto);
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using Data.Abstractions;
|
using Data.Abstractions;
|
||||||
using IW4MAdmin.Plugins.Stats.Config;
|
using IW4MAdmin.Plugins.Stats.Config;
|
||||||
|
using Stats.Config;
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
namespace IW4MAdmin.Plugins.Web.StatsWeb.Controllers
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user