Compare commits
45 Commits
2.3-prerel
...
2.3-prerel
Author | SHA1 | Date | |
---|---|---|---|
02622ea7de | |||
03ae3b5822 | |||
0711249a46 | |||
18f4ffa9ff | |||
934fead5c2 | |||
02cad10d77 | |||
b134cd4728 | |||
b9c4a1b5f6 | |||
f0fd4c66e9 | |||
52fe8fc847 | |||
9f8c35dbed | |||
9d9be7f8af | |||
dd82a5e3fa | |||
8667532d24 | |||
863ba8b096 | |||
8ab89e113d | |||
00634780d4 | |||
6f80f1edbb | |||
9393b35c39 | |||
37d3f4f90d | |||
8521df85f5 | |||
7b8126d57e | |||
1e2e2218e3 | |||
11e3235d5d | |||
cae6d8389e | |||
1056c7c335 | |||
95e4ee672e | |||
bf0a0befc6 | |||
53c3ff6ce3 | |||
c8ec0eefa9 | |||
82bae772f0 | |||
af3aea77bc | |||
b3e5f468a1 | |||
c21bf2ebf1 | |||
d318a57830 | |||
61f1436faf | |||
0c527a5f65 | |||
1457b843e2 | |||
de69bed792 | |||
4b1f44cc2a | |||
74cdf8e885 | |||
5d41059641 | |||
2e6889d9bb | |||
9c4d23f0b4 | |||
b4e3e8526a |
5
.gitignore
vendored
5
.gitignore
vendored
@ -223,7 +223,7 @@ global.min.js
|
||||
bootstrap-custom.min.css
|
||||
**/Master/static
|
||||
**/Master/dev_env
|
||||
/WebfrontCore/Views/Plugins/Stats
|
||||
/WebfrontCore/Views/Plugins/*
|
||||
/WebfrontCore/wwwroot/**/dds
|
||||
|
||||
/DiscordWebhook/env
|
||||
@ -232,4 +232,5 @@ bootstrap-custom.min.css
|
||||
launchSettings.json
|
||||
/VpnDetectionPrivate.js
|
||||
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
|
||||
**/Master/env_master
|
||||
**/Master/env_master
|
||||
/GameLogServer/log_env
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using RestEase;
|
||||
using SharedLibraryCore;
|
||||
@ -11,6 +12,7 @@ namespace IW4MAdmin.Application.API.Master
|
||||
public class HeartbeatState
|
||||
{
|
||||
public bool Connected { get; set; }
|
||||
public CancellationToken Token { get; set; }
|
||||
}
|
||||
|
||||
public class Heartbeat
|
||||
|
@ -6,7 +6,7 @@
|
||||
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
|
||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||
<Version>2.2.5.2</Version>
|
||||
<Version>2.2.6.5</Version>
|
||||
<Authors>RaidMax</Authors>
|
||||
<Company>Forever None</Company>
|
||||
<Product>IW4MAdmin</Product>
|
||||
@ -31,8 +31,8 @@
|
||||
<PropertyGroup>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<TieredCompilation>true</TieredCompilation>
|
||||
<AssemblyVersion>2.2.5.2</AssemblyVersion>
|
||||
<FileVersion>2.2.5.2</FileVersion>
|
||||
<AssemblyVersion>2.2.6.5</AssemblyVersion>
|
||||
<FileVersion>2.2.6.5</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -49,18 +49,6 @@
|
||||
<None Update="DefaultSettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\IW4MAdmin.en-US.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\IW4MAdmin.es-EC.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\IW4MAdmin.pt-BR.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Localization\IW4MAdmin.ru-RU.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -7,10 +7,12 @@ using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -39,10 +41,9 @@ namespace IW4MAdmin.Application
|
||||
|
||||
public IList<IRConParser> AdditionalRConParsers { get; }
|
||||
public IList<IEventParser> AdditionalEventParsers { get; }
|
||||
|
||||
public ITokenAuthentication TokenAuthenticator => Authenticator;
|
||||
|
||||
public ITokenAuthentication Authenticator => _authenticator;
|
||||
public string ExternalIPAddress { get; private set; }
|
||||
|
||||
static ApplicationManager Instance;
|
||||
readonly List<AsyncStatus> TaskStatuses;
|
||||
@ -58,6 +59,8 @@ namespace IW4MAdmin.Application
|
||||
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
|
||||
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
|
||||
readonly ITokenAuthentication _authenticator;
|
||||
private readonly MetaService _metaService;
|
||||
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
|
||||
|
||||
private ApplicationManager()
|
||||
{
|
||||
@ -77,6 +80,7 @@ namespace IW4MAdmin.Application
|
||||
OnServerEvent += OnGameEvent;
|
||||
OnServerEvent += EventApi.OnGameEvent;
|
||||
_authenticator = new TokenAuthentication();
|
||||
_metaService = new MetaService();
|
||||
}
|
||||
|
||||
private async void OnGameEvent(object sender, GameEventArgs args)
|
||||
@ -129,7 +133,7 @@ namespace IW4MAdmin.Application
|
||||
catch (Exception ex)
|
||||
{
|
||||
newEvent.FailReason = GameEvent.EventFailReason.Exception;
|
||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}");
|
||||
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EXCEPTION"].FormatExt(newEvent.Owner));
|
||||
Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
@ -154,7 +158,7 @@ namespace IW4MAdmin.Application
|
||||
return Instance ?? (Instance = new ApplicationManager());
|
||||
}
|
||||
|
||||
public async Task UpdateServerStates()
|
||||
public async Task UpdateServerStates(CancellationToken token)
|
||||
{
|
||||
// store the server hash code and task for it
|
||||
var runningUpdateTasks = new Dictionary<long, Task>();
|
||||
@ -190,7 +194,11 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
try
|
||||
{
|
||||
await server.ProcessUpdatesAsync(new CancellationToken());
|
||||
await server.ProcessUpdatesAsync(token);
|
||||
if (server.Throttled)
|
||||
{
|
||||
await Task.Delay((int)_throttleTimeout.TotalMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -198,6 +206,11 @@ namespace IW4MAdmin.Application
|
||||
Logger.WriteWarning($"Failed to update status for {server}");
|
||||
Logger.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
server.IsInitialized = true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
#if DEBUG
|
||||
@ -206,17 +219,19 @@ namespace IW4MAdmin.Application
|
||||
ThreadPool.GetAvailableThreads(out int availableThreads, out int m);
|
||||
Logger.WriteDebug($"There are {workerThreads - availableThreads} active threading tasks");
|
||||
#endif
|
||||
await Task.Delay(ConfigHandler.Configuration().RConPollRate);
|
||||
try
|
||||
{
|
||||
await Task.Delay(ConfigHandler.Configuration().RConPollRate, token);
|
||||
}
|
||||
// if a cancellation is received, we want to return immediately
|
||||
catch { break; }
|
||||
}
|
||||
|
||||
// trigger the event processing loop to end
|
||||
SetHasEvent();
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
{
|
||||
Running = true;
|
||||
|
||||
ExternalIPAddress = await Utilities.GetExternalIP();
|
||||
|
||||
#region PLUGINS
|
||||
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
||||
@ -246,10 +261,11 @@ namespace IW4MAdmin.Application
|
||||
ConfigHandler.Set((ApplicationConfiguration)new ApplicationConfiguration().Generate());
|
||||
var newConfig = ConfigHandler.Configuration();
|
||||
|
||||
newConfig.AutoMessagePeriod = defaultConfig.AutoMessagePeriod;
|
||||
newConfig.AutoMessages = defaultConfig.AutoMessages;
|
||||
newConfig.GlobalRules = defaultConfig.GlobalRules;
|
||||
newConfig.Maps = defaultConfig.Maps;
|
||||
newConfig.DisallowedClientNames = defaultConfig.DisallowedClientNames;
|
||||
newConfig.QuickMessages = defaultConfig.QuickMessages;
|
||||
|
||||
if (newConfig.Servers == null)
|
||||
{
|
||||
@ -277,7 +293,7 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
}
|
||||
|
||||
else if (config != null)
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(config.Id))
|
||||
{
|
||||
@ -313,7 +329,7 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
}
|
||||
|
||||
else if (config.Servers.Count == 0)
|
||||
if (config.Servers.Count == 0)
|
||||
{
|
||||
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
|
||||
}
|
||||
@ -384,11 +400,139 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region META
|
||||
async Task<List<ProfileMeta>> getProfileMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
var metaList = new List<ProfileMeta>();
|
||||
|
||||
// we don't want to return anything because it means we're trying to retrieve paged meta data
|
||||
if (count > 1)
|
||||
{
|
||||
return metaList;
|
||||
}
|
||||
|
||||
var lastMapMeta = await _metaService.GetPersistentMeta("LastMapPlayed", new EFClient() { ClientId = clientId });
|
||||
|
||||
if (lastMapMeta != null)
|
||||
{
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = lastMapMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_MAP"],
|
||||
Value = lastMapMeta.Value,
|
||||
Show = true,
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
});
|
||||
}
|
||||
|
||||
var lastServerMeta = await _metaService.GetPersistentMeta("LastServerPlayed", new EFClient() { ClientId = clientId });
|
||||
|
||||
if (lastServerMeta != null)
|
||||
{
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = lastServerMeta.MetaId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_LAST_SERVER"],
|
||||
Value = lastServerMeta.Value,
|
||||
Show = true,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
}
|
||||
|
||||
var client = await GetClientService().Get(clientId);
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_TIME_HOURS"]} {Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"]}",
|
||||
Value = Math.Round(client.TotalConnectionTime / 3600.0, 1).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 0,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_FSEEN"],
|
||||
Value = Utilities.GetTimePassed(client.FirstConnection, false),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 1,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_LSEEN"],
|
||||
Value = Utilities.GetTimePassed(client.LastConnection, false),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 2,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Id = client.ClientId,
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_CONNECTIONS"],
|
||||
Value = client.Connections.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Show = true,
|
||||
Column = 1,
|
||||
Order = 3,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
metaList.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_TRUE"] : Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Sensitive = true,
|
||||
Column = 1,
|
||||
Order = 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
});
|
||||
|
||||
return metaList;
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getPenaltyMeta(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
if (count <= 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
var penalties = await GetPenaltyService().GetClientPenaltyForMetaAsync(clientId, count, offset, startAt);
|
||||
|
||||
return penalties.Select(_penalty => new ProfileMeta()
|
||||
{
|
||||
Id = _penalty.Id,
|
||||
Type = _penalty.PunisherId == clientId ? ProfileMeta.MetaType.Penalized : ProfileMeta.MetaType.ReceivedPenalty,
|
||||
Value = _penalty,
|
||||
When = _penalty.TimePunished,
|
||||
Sensitive = _penalty.Sensitive
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
MetaService.AddRuntimeMeta(getProfileMeta);
|
||||
MetaService.AddRuntimeMeta(getPenaltyMeta);
|
||||
#endregion
|
||||
|
||||
#region INIT
|
||||
int failedServers = 0;
|
||||
int successServers = 0;
|
||||
Exception lastException = null;
|
||||
|
||||
async Task Init(ServerConfiguration Conf)
|
||||
{
|
||||
// setup the event handler after the class is initialized
|
||||
|
||||
Handler = new GameEventHandler(this);
|
||||
|
||||
try
|
||||
{
|
||||
var ServerInstance = new IW4MServer(this, Conf);
|
||||
@ -399,7 +543,7 @@ namespace IW4MAdmin.Application
|
||||
_servers.Add(ServerInstance);
|
||||
}
|
||||
|
||||
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
||||
Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname));
|
||||
// add the start event for this server
|
||||
|
||||
var e = new GameEvent()
|
||||
@ -410,26 +554,37 @@ namespace IW4MAdmin.Application
|
||||
};
|
||||
|
||||
Handler.AddEvent(e);
|
||||
successServers++;
|
||||
}
|
||||
|
||||
catch (ServerException e)
|
||||
{
|
||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"]} [{Conf.IPAddress}:{Conf.Port}]");
|
||||
Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_UNFIXABLE"].FormatExt($"[{Conf.IPAddress}:{Conf.Port}]"));
|
||||
|
||||
if (e.GetType() == typeof(DvarException))
|
||||
{
|
||||
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"]} {(e as DvarException).Data["dvar_name"]} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
|
||||
Logger.WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR"].FormatExt((e as DvarException).Data["dvar_name"])} ({Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_DVAR_HELP"]})");
|
||||
}
|
||||
|
||||
else if (e.GetType() == typeof(NetworkException))
|
||||
{
|
||||
Logger.WriteDebug(e.Message);
|
||||
}
|
||||
|
||||
// throw the exception to the main method to stop before instantly exiting
|
||||
throw e;
|
||||
failedServers++;
|
||||
lastException = e;
|
||||
}
|
||||
}
|
||||
|
||||
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
||||
|
||||
if (successServers - failedServers <= 0)
|
||||
{
|
||||
if (!Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_START_WITH_ERRORS"]))
|
||||
{
|
||||
throw lastException;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -494,27 +649,34 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
}
|
||||
await Task.Delay(30000);
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(30000, heartbeatState.Token);
|
||||
}
|
||||
catch { break; }
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var tokenSource = new CancellationTokenSource();
|
||||
// this needs to be run seperately from the main thread
|
||||
var _ = Task.Run(() => SendHeartbeat(new HeartbeatState()));
|
||||
_ = Task.Run(() => UpdateServerStates());
|
||||
_ = Task.Run(() => SendHeartbeat(new HeartbeatState() { Token = tokenSource.Token }));
|
||||
_ = Task.Run(() => UpdateServerStates(tokenSource.Token));
|
||||
|
||||
while (Running)
|
||||
{
|
||||
OnQuit.Wait();
|
||||
tokenSource.Cancel();
|
||||
OnQuit.Reset();
|
||||
}
|
||||
_servers.Clear();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
Running = false;
|
||||
OnQuit.Set();
|
||||
}
|
||||
|
||||
public ILogger GetLogger(long serverId)
|
||||
@ -589,7 +751,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
public void SetHasEvent()
|
||||
{
|
||||
OnQuit.Set();
|
||||
|
||||
}
|
||||
|
||||
public IList<Assembly> GetPluginAssemblies()
|
||||
|
@ -97,11 +97,19 @@ if "%CurrentConfiguration%" == "Release" (
|
||||
if exist "%SolutionDir%Publish\Windows\refs" move "%SolutionDir%Publish\Windows\refs" "%SolutionDir%Publish\Windows\Lib\refs"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Prerelease" (
|
||||
echo PR-LOC
|
||||
if not exist "%SolutionDir%Publish\WindowsPrerelease\Localization" md "%SolutionDir%Publish\WindowsPrerelease\Localization"
|
||||
)
|
||||
|
||||
if "%CurrentConfiguration%" == "Release" (
|
||||
echo R-LOC
|
||||
if not exist "%SolutionDir%Publish\Windows\Localization" md "%SolutionDir%Publish\Windows\Localization"
|
||||
)
|
||||
|
||||
echo making start scripts
|
||||
@(echo dotnet Lib/IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
||||
@(echo dotnet Lib/IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.cmd"
|
||||
@(echo @echo off && echo @title IW4MAdmin && echo dotnet Lib\IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.cmd"
|
||||
@(echo @echo off && echo @title IW4MAdmin && echo dotnet Lib\IW4MAdmin.dll && echo pause) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.cmd"
|
||||
|
||||
@(echo #!/bin/bash && echo dotnet Lib\IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
|
||||
@(echo #!/bin/bash && echo dotnet Lib\IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"
|
||||
|
||||
eCHO "%CurrentConfiguration%"
|
||||
@(echo #!/bin/bash && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
|
||||
@(echo #!/bin/bash && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"
|
||||
|
@ -16,7 +16,231 @@
|
||||
"Keep grenade launcher use to a minimum",
|
||||
"Balance teams at ALL times"
|
||||
],
|
||||
"DisallowedClientNames": ["Unknown Soldier", "VickNet", "UnknownSoldier", "CHEATER"],
|
||||
"QuickMessages": [
|
||||
{
|
||||
"Game": "IW4",
|
||||
"Messages": {
|
||||
"QUICKMESSAGE_AREA_SECURE": "Area secure!",
|
||||
"QUICKMESSAGE_ARE_YOU_CRAZY": "Are you crazy?",
|
||||
"QUICKMESSAGE_ATTACK_LEFT_FLANK": "Attack left flank!",
|
||||
"QUICKMESSAGE_ATTACK_RIGHT_FLANK": "Attack right flank!",
|
||||
"QUICKMESSAGE_COME_ON": "Come on.",
|
||||
"QUICKMESSAGE_ENEMIES_SPOTTED": "Multiple contacts!",
|
||||
"QUICKMESSAGE_ENEMY_DOWN": "Enemy down!",
|
||||
"QUICKMESSAGE_ENEMY_GRENADE": "Enemy grenade!",
|
||||
"QUICKMESSAGE_ENEMY_SPOTTED": "Contact!",
|
||||
"QUICKMESSAGE_FALL_BACK": "Fall back!",
|
||||
"QUICKMESSAGE_FOLLOW_ME": "On me!",
|
||||
"QUICKMESSAGE_GREAT_SHOT": "Nice shot!",
|
||||
"QUICKMESSAGE_GRENADE": "Grenade!",
|
||||
"QUICKMESSAGE_HOLD_THIS_POSITION": "Hold this position!",
|
||||
"QUICKMESSAGE_HOLD_YOUR_FIRE": "Hold your fire!",
|
||||
"QUICKMESSAGE_IM_IN_POSITION": "In position.",
|
||||
"QUICKMESSAGE_IM_ON_MY_WAY": "Moving.",
|
||||
"QUICKMESSAGE_MOVE_IN": "Move in!",
|
||||
"QUICKMESSAGE_NEED_REINFORCEMENTS": "Need reinforcements!",
|
||||
"QUICKMESSAGE_NO_SIR": "Negative.",
|
||||
"QUICKMESSAGE_ON_MY_WAY": "On my way.",
|
||||
"QUICKMESSAGE_REGROUP": "Regroup!",
|
||||
"QUICKMESSAGE_SNIPER": "Sniper!",
|
||||
"QUICKMESSAGE_SORRY": "Sorry.",
|
||||
"QUICKMESSAGE_SQUAD_ATTACK_LEFT_FLANK": "Squad, attack left flank!",
|
||||
"QUICKMESSAGE_SQUAD_ATTACK_RIGHT_FLANK": "Squad, attack right flank!",
|
||||
"QUICKMESSAGE_SQUAD_HOLD_THIS_POSITION": "Squad, hold this position!",
|
||||
"QUICKMESSAGE_SQUAD_REGROUP": "Squad, regroup!",
|
||||
"QUICKMESSAGE_SQUAD_STICK_TOGETHER": "Squad, stick together!",
|
||||
"QUICKMESSAGE_STICK_TOGETHER": "Stick together!",
|
||||
"QUICKMESSAGE_SUPPRESSING_FIRE": "Base of fire!",
|
||||
"QUICKMESSAGE_TOOK_LONG_ENOUGH": "Took long enough!",
|
||||
"QUICKMESSAGE_TOOK_YOU_LONG_ENOUGH": "Took you long enough!",
|
||||
"QUICKMESSAGE_WATCH_SIX": "Watch your six!",
|
||||
"QUICKMESSAGE_YES_SIR": "Roger.",
|
||||
"QUICKMESSAGE_YOURE_CRAZY": "You're crazy!",
|
||||
"QUICKMESSAGE_YOURE_NUTS": "You're nuts!",
|
||||
"QUICKMESSAGE_YOU_OUTTA_YOUR_MIND": "You outta your mind?"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Maps": [
|
||||
{
|
||||
"Game": "IW3",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Ambush",
|
||||
"Name": "mp_convoy"
|
||||
},
|
||||
{
|
||||
"Alias": "Backlot",
|
||||
"Name": "mp_backlot"
|
||||
},
|
||||
{
|
||||
"Alias": "Bloc",
|
||||
"Name": "mp_bloc"
|
||||
},
|
||||
{
|
||||
"Alias": "Bog",
|
||||
"Name": "mp_bog"
|
||||
},
|
||||
{
|
||||
"Alias": "Countdown",
|
||||
"Name": "mp_countdown"
|
||||
},
|
||||
{
|
||||
"Alias": "Crash",
|
||||
"Name": "mp_crash"
|
||||
},
|
||||
{
|
||||
"Alias": "Crossfire",
|
||||
"Name": "mp_crossfire"
|
||||
},
|
||||
{
|
||||
"Alias": "District",
|
||||
"Name": "mp_citystreets"
|
||||
},
|
||||
{
|
||||
"Alias": "Downpour",
|
||||
"Name": "mp_farm"
|
||||
},
|
||||
{
|
||||
"Alias": "Overgrown",
|
||||
"Name": "mp_overgrown"
|
||||
},
|
||||
{
|
||||
"Alias": "Pipeline",
|
||||
"Name": "mp_pipeline"
|
||||
},
|
||||
{
|
||||
"Alias": "Shipment",
|
||||
"Name": "mp_shipment"
|
||||
},
|
||||
{
|
||||
"Alias": "Showdown",
|
||||
"Name": "mp_showdown"
|
||||
},
|
||||
{
|
||||
"Alias": "Strike",
|
||||
"Name": "mp_strike"
|
||||
},
|
||||
{
|
||||
"Alias": "Vacant",
|
||||
"Name": "mp_vacant"
|
||||
},
|
||||
{
|
||||
"Alias": "Wet Work",
|
||||
"Name": "mp_cargoship"
|
||||
},
|
||||
{
|
||||
"Alias": "Winter Crash",
|
||||
"Name": "mp_crash_snow"
|
||||
},
|
||||
{
|
||||
"Alias": "Broadcast",
|
||||
"Name": "mp_broadcast"
|
||||
},
|
||||
{
|
||||
"Alias": "Creek",
|
||||
"Name": "mp_creek"
|
||||
},
|
||||
{
|
||||
"Alias": "Chinatown",
|
||||
"Name": "mp_carentan"
|
||||
},
|
||||
{
|
||||
"Alias": "Killhouse",
|
||||
"Name": "mp_killhouse"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "T4",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Airfield",
|
||||
"Name": "mp_airfield"
|
||||
},
|
||||
{
|
||||
"Alias": "Asylum",
|
||||
"Name": "mp_asylum"
|
||||
},
|
||||
{
|
||||
"Alias": "Castle",
|
||||
"Name": "mp_castle"
|
||||
},
|
||||
{
|
||||
"Alias": "Cliffside",
|
||||
"Name": "mp_shrine"
|
||||
},
|
||||
{
|
||||
"Alias": "Courtyard",
|
||||
"Name": "mp_courtyard"
|
||||
},
|
||||
{
|
||||
"Alias": "Dome",
|
||||
"Name": "mp_dome"
|
||||
},
|
||||
{
|
||||
"Alias": "Downfall",
|
||||
"Name": "mp_downfall"
|
||||
},
|
||||
{
|
||||
"Alias": "Hanger",
|
||||
"Name": "mp_hangar"
|
||||
},
|
||||
{
|
||||
"Alias": "Makin",
|
||||
"Name": "mp_makin"
|
||||
},
|
||||
{
|
||||
"Alias": "Outskirts",
|
||||
"Name": "mp_outskirts"
|
||||
},
|
||||
{
|
||||
"Alias": "Roundhouse",
|
||||
"Name": "mp_roundhouse"
|
||||
},
|
||||
{
|
||||
"Alias": "Upheaval",
|
||||
"Name": "mp_suburban"
|
||||
},
|
||||
{
|
||||
"Alias": "Knee Deep",
|
||||
"Name": "mp_kneedeep"
|
||||
},
|
||||
{
|
||||
"Alias": "Nightfire",
|
||||
"Name": "mp_nachtfeuer"
|
||||
},
|
||||
{
|
||||
"Alias": "Station",
|
||||
"Name": "mp_subway"
|
||||
},
|
||||
{
|
||||
"Alias": "Banzai",
|
||||
"Name": "mp_kwai"
|
||||
},
|
||||
{
|
||||
"Alias": "Corrosion",
|
||||
"Name": "mp_stalingrad"
|
||||
},
|
||||
{
|
||||
"Alias": "Sub Pens",
|
||||
"Name": "mp_docks"
|
||||
},
|
||||
{
|
||||
"Alias": "Battery",
|
||||
"Name": "mp_drum"
|
||||
},
|
||||
{
|
||||
"Alias": "Breach",
|
||||
"Name": "mp_bgate"
|
||||
},
|
||||
{
|
||||
"Alias": "Revolution",
|
||||
"Name": "mp_vodka"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "IW4",
|
||||
"Maps": [
|
||||
@ -246,6 +470,260 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "T5",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Array",
|
||||
"Name": "mp_array"
|
||||
},
|
||||
{
|
||||
"Alias": "Berlin Wall",
|
||||
"Name": "mp_berlinwall2"
|
||||
},
|
||||
{
|
||||
"Alias": "Convoy",
|
||||
"Name": "mp_gridlock"
|
||||
},
|
||||
{
|
||||
"Alias": "Cracked",
|
||||
"Name": "mp_cracked"
|
||||
},
|
||||
{
|
||||
"Alias": "Crisis",
|
||||
"Name": "mp_crisis"
|
||||
},
|
||||
{
|
||||
"Alias": "Discovery",
|
||||
"Name": "mp_discovery"
|
||||
},
|
||||
{
|
||||
"Alias": "Drive-In",
|
||||
"Name": "mp_drivein"
|
||||
},
|
||||
{
|
||||
"Alias": "Firing Range",
|
||||
"Name": "mp_firingrange"
|
||||
},
|
||||
{
|
||||
"Alias": "Grid",
|
||||
"Name": "mp_duga"
|
||||
},
|
||||
{
|
||||
"Alias": "Hangar 18",
|
||||
"Name": "mp_area51"
|
||||
},
|
||||
{
|
||||
"Alias": "Hanoi",
|
||||
"Name": "mp_hanoi"
|
||||
},
|
||||
{
|
||||
"Alias": "Hazard",
|
||||
"Name": "mp_golfcourse"
|
||||
},
|
||||
{
|
||||
"Alias": "Hotel",
|
||||
"Name": "mp_hotel"
|
||||
},
|
||||
{
|
||||
"Alias": "Jungle",
|
||||
"Name": "mp_havoc"
|
||||
},
|
||||
{
|
||||
"Alias": "Kowloon",
|
||||
"Name": "mp_kowloon"
|
||||
},
|
||||
{
|
||||
"Alias": "Launch",
|
||||
"Name": "mp_cosmodrome"
|
||||
},
|
||||
{
|
||||
"Alias": "Nuketown",
|
||||
"Name": "mp_nuked"
|
||||
},
|
||||
{
|
||||
"Alias": "Radiation",
|
||||
"Name": "mp_radiation"
|
||||
},
|
||||
{
|
||||
"Alias": "Silo",
|
||||
"Name": "mp_silo"
|
||||
},
|
||||
{
|
||||
"Alias": "Stadium",
|
||||
"Name": "mp_stadium"
|
||||
},
|
||||
{
|
||||
"Alias": "Stockpile",
|
||||
"Name": "mp_outskirts"
|
||||
},
|
||||
{
|
||||
"Alias": "Summit",
|
||||
"Name": "mp_mountain"
|
||||
},
|
||||
{
|
||||
"Alias": "Villa",
|
||||
"Name": "mp_villa"
|
||||
},
|
||||
{
|
||||
"Alias": "WMD",
|
||||
"Name": "mp_russianbase"
|
||||
},
|
||||
{
|
||||
"Alias": "Zoo",
|
||||
"Name": "mp_zoo"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "IW5",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Seatown",
|
||||
"Name": "mp_seatown"
|
||||
},
|
||||
{
|
||||
"Alias": "Lockdown",
|
||||
"Name": "mp_alpha"
|
||||
},
|
||||
{
|
||||
"Alias": "Mission",
|
||||
"Name": "mp_bravo"
|
||||
},
|
||||
{
|
||||
"Alias": "Carbon",
|
||||
"Name": "mp_carbon"
|
||||
},
|
||||
{
|
||||
"Alias": "Dome",
|
||||
"Name": "mp_dome"
|
||||
},
|
||||
{
|
||||
"Alias": "Arkaden",
|
||||
"Name": "mp_plaza2"
|
||||
},
|
||||
{
|
||||
"Alias": "Downturn",
|
||||
"Name": "mp_exchange"
|
||||
},
|
||||
{
|
||||
"Alias": "Bootleg",
|
||||
"Name": "mp_bootleg"
|
||||
},
|
||||
{
|
||||
"Alias": "Hardhat",
|
||||
"Name": "mp_hardhat"
|
||||
},
|
||||
{
|
||||
"Alias": "Interchange",
|
||||
"Name": "mp_interchange"
|
||||
},
|
||||
{
|
||||
"Alias": "Fallen",
|
||||
"Name": "mp_lambeth"
|
||||
},
|
||||
{
|
||||
"Alias": "Outpost",
|
||||
"Name": "mp_radar"
|
||||
},
|
||||
{
|
||||
"Alias": "Bakaara",
|
||||
"Name": "mp_mogadishu"
|
||||
},
|
||||
{
|
||||
"Alias": "Resistance",
|
||||
"Name": "mp_paris"
|
||||
},
|
||||
{
|
||||
"Alias": "Underground",
|
||||
"Name": "mp_underground"
|
||||
},
|
||||
{
|
||||
"Alias": "Village",
|
||||
"Name": "mp_village"
|
||||
},
|
||||
{
|
||||
"Alias": "Aground",
|
||||
"Name": "mp_aground_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Boardwalk",
|
||||
"Name": "mp_boardwalk"
|
||||
},
|
||||
{
|
||||
"Alias": "U-turn",
|
||||
"Name": "mp_burn_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Foundation",
|
||||
"Name": "mp_cement"
|
||||
},
|
||||
{
|
||||
"Alias": "Erosion",
|
||||
"Name": "mp_courtyard_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Intersection",
|
||||
"Name": "mp_crosswalk_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Getaway",
|
||||
"Name": "mp_hillside_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Piazza",
|
||||
"Name": "mp_italy"
|
||||
},
|
||||
{
|
||||
"Alias": "Sanctuary",
|
||||
"Name": "mp_meteora"
|
||||
},
|
||||
{
|
||||
"Alias": "Gulch",
|
||||
"Name": "mp_moab"
|
||||
},
|
||||
{
|
||||
"Alias": "Black Box",
|
||||
"Name": "mp_morningwood"
|
||||
},
|
||||
{
|
||||
"Alias": "Parish",
|
||||
"Name": "mp_nola"
|
||||
},
|
||||
{
|
||||
"Alias": "Overwatch",
|
||||
"Name": "mp_overwatch"
|
||||
},
|
||||
{
|
||||
"Alias": "Liberation",
|
||||
"Name": "mp_park"
|
||||
},
|
||||
{
|
||||
"Alias": "Oasis",
|
||||
"Name": "mp_qadeem"
|
||||
},
|
||||
{
|
||||
"Alias": "Lookout",
|
||||
"Name": "mp_restrepo_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Off Shore",
|
||||
"Name": "mp_roughneck"
|
||||
},
|
||||
{
|
||||
"Alias": "Decommission",
|
||||
"Name": "mp_shipbreaker"
|
||||
},
|
||||
{
|
||||
"Alias": "Vortex",
|
||||
"Name": "mp_six_ss"
|
||||
},
|
||||
{
|
||||
"Alias": "Terminal",
|
||||
"Name": "mp_terminal_cls"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "T6",
|
||||
"Maps": [
|
||||
@ -402,95 +880,6 @@
|
||||
"Name": "zm_transit"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Game": "IW3",
|
||||
"Maps": [
|
||||
{
|
||||
"Alias": "Ambush",
|
||||
"Name": "mp_convoy"
|
||||
},
|
||||
{
|
||||
"Alias": "Backlot",
|
||||
"Name": "mp_backlot"
|
||||
},
|
||||
{
|
||||
"Alias": "Bloc",
|
||||
"Name": "mp_bloc"
|
||||
},
|
||||
{
|
||||
"Alias": "Bog",
|
||||
"Name": "mp_bog"
|
||||
},
|
||||
{
|
||||
"Alias": "Countdown",
|
||||
"Name": "mp_countdown"
|
||||
},
|
||||
{
|
||||
"Alias": "Crash",
|
||||
"Name": "mp_crash"
|
||||
},
|
||||
{
|
||||
"Alias": "Crossfire",
|
||||
"Name": "mp_crossfire"
|
||||
},
|
||||
{
|
||||
"Alias": "District",
|
||||
"Name": "mp_citystreets"
|
||||
},
|
||||
{
|
||||
"Alias": "Downpour",
|
||||
"Name": "mp_farm"
|
||||
},
|
||||
{
|
||||
"Alias": "Overgrown",
|
||||
"Name": "mp_overgrown"
|
||||
},
|
||||
{
|
||||
"Alias": "Pipeline",
|
||||
"Name": "mp_pipeline"
|
||||
},
|
||||
{
|
||||
"Alias": "Shipment",
|
||||
"Name": "mp_shipment"
|
||||
},
|
||||
{
|
||||
"Alias": "Showdown",
|
||||
"Name": "mp_showdown"
|
||||
},
|
||||
{
|
||||
"Alias": "Strike",
|
||||
"Name": "mp_strike"
|
||||
},
|
||||
{
|
||||
"Alias": "Vacant",
|
||||
"Name": "mp_vacant"
|
||||
},
|
||||
{
|
||||
"Alias": "Wet Work",
|
||||
"Name": "mp_cargoship"
|
||||
},
|
||||
{
|
||||
"Alias": "Winter Crash",
|
||||
"Name": "mp_crash_snow"
|
||||
},
|
||||
{
|
||||
"Alias": "Broadcast",
|
||||
"Name": "mp_broadcast"
|
||||
},
|
||||
{
|
||||
"Alias": "Creek",
|
||||
"Name": "mp_creek"
|
||||
},
|
||||
{
|
||||
"Alias": "Chinatown",
|
||||
"Name": "mp_carentan"
|
||||
},
|
||||
{
|
||||
"Alias": "Killhouse",
|
||||
"Name": "mp_killhouse"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -17,26 +17,26 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
GameDirectory = "main",
|
||||
};
|
||||
|
||||
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{8,32});([0-9]+);(.+);(.*)$";
|
||||
Configuration.Say.Pattern = @"^(say|sayteam);(-?[A-Fa-f0-9_]{1,32});([0-9]+);(.+);(.*)$";
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
Configuration.Say.AddMapping(ParserRegex.GroupType.Message, 5);
|
||||
|
||||
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Quit.Pattern = @"^(Q);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Quit.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
|
||||
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Join.Pattern = @"^(J);(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+);([0-9]+);(.*)$";
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginNetworkId, 2);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginClientNumber, 3);
|
||||
Configuration.Join.AddMapping(ParserRegex.GroupType.OriginName, 4);
|
||||
|
||||
Configuration.Damage.Pattern = @"^(D);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{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-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||
@ -51,7 +51,7 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.MeansOfDeath, 12);
|
||||
Configuration.Damage.AddMapping(ParserRegex.GroupType.HitLocation, 13);
|
||||
|
||||
Configuration.Kill.Pattern = @"^(K);(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+);(-?[0-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{8,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{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-9]+);(axis|allies|world);(.{1,24});(-?[A-Fa-f0-9_]{1,32}|bot[0-9]+)?;-?([0-9]+);(axis|allies|world);(.{1,24})?;((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$";
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.EventType, 1);
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetNetworkId, 2);
|
||||
Configuration.Kill.AddMapping(ParserRegex.GroupType.TargetClientNumber, 3);
|
||||
@ -73,6 +73,8 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
public Game GameName { get; set; } = Game.COD;
|
||||
|
||||
public string URLProtocolFormat { get; set; } = "CoD://{{ip}}:{{port}}";
|
||||
|
||||
public virtual GameEvent GetEvent(Server server, string logLine)
|
||||
{
|
||||
logLine = Regex.Replace(logLine, @"([0-9]+:[0-9]+ |^[0-9]+ )", "").Trim();
|
||||
@ -139,11 +141,16 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
var origin = server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
||||
var target = server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
||||
string originId = match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].Value.ToString();
|
||||
string targetId = match.Groups[Configuration.Kill.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].Value.ToString();
|
||||
|
||||
var origin = !string.IsNullOrEmpty(originId) ? server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == originId.ConvertLong()) :
|
||||
Utilities.IW4MAdminClient(server);
|
||||
|
||||
var target = !string.IsNullOrEmpty(targetId) ? server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == targetId.ConvertLong()) :
|
||||
Utilities.IW4MAdminClient(server);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -159,8 +166,14 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (eventType == "ScriptKill")
|
||||
{
|
||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
long originId = lineSplit[1].ConvertLong();
|
||||
long targetId = lineSplit[2].ConvertLong();
|
||||
|
||||
var origin = originId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||
server.GetClientsAsList().First(c => c.NetworkId == originId);
|
||||
var target = targetId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||
server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == targetId) ?? Utilities.IW4MAdminClient(server);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ScriptKill,
|
||||
@ -173,8 +186,13 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (eventType == "ScriptDamage")
|
||||
{
|
||||
var origin = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetClientsAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
long originId = lineSplit[1].ConvertLong();
|
||||
long targetId = lineSplit[2].ConvertLong();
|
||||
|
||||
var origin = originId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||
server.GetClientsAsList().First(c => c.NetworkId == originId);
|
||||
var target = targetId == long.MinValue ? Utilities.IW4MAdminClient(server) :
|
||||
server.GetClientsAsList().FirstOrDefault(c => c.NetworkId == targetId) ?? Utilities.IW4MAdminClient(server);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -195,10 +213,16 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
|
||||
if (regexMatch.Success)
|
||||
{
|
||||
var origin = server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong());
|
||||
var target = server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString().ConvertLong());
|
||||
string originId = regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString();
|
||||
string targetId = regexMatch.Groups[Configuration.Damage.GroupMapping[ParserRegex.GroupType.TargetNetworkId]].ToString();
|
||||
|
||||
var origin = !string.IsNullOrEmpty(originId) ? server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == originId.ConvertLong()) :
|
||||
Utilities.IW4MAdminClient(server);
|
||||
|
||||
var target = !string.IsNullOrEmpty(targetId) ? server.GetClientsAsList()
|
||||
.First(c => c.NetworkId == targetId.ConvertLong()) :
|
||||
Utilities.IW4MAdminClient(server);
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
@ -229,7 +253,6 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = false,
|
||||
Name = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors(),
|
||||
},
|
||||
NetworkId = regexMatch.Groups[Configuration.Join.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||
@ -256,7 +279,6 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
CurrentAlias = new EFAlias()
|
||||
{
|
||||
Active = false,
|
||||
Name = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginName]].ToString().StripColors()
|
||||
},
|
||||
NetworkId = regexMatch.Groups[Configuration.Quit.GroupMapping[ParserRegex.GroupType.OriginNetworkId]].ToString().ConvertLong(),
|
||||
|
@ -30,7 +30,7 @@ namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
while (!Server.Manager.ShutdownRequested())
|
||||
{
|
||||
if ((Server.Manager as ApplicationManager).IsInitialized)
|
||||
if (Server.IsInitialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -34,9 +34,9 @@ namespace IW4MAdmin.Application.IO
|
||||
// todo: max async
|
||||
// take the old start position and go back the number of new characters
|
||||
rd.BaseStream.Seek(-fileSizeDiff, SeekOrigin.End);
|
||||
// the difference should be in the range of a int :P
|
||||
|
||||
string newLine;
|
||||
while (!String.IsNullOrEmpty(newLine = rd.ReadLine()))
|
||||
while (!string.IsNullOrEmpty(newLine = await rd.ReadLineAsync()))
|
||||
{
|
||||
logLines.Add(newLine);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Localization;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -17,6 +18,7 @@ using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
@ -25,6 +27,7 @@ namespace IW4MAdmin
|
||||
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
private GameLogEventDetection LogEvent;
|
||||
private DateTime SessionStart;
|
||||
|
||||
public int Id { get; private set; }
|
||||
|
||||
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
|
||||
@ -34,7 +37,6 @@ namespace IW4MAdmin
|
||||
override public async Task OnClientConnected(EFClient clientFromLog)
|
||||
{
|
||||
Logger.WriteDebug($"Client slot #{clientFromLog.ClientNumber} now reserved");
|
||||
Clients[clientFromLog.ClientNumber] = new EFClient();
|
||||
|
||||
try
|
||||
{
|
||||
@ -44,19 +46,16 @@ namespace IW4MAdmin
|
||||
if (client == null)
|
||||
{
|
||||
Logger.WriteDebug($"Client {clientFromLog} first time connecting");
|
||||
clientFromLog.CurrentServer = this;
|
||||
client = await Manager.GetClientService().Create(clientFromLog);
|
||||
}
|
||||
|
||||
// client has connected in the past
|
||||
else
|
||||
/// this is only a temporary version until the IPAddress is transmitted
|
||||
client.CurrentAlias = new EFAlias()
|
||||
{
|
||||
// this is only a temporary version until the IPAddress is transmitted
|
||||
client.CurrentAlias = new EFAlias
|
||||
{
|
||||
Name = clientFromLog.Name,
|
||||
IPAddress = clientFromLog.IPAddress
|
||||
};
|
||||
}
|
||||
Name = clientFromLog.Name,
|
||||
IPAddress = clientFromLog.IPAddress
|
||||
};
|
||||
|
||||
Logger.WriteInfo($"Client {client} connected...");
|
||||
|
||||
@ -68,8 +67,6 @@ namespace IW4MAdmin
|
||||
client.CurrentServer = this;
|
||||
|
||||
Clients[client.ClientNumber] = client;
|
||||
|
||||
client.State = EFClient.ClientState.Connected;
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"End PreConnect for {client}");
|
||||
#endif
|
||||
@ -81,11 +78,8 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
|
||||
if (client.IPAddress != null)
|
||||
{
|
||||
await client.OnJoin(client.IPAddress);
|
||||
}
|
||||
await client.OnJoin(client.IPAddress);
|
||||
client.State = ClientState.Connected;
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
@ -97,12 +91,14 @@ namespace IW4MAdmin
|
||||
|
||||
override public async Task OnClientDisconnected(EFClient client)
|
||||
{
|
||||
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
|
||||
await client.OnDisconnect();
|
||||
Clients[client.ClientNumber] = null;
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"End PreDisconnect for {client}");
|
||||
if (client.ClientNumber >= 0)
|
||||
{
|
||||
#endif
|
||||
Logger.WriteInfo($"Client {client} [{client.State.ToString().ToLower()}] disconnecting...");
|
||||
Clients[client.ClientNumber] = null;
|
||||
await client.OnDisconnect();
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Origin = client,
|
||||
@ -111,6 +107,9 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
#if DEBUG == true
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override async Task ExecuteEvent(GameEvent E)
|
||||
@ -176,9 +175,33 @@ namespace IW4MAdmin
|
||||
/// <returns></returns>
|
||||
override protected async Task<bool> ProcessEvent(GameEvent E)
|
||||
{
|
||||
if (E.Type == GameEvent.EventType.ConnectionLost)
|
||||
{
|
||||
var exception = E.Extra as Exception;
|
||||
Logger.WriteError(exception.Message);
|
||||
if (exception.Data["internal_exception"] != null)
|
||||
{
|
||||
Logger.WriteDebug($"Internal Exception: {exception.Data["internal_exception"]}");
|
||||
}
|
||||
Logger.WriteInfo("Connection lost to server, so we are throttling the poll rate");
|
||||
Throttled = true;
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.ConnectionRestored)
|
||||
{
|
||||
if (Throttled)
|
||||
{
|
||||
Logger.WriteVerbose(loc["MANAGER_CONNECTION_REST"].FormatExt($"[{IP}:{Port}]"));
|
||||
}
|
||||
Logger.WriteInfo("Connection restored to server, so we are no longer throttling the poll rate");
|
||||
Throttled = false;
|
||||
}
|
||||
|
||||
if (E.Type == GameEvent.EventType.ChangePermission)
|
||||
{
|
||||
if (!E.Target.IsPrivileged())
|
||||
var newPermission = (Permission)E.Extra;
|
||||
|
||||
if (newPermission < Permission.Moderator)
|
||||
{
|
||||
// remove banned or demoted privileged user
|
||||
Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
|
||||
@ -186,17 +209,46 @@ namespace IW4MAdmin
|
||||
|
||||
else
|
||||
{
|
||||
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
||||
if (Manager.GetPrivilegedClients().ContainsKey(E.Target.ClientId))
|
||||
{
|
||||
Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Manager.GetPrivilegedClients().Add(E.Target.ClientId, E.Target);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.WriteInfo($"{E.Origin} is setting {E.Target} to permission level {newPermission}");
|
||||
await Manager.GetClientService().UpdateLevel(newPermission, E.Target, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.PreConnect)
|
||||
{
|
||||
// we don't want to track bots in the database at all if ignore bots is requested
|
||||
if (E.Origin.IsBot && Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var existingClient = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
||||
|
||||
// they're already connected
|
||||
if (existingClient != null)
|
||||
{
|
||||
Logger.WriteWarning($"detected preconnect for {E.Origin}, but they are already connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
CONNECT:
|
||||
if (Clients[E.Origin.ClientNumber] == null)
|
||||
{
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
|
||||
#endif
|
||||
// we can go ahead and put them in so that they don't get re added
|
||||
Clients[E.Origin.ClientNumber] = E.Origin;
|
||||
await OnClientConnected(E.Origin);
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
@ -212,9 +264,12 @@ namespace IW4MAdmin
|
||||
}
|
||||
}
|
||||
|
||||
// for some reason there's still a client in the spot
|
||||
else
|
||||
{
|
||||
return false;
|
||||
Logger.WriteWarning($"{E.Origin} is connecteding but {Clients[E.Origin.ClientNumber]} is currently in that client slot");
|
||||
await OnClientDisconnected(Clients[E.Origin.ClientNumber]);
|
||||
goto CONNECT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -233,17 +288,29 @@ namespace IW4MAdmin
|
||||
};
|
||||
|
||||
var addedPenalty = await Manager.GetPenaltyService().Create(newPenalty);
|
||||
await Manager.GetClientService().Update(E.Target);
|
||||
E.Target.SetLevel(Permission.Flagged, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Unflag)
|
||||
{
|
||||
await Manager.GetClientService().Update(E.Target);
|
||||
var unflagPenalty = new Penalty()
|
||||
{
|
||||
Type = Penalty.PenaltyType.Unflag,
|
||||
Expires = DateTime.UtcNow,
|
||||
Offender = E.Target,
|
||||
Offense = E.Data,
|
||||
Punisher = E.Origin,
|
||||
When = DateTime.UtcNow,
|
||||
Link = E.Target.AliasLink
|
||||
};
|
||||
|
||||
await Manager.GetPenaltyService().Create(unflagPenalty);
|
||||
E.Target.SetLevel(Permission.User, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Report)
|
||||
{
|
||||
this.Reports.Add(new Report()
|
||||
Reports.Add(new Report()
|
||||
{
|
||||
Origin = E.Origin,
|
||||
Target = E.Target,
|
||||
@ -277,37 +344,21 @@ namespace IW4MAdmin
|
||||
await Warn(E.Data, E.Target, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Quit)
|
||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||
{
|
||||
var origin = GetClientsAsList().FirstOrDefault(_client => _client.NetworkId.Equals(E.Origin));
|
||||
|
||||
if (origin != null)
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Disconnect,
|
||||
Origin = origin,
|
||||
Owner = this
|
||||
};
|
||||
Name = E.Origin.Name,
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await new MetaService().AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
|
||||
await new MetaService().AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.PreDisconnect)
|
||||
{
|
||||
if ((DateTime.UtcNow - SessionStart).TotalSeconds < 30)
|
||||
{
|
||||
Logger.WriteInfo($"Cancelling pre disconnect for {E.Origin} as it occured too close to map end");
|
||||
E.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
return false;
|
||||
}
|
||||
|
||||
// predisconnect comes from minimal rcon polled players and minimal log players
|
||||
// so we need to disconnect the "full" version of the client
|
||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));
|
||||
@ -317,18 +368,16 @@ namespace IW4MAdmin
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"Begin PreDisconnect for {client}");
|
||||
#endif
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = client.Name,
|
||||
Message = "DISCONNECTED",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await OnClientDisconnected(client);
|
||||
#if DEBUG == true
|
||||
Logger.WriteDebug($"End PreDisconnect for {client}");
|
||||
#endif
|
||||
}
|
||||
|
||||
else
|
||||
else if (client?.State != ClientState.Disconnecting)
|
||||
{
|
||||
Logger.WriteWarning($"Client {E.Origin} detected as disconnecting, but could not find them in the player list");
|
||||
Logger.WriteDebug($"Expected {E.Origin} but found {GetClientsAsList().FirstOrDefault(_client => _client.ClientNumber == E.Origin.ClientNumber)}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -345,18 +394,27 @@ namespace IW4MAdmin
|
||||
{
|
||||
E.Data = E.Data.StripColors();
|
||||
|
||||
if (E.Data.Length > 0)
|
||||
if (E.Data?.Length > 0)
|
||||
{
|
||||
// this may be a fix for a hard to reproduce null exception error
|
||||
lock (ChatHistory)
|
||||
string message = E.Data;
|
||||
if (E.Data.IsQuickMessage())
|
||||
{
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
try
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = E.Data ?? "NULL",
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
message = Manager.GetApplicationSettings().Configuration()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == GameName)
|
||||
.Messages[E.Data.Substring(1)];
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
ChatHistory.Add(new ChatInfo()
|
||||
{
|
||||
Name = E.Origin.Name,
|
||||
Message = message,
|
||||
Time = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,7 +447,7 @@ namespace IW4MAdmin
|
||||
var dict = (Dictionary<string, string>)E.Extra;
|
||||
Gametype = dict["g_gametype"].StripColors();
|
||||
Hostname = dict["sv_hostname"].StripColors();
|
||||
MaxClients = Int32.Parse(dict["sv_maxclients"]);
|
||||
MaxClients = int.Parse(dict["sv_maxclients"]);
|
||||
|
||||
string mapname = dict["mapname"].StripColors();
|
||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
|
||||
@ -440,9 +498,9 @@ namespace IW4MAdmin
|
||||
return true;
|
||||
}
|
||||
|
||||
private Task OnClientUpdate(EFClient origin)
|
||||
private async Task OnClientUpdate(EFClient origin)
|
||||
{
|
||||
var client = Clients[origin.ClientNumber];
|
||||
var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(origin));
|
||||
|
||||
if (client != null)
|
||||
{
|
||||
@ -450,13 +508,22 @@ namespace IW4MAdmin
|
||||
client.Score = origin.Score;
|
||||
|
||||
// update their IP if it hasn't been set yet
|
||||
if (client.IPAddress == null && !client.IsBot)
|
||||
if (client.IPAddress == null &&
|
||||
!client.IsBot &&
|
||||
client.State == ClientState.Connected)
|
||||
{
|
||||
return client.OnJoin(origin.IPAddress);
|
||||
try
|
||||
{
|
||||
await client.OnJoin(origin.IPAddress);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
origin.CurrentServer.Logger.WriteWarning($"Could not execute on join for {origin}");
|
||||
origin.CurrentServer.Logger.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -472,6 +539,7 @@ namespace IW4MAdmin
|
||||
#endif
|
||||
var currentClients = GetClientsAsList();
|
||||
var polledClients = (await this.GetStatusAsync()).AsEnumerable();
|
||||
|
||||
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||
{
|
||||
polledClients = polledClients.Where(c => !c.IsBot);
|
||||
@ -479,8 +547,6 @@ namespace IW4MAdmin
|
||||
#if DEBUG
|
||||
Logger.WriteInfo($"Polling players took {(DateTime.Now - now).TotalMilliseconds}ms");
|
||||
#endif
|
||||
Throttled = false;
|
||||
|
||||
var disconnectingClients = currentClients.Except(polledClients);
|
||||
var connectingClients = polledClients.Except(currentClients);
|
||||
var updatedClients = polledClients.Except(connectingClients).Except(disconnectingClients);
|
||||
@ -533,7 +599,7 @@ namespace IW4MAdmin
|
||||
|
||||
foreach (var disconnectingClient in polledClients[1])
|
||||
{
|
||||
if (disconnectingClient.State == EFClient.ClientState.Disconnecting)
|
||||
if (disconnectingClient.State == ClientState.Disconnecting)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -557,6 +623,12 @@ namespace IW4MAdmin
|
||||
// this are our new connecting clients
|
||||
foreach (var client in polledClients[0])
|
||||
{
|
||||
// note: this prevents players in ZMBI state from being registered with no name
|
||||
if (string.IsNullOrEmpty(client.Name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.PreConnect,
|
||||
@ -590,8 +662,15 @@ namespace IW4MAdmin
|
||||
|
||||
if (ConnectionErrors > 0)
|
||||
{
|
||||
Logger.WriteVerbose($"{loc["MANAGER_CONNECTION_REST"]} {IP}:{Port}");
|
||||
Throttled = false;
|
||||
var _event = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ConnectionRestored,
|
||||
Owner = this,
|
||||
Origin = Utilities.IW4MAdminClient(this),
|
||||
Target = Utilities.IW4MAdminClient(this)
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(_event);
|
||||
}
|
||||
|
||||
ConnectionErrors = 0;
|
||||
@ -603,9 +682,17 @@ namespace IW4MAdmin
|
||||
ConnectionErrors++;
|
||||
if (ConnectionErrors == 3)
|
||||
{
|
||||
Logger.WriteError($"{e.Message} {IP}:{Port}, {loc["SERVER_ERROR_POLLING"]}");
|
||||
Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
|
||||
Throttled = true;
|
||||
var _event = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ConnectionLost,
|
||||
Owner = this,
|
||||
Origin = Utilities.IW4MAdminClient(this),
|
||||
Target = Utilities.IW4MAdminClient(this),
|
||||
Extra = e,
|
||||
Data = ConnectionErrors.ToString()
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(_event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -630,7 +717,7 @@ namespace IW4MAdmin
|
||||
&& BroadcastMessages.Count > 0
|
||||
&& ClientNum > 0)
|
||||
{
|
||||
string[] messages = this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]).Split(Environment.NewLine);
|
||||
string[] messages = (await this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])).Split(Environment.NewLine);
|
||||
|
||||
foreach (string message in messages)
|
||||
{
|
||||
@ -647,9 +734,9 @@ namespace IW4MAdmin
|
||||
// this one is ok
|
||||
catch (ServerException e)
|
||||
{
|
||||
if (e is NetworkException)
|
||||
if (e is NetworkException && !Throttled)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_COMMUNICATION"]} {IP}:{Port}");
|
||||
Logger.WriteError(loc["SERVER_ERROR_COMMUNICATION"].FormatExt($"{IP}:{Port}"));
|
||||
Logger.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
|
||||
@ -658,7 +745,7 @@ namespace IW4MAdmin
|
||||
|
||||
catch (Exception E)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_EXCEPTION"]} {IP}:{Port}");
|
||||
Logger.WriteError(loc["SERVER_ERROR_EXCEPTION"].FormatExt($"[{IP}:{Port}]"));
|
||||
Logger.WriteDebug(E.GetExceptionInfo());
|
||||
return false;
|
||||
}
|
||||
@ -751,7 +838,7 @@ namespace IW4MAdmin
|
||||
}
|
||||
|
||||
CustomCallback = await ScriptLoaded();
|
||||
|
||||
|
||||
// they've manually specified the log path
|
||||
if (!string.IsNullOrEmpty(ServerConfig.ManualLogPath))
|
||||
{
|
||||
@ -774,8 +861,8 @@ namespace IW4MAdmin
|
||||
|
||||
if (!File.Exists(LogPath) && ServerConfig.GameLogServerUrl == null)
|
||||
{
|
||||
Logger.WriteError($"{LogPath} {loc["SERVER_ERROR_DNE"]}");
|
||||
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {LogPath}");
|
||||
Logger.WriteError(loc["SERVER_ERROR_DNE"].FormatExt(LogPath));
|
||||
throw new ServerException(loc["SERVER_ERROR_DNE"].FormatExt(LogPath));
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,7 +932,7 @@ namespace IW4MAdmin
|
||||
#if !DEBUG
|
||||
else
|
||||
{
|
||||
string formattedKick = String.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7");
|
||||
string formattedKick = string.Format(RconParser.Configuration.CommandPrefixes.Kick, Target.ClientNumber, $"{loc["SERVER_KICK_TEXT"]} - ^5{Reason}^7");
|
||||
await Target.CurrentServer.ExecuteCommandAsync(formattedKick);
|
||||
}
|
||||
#endif
|
||||
@ -925,11 +1012,9 @@ namespace IW4MAdmin
|
||||
|
||||
else
|
||||
{
|
||||
// this is set only because they're still in the server.
|
||||
targetClient.Level = EFClient.Permission.Banned;
|
||||
|
||||
#if !DEBUG
|
||||
string formattedString = String.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
|
||||
string formattedString = String.Format(RconParser.Configuration.CommandPrefixes.Kick, targetClient.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{reason} ^7{loc["SERVER_BAN_APPEAL"].FormatExt(Website)}^7");
|
||||
await targetClient.CurrentServer.ExecuteCommandAsync(formattedString);
|
||||
#else
|
||||
await targetClient.CurrentServer.OnClientDisconnected(targetClient);
|
||||
@ -946,8 +1031,10 @@ namespace IW4MAdmin
|
||||
Link = targetClient.AliasLink,
|
||||
AutomatedOffense = originClient.AdministeredPenalties?.FirstOrDefault()?.AutomatedOffense,
|
||||
IsEvadedOffense = isEvade
|
||||
|
||||
};
|
||||
|
||||
targetClient.SetLevel(Permission.Banned, originClient);
|
||||
await Manager.GetPenaltyService().Create(newPenalty);
|
||||
}
|
||||
|
||||
@ -965,16 +1052,17 @@ namespace IW4MAdmin
|
||||
Link = Target.AliasLink
|
||||
};
|
||||
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId);
|
||||
await Manager.GetPenaltyService().RemoveActivePenalties(Target.AliasLink.AliasLinkId, Origin);
|
||||
await Manager.GetPenaltyService().Create(unbanPenalty);
|
||||
Target.SetLevel(Permission.User, Origin);
|
||||
}
|
||||
|
||||
override public void InitializeTokens()
|
||||
{
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("TOTALPLAYERS", (Server s) => Manager.GetClientService().GetTotalClientsAsync().Result.ToString()));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("VERSION", (Server s) => Application.Program.Version.ToString()));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.CNextMap.GetNextMap(s).Result));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("ADMINS", (Server s) => SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s)));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString())));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.CNextMap.GetNextMap(s)));
|
||||
Manager.GetMessageTokens().Add(new SharedLibraryCore.Helpers.MessageToken("ADMINS", (Server s) => Task.FromResult(SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,17 +17,20 @@ namespace IW4MAdmin.Application.Localization
|
||||
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
|
||||
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
|
||||
|
||||
try
|
||||
if (!Program.ServerManager.GetApplicationSettings()?.Configuration()?.UseLocalTranslations ?? false)
|
||||
{
|
||||
var api = Endpoint.Get();
|
||||
var localization = api.GetLocalization(currentLocale).Result;
|
||||
Utilities.CurrentLocalization = localization;
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
var api = Endpoint.Get();
|
||||
var localization = api.GetLocalization(currentLocale).Result;
|
||||
Utilities.CurrentLocalization = localization;
|
||||
return;
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
// the online localization failed so will default to local files
|
||||
catch (Exception)
|
||||
{
|
||||
// the online localization failed so will default to local files
|
||||
}
|
||||
}
|
||||
|
||||
// culture doesn't exist so we just want language
|
||||
|
@ -1,269 +0,0 @@
|
||||
{
|
||||
"LocalizationName": "en-US",
|
||||
"LocalizationIndex": {
|
||||
"Set": {
|
||||
"BROADCAST_OFFLINE": "^5IW4MAdmin ^7is going ^1OFFLINE",
|
||||
"BROADCAST_ONLINE": "^5IW4MADMIN ^7is now ^2ONLINE",
|
||||
"COMMAND_HELP_OPTIONAL": "optional",
|
||||
"COMMAND_HELP_SYNTAX": "syntax:",
|
||||
"COMMAND_MISSINGARGS": "Not enough arguments supplied",
|
||||
"COMMAND_NOACCESS": "You do not have access to that command",
|
||||
"COMMAND_NOTAUTHORIZED": "You are not authorized to execute that command",
|
||||
"COMMAND_TARGET_MULTI": "Multiple players match that name",
|
||||
"COMMAND_TARGET_NOTFOUND": "Unable to find specified player",
|
||||
"COMMAND_UNKNOWN": "You entered an unknown command",
|
||||
"COMMANDS_ADMINS_DESC": "list currently connected privileged clients",
|
||||
"COMMANDS_ADMINS_NONE": "No visible administrators online",
|
||||
"COMMANDS_ALIAS_ALIASES": "Aliases",
|
||||
"COMMANDS_ALIAS_DESC": "get past aliases and ips of a client",
|
||||
"COMMANDS_ALIAS_IPS": "IPs",
|
||||
"COMMANDS_ARGS_CLEAR": "clear",
|
||||
"COMMANDS_ARGS_CLIENTID": "client id",
|
||||
"COMMANDS_ARGS_COMMANDS": "commands",
|
||||
"COMMANDS_ARGS_DURATION": "duration (m|h|d|w|y)",
|
||||
"COMMANDS_ARGS_INACTIVE": "inactive days",
|
||||
"COMMANDS_ARGS_LEVEL": "level",
|
||||
"COMMANDS_ARGS_MAP": "map",
|
||||
"COMMANDS_ARGS_MESSAGE": "message",
|
||||
"COMMANDS_ARGS_PASSWORD": "password",
|
||||
"COMMANDS_ARGS_PLAYER": "player",
|
||||
"COMMANDS_ARGS_REASON": "reason",
|
||||
"COMMANDS_BAN_DESC": "permanently ban a client from the server",
|
||||
"COMMANDS_BAN_FAIL": "You cannot ban",
|
||||
"COMMANDS_BAN_SUCCESS": "has been permanently banned",
|
||||
"COMMANDS_BANINFO_DESC": "get information about a ban for a client",
|
||||
"COMMANDS_BANINFO_NONE": "No active ban was found for that player",
|
||||
"COMMANDS_BANINO_SUCCESS": "was banned by ^5{0} ^7for:",
|
||||
"COMMANDS_FASTRESTART_DESC": "fast restart current map",
|
||||
"COMMANDS_FASTRESTART_MASKED": "The map has been fast restarted",
|
||||
"COMMANDS_FASTRESTART_UNMASKED": "fast restarted the map",
|
||||
"COMMANDS_FIND_DESC": "find client in database",
|
||||
"COMMANDS_FIND_EMPTY": "No players found",
|
||||
"COMMANDS_FIND_MIN": "Please enter at least 3 characters",
|
||||
"COMMANDS_FLAG_DESC": "flag a suspicious client and announce to admins on join",
|
||||
"COMMANDS_FLAG_FAIL": "You cannot flag",
|
||||
"COMMANDS_FLAG_SUCCESS": "You have flagged",
|
||||
"COMMANDS_FLAG_UNFLAG": "You have unflagged",
|
||||
"COMMANDS_HELP_DESC": "list all available commands",
|
||||
"COMMANDS_HELP_MOREINFO": "Type !help <command name> to get command usage syntax",
|
||||
"COMMANDS_HELP_NOTFOUND": "Could not find that command",
|
||||
"COMMANDS_IP_DESC": "view your external IP address",
|
||||
"COMMANDS_IP_SUCCESS": "Your external IP is",
|
||||
"COMMANDS_KICK_DESC": "kick a client by name",
|
||||
"COMMANDS_KICK_FAIL": "You do not have the required privileges to kick",
|
||||
"COMMANDS_KICK_SUCCESS": "has been kicked",
|
||||
"COMMANDS_LIST_DESC": "list active clients",
|
||||
"COMMANDS_MAP_DESC": "change to specified map",
|
||||
"COMMANDS_MAP_SUCCESS": "Changing to map",
|
||||
"COMMANDS_MAP_UKN": "Attempting to change to unknown map",
|
||||
"COMMANDS_MAPROTATE": "Map rotating in ^55 ^7seconds",
|
||||
"COMMANDS_MAPROTATE_DESC": "cycle to the next map in rotation",
|
||||
"COMMANDS_MASK_DESC": "hide your presence as a privileged client",
|
||||
"COMMANDS_MASK_OFF": "You are now unmasked",
|
||||
"COMMANDS_MASK_ON": "You are now masked",
|
||||
"COMMANDS_OWNER_DESC": "claim ownership of the server",
|
||||
"COMMANDS_OWNER_FAIL": "This server already has an owner",
|
||||
"COMMANDS_OWNER_SUCCESS": "Congratulations, you have claimed ownership of this server!",
|
||||
"COMMANDS_PASSWORD_FAIL": "Your password must be at least 5 characters long",
|
||||
"COMMANDS_PASSWORD_SUCCESS": "Your password has been set successfully",
|
||||
"COMMANDS_PING_DESC": "get client's latency",
|
||||
"COMMANDS_PING_SELF": "Your latency is",
|
||||
"COMMANDS_PING_TARGET": "latency is",
|
||||
"COMMANDS_PLUGINS_DESC": "view all loaded plugins",
|
||||
"COMMANDS_PLUGINS_LOADED": "Loaded Plugins",
|
||||
"COMMANDS_PM_DESC": "send message to other client",
|
||||
"COMMANDS_PRUNE_DESC": "demote any privileged clients that have not connected recently (defaults to 30 days)",
|
||||
"COMMANDS_PRUNE_FAIL": "Invalid number of inactive days",
|
||||
"COMMANDS_PRUNE_SUCCESS": "inactive privileged users were pruned",
|
||||
"COMMANDS_QUIT_DESC": "quit IW4MAdmin",
|
||||
"COMMANDS_RCON_DESC": "send rcon command to server",
|
||||
"COMMANDS_RCON_SUCCESS": "Successfully sent RCon command",
|
||||
"COMMANDS_REPORT_DESC": "report a client for suspicious behavior",
|
||||
"COMMANDS_REPORT_FAIL": "You cannot report",
|
||||
"COMMANDS_REPORT_FAIL_CAMP": "You cannot report an player for camping",
|
||||
"COMMANDS_REPORT_FAIL_DUPLICATE": "You have already reported this player",
|
||||
"COMMANDS_REPORT_FAIL_SELF": "You cannot report yourself",
|
||||
"COMMANDS_REPORT_SUCCESS": "Thank you for your report, an administrator has been notified",
|
||||
"COMMANDS_REPORTS_CLEAR_SUCCESS": "Reports successfully cleared",
|
||||
"COMMANDS_REPORTS_DESC": "get or clear recent reports",
|
||||
"COMMANDS_REPORTS_NONE": "No players reported yet",
|
||||
"COMMANDS_RULES_DESC": "list server rules",
|
||||
"COMMANDS_RULES_NONE": "The server owner has not set any rules",
|
||||
"COMMANDS_SAY_DESC": "broadcast message to all clients",
|
||||
"COMMANDS_SETLEVEL_DESC": "set client to specified privilege level",
|
||||
"COMMANDS_SETLEVEL_FAIL": "Invalid group specified",
|
||||
"COMMANDS_SETLEVEL_LEVELTOOHIGH": "You can only promote ^5{0} ^7to ^5{1} ^7or lower privilege",
|
||||
"COMMANDS_SETLEVEL_OWNER": "There can only be 1 owner. Modify your settings if multiple owners are required",
|
||||
"COMMANDS_SETLEVEL_SELF": "You cannot change your own level",
|
||||
"COMMANDS_SETLEVEL_STEPPEDDISABLED": "This server does not allow you to promote",
|
||||
"COMMANDS_SETLEVEL_SUCCESS": "was successfully promoted",
|
||||
"COMMANDS_SETLEVEL_SUCCESS_TARGET": "Congratulations! You have been promoted to",
|
||||
"COMMANDS_SETPASSWORD_DESC": "set your authentication password",
|
||||
"COMMANDS_TEMPBAN_DESC": "temporarily ban a client for specified time (defaults to 1 hour)",
|
||||
"COMMANDS_TEMPBAN_FAIL": "You cannot temporarily ban",
|
||||
"COMMANDS_TEMPBAN_SUCCESS": "has been temporarily banned for",
|
||||
"COMMANDS_UNBAN_DESC": "unban client by client id",
|
||||
"COMMANDS_UNBAN_FAIL": "is not banned",
|
||||
"COMMANDS_UNBAN_SUCCESS": "Successfully unbanned",
|
||||
"COMMANDS_UPTIME_DESC": "get current application running time",
|
||||
"COMMANDS_UPTIME_TEXT": "has been online for",
|
||||
"COMMANDS_USAGE_DESC": "get application memory usage",
|
||||
"COMMANDS_USAGE_TEXT": "is using",
|
||||
"COMMANDS_WARN_DESC": "warn client for infringing rules",
|
||||
"COMMANDS_WARN_FAIL": "You do not have the required privileges to warn",
|
||||
"COMMANDS_WARNCLEAR_DESC": "remove all warnings for a client",
|
||||
"COMMANDS_WARNCLEAR_SUCCESS": "All warning cleared for",
|
||||
"COMMANDS_WHO_DESC": "give information about yourself",
|
||||
"GLOBAL_TIME_DAYS": "days",
|
||||
"GLOBAL_ERROR": "Error",
|
||||
"GLOBAL_TIME_HOURS": "hours",
|
||||
"GLOBAL_INFO": "Info",
|
||||
"GLOBAL_TIME_MINUTES": "minutes",
|
||||
"GLOBAL_REPORT": "If you suspect someone of ^5CHEATING ^7use the ^5!report ^7command",
|
||||
"GLOBAL_VERBOSE": "Verbose",
|
||||
"GLOBAL_WARNING": "Warning",
|
||||
"MANAGER_CONNECTION_REST": "Connection has been reestablished with",
|
||||
"MANAGER_CONSOLE_NOSERV": "No servers are currently being monitored",
|
||||
"MANAGER_EXIT": "Press any key to exit...",
|
||||
"MANAGER_INIT_FAIL": "Fatal error during initialization",
|
||||
"MANAGER_MONITORING_TEXT": "Now monitoring",
|
||||
"MANAGER_SHUTDOWN_SUCCESS": "Shutdown complete",
|
||||
"MANAGER_VERSION_CURRENT": "Your version is",
|
||||
"MANAGER_VERSION_FAIL": "Could not get latest IW4MAdmin version",
|
||||
"MANAGER_VERSION_SUCCESS": "IW4MAdmin is up to date",
|
||||
"MANAGER_VERSION_UPDATE": "has an update. Latest version is",
|
||||
"PLUGIN_IMPORTER_NOTFOUND": "No plugins found to load",
|
||||
"PLUGIN_IMPORTER_REGISTERCMD": "Registered command",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_DESC": "login using password",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL": "Your password is incorrect",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS": "You are now logged in",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_DESC": "reset your stats to factory-new",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_FAIL": "You must be connected to a server to reset your stats",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_SUCCESS": "Your stats for this server have been reset",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_DESC": "view the top 5 players in this server",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_TEXT": "Top Players",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_DESC": "view your stats",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL": "Cannot find the player you specified",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME": "The specified player must be ingame",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME_SELF": "You must be ingame to view your stats",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_SUCCESS": "Stats for",
|
||||
"PLUGINS_STATS_TEXT_DEATHS": "DEATHS",
|
||||
"PLUGINS_STATS_TEXT_KILLS": "KILLS",
|
||||
"PLUGINS_STATS_TEXT_NOQUALIFY": "No players qualify for top stats yet",
|
||||
"PLUGINS_STATS_TEXT_SKILL": "SKILL",
|
||||
"SERVER_BAN_APPEAL": "appeal at",
|
||||
"SERVER_BAN_PREV": "Previously banned for",
|
||||
"SERVER_BAN_TEXT": "You're banned",
|
||||
"SERVER_ERROR_ADDPLAYER": "Unable to add player",
|
||||
"SERVER_ERROR_COMMAND_INGAME": "An internal error occured while processing your command",
|
||||
"SERVER_ERROR_COMMAND_LOG": "command generated an error",
|
||||
"SERVER_ERROR_COMMUNICATION": "Could not communicate with",
|
||||
"SERVER_ERROR_DNE": "does not exist",
|
||||
"SERVER_ERROR_DVAR": "Could not get the dvar value for",
|
||||
"SERVER_ERROR_DVAR_HELP": "ensure the server has a map loaded",
|
||||
"SERVER_ERROR_EXCEPTION": "Unexpected exception on",
|
||||
"SERVER_ERROR_LOG": "Invalid game log file",
|
||||
"SERVER_ERROR_PLUGIN": "An error occured loading plugin",
|
||||
"SERVER_ERROR_POLLING": "reducing polling rate",
|
||||
"SERVER_ERROR_UNFIXABLE": "Not monitoring server due to uncorrectable errors",
|
||||
"SERVER_KICK_CONTROLCHARS": "Your name cannot contain control characters",
|
||||
"SERVER_KICK_GENERICNAME": "Please change your name using /name",
|
||||
"SERVER_KICK_MINNAME": "Your name must contain at least 3 characters",
|
||||
"SERVER_KICK_NAME_INUSE": "Your name is being used by someone else",
|
||||
"SERVER_KICK_TEXT": "You were kicked",
|
||||
"SERVER_KICK_VPNS_NOTALLOWED": "VPNs are not allowed on this server",
|
||||
"SERVER_PLUGIN_ERROR": "A plugin generated an error",
|
||||
"SERVER_REPORT_COUNT": "There are ^5{0} ^7recent reports",
|
||||
"SERVER_TB_REMAIN": "You are temporarily banned",
|
||||
"SERVER_TB_TEXT": "You're temporarily banned",
|
||||
"SERVER_WARNING": "WARNING",
|
||||
"SERVER_WARNLIMT_REACHED": "Too many warnings",
|
||||
"SERVER_WEBSITE_GENERIC": "this server's website",
|
||||
"SETUP_DISPLAY_SOCIAL": "Display social media link on webfront (discord, website, VK, etc..)",
|
||||
"SETUP_ENABLE_CUSTOMSAY": "Enable custom say name",
|
||||
"SETUP_ENABLE_MULTIOWN": "Enable multiple owners",
|
||||
"SETUP_ENABLE_STEPPEDPRIV": "Enable stepped privilege hierarchy",
|
||||
"SETUP_ENABLE_VPNS": "Enable client VPNs",
|
||||
"SETUP_ENABLE_WEBFRONT": "Enable webfront",
|
||||
"SETUP_ENCODING_STRING": "Enter encoding string",
|
||||
"SETUP_IPHUB_KEY": "Enter iphub.info api key",
|
||||
"SETUP_SAY_NAME": "Enter custom say name",
|
||||
"SETUP_SERVER_IP": "Enter server IP Address",
|
||||
"SETUP_SERVER_MANUALLOG": "Enter manual log file path",
|
||||
"SETUP_SERVER_PORT": "Enter server port",
|
||||
"SETUP_SERVER_RCON": "Enter server RCon password",
|
||||
"SETUP_SERVER_SAVE": "Configuration saved, add another",
|
||||
"SETUP_SERVER_USEIW5M": "Use Pluto IW5 Parser",
|
||||
"SETUP_SERVER_USET6M": "Use Pluto T6 parser",
|
||||
"SETUP_SOCIAL_LINK": "Enter social media link",
|
||||
"SETUP_SOCIAL_TITLE": "Enter social media name",
|
||||
"SETUP_USE_CUSTOMENCODING": "Use custom encoding parser",
|
||||
"WEBFRONT_ACTION_BAN_NAME": "Ban",
|
||||
"WEBFRONT_ACTION_LABEL_ID": "Client ID",
|
||||
"WEBFRONT_ACTION_LABEL_PASSWORD": "Password",
|
||||
"WEBFRONT_ACTION_LABEL_REASON": "Reason",
|
||||
"WEBFRONT_ACTION_LOGIN_NAME": "Login",
|
||||
"WEBFRONT_ACTION_UNBAN_NAME": "Unban",
|
||||
"WEBFRONT_CLIENT_META_FALSE": "Is not",
|
||||
"WEBFRONT_CLIENT_META_JOINED": "Joined with alias",
|
||||
"WEBFRONT_CLIENT_META_MASKED": "Masked",
|
||||
"WEBFRONT_CLIENT_META_TRUE": "Is",
|
||||
"WEBFRONT_CLIENT_PRIVILEGED_TITLE": "Privileged Clients",
|
||||
"WEBFRONT_CLIENT_PROFILE_TITLE": "Profile",
|
||||
"WEBFRONT_CLIENT_SEARCH_MATCHING": "Clients Matching",
|
||||
"WEBFRONT_CONSOLE_EXECUTE": "Execute",
|
||||
"WEBFRONT_CONSOLE_TITLE": "Web Console",
|
||||
"WEBFRONT_ERROR_DESC": "IW4MAdmin encountered an error",
|
||||
"WEBFRONT_ERROR_GENERIC_DESC": "An error occurred while processing your request",
|
||||
"WEBFRONT_ERROR_GENERIC_TITLE": "Sorry!",
|
||||
"WEBFRONT_ERROR_TITLE": "Error!",
|
||||
"WEBFRONT_HOME_TITLE": "Server Overview",
|
||||
"WEBFRONT_NAV_CONSOLE": "Console",
|
||||
"WEBFRONT_NAV_DISCORD": "Discord",
|
||||
"WEBFRONT_NAV_HOME": "Home",
|
||||
"WEBFRONT_NAV_LOGOUT": "Logout",
|
||||
"WEBFRONT_NAV_PENALTIES": "Penalties",
|
||||
"WEBFRONT_NAV_PRIVILEGED": "Admins",
|
||||
"WEBFRONT_NAV_PROFILE": "Client Profile",
|
||||
"WEBFRONT_NAV_SEARCH": "Find Client",
|
||||
"WEBFRONT_NAV_SOCIAL": "Social",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_ADMIN": "Admin",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_AGO": "ago",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_NAME": "Name",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_OFFENSE": "Offense",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_REMAINING": "left",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOW": "Show",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOWONLY": "Show only",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TIME": "Time/Left",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TYPE": "Type",
|
||||
"WEBFRONT_PENALTY_TITLE": "Client Penalties",
|
||||
"WEBFRONT_PROFILE_FSEEN": "First seen",
|
||||
"WEBFRONT_PROFILE_LEVEL": "Level",
|
||||
"WEBFRONT_PROFILE_LSEEN": "Last seen",
|
||||
"WEBFRONT_PROFILE_PLAYER": "Played",
|
||||
"PLUGIN_STATS_SETUP_ENABLEAC": "Enable server-side anti-cheat (IW4 only)",
|
||||
"PLUGIN_STATS_ERROR_ADD": "Could not add server to server stats",
|
||||
"PLUGIN_STATS_CHEAT_DETECTED": "You appear to be cheating",
|
||||
"PLUGINS_STATS_TEXT_KDR": "KDR",
|
||||
"PLUGINS_STATS_META_SPM": "Score per Minute",
|
||||
"PLUGINS_WELCOME_USERANNOUNCE": "^5{{ClientName}} ^7hails from ^5{{ClientLocation}}",
|
||||
"PLUGINS_WELCOME_USERWELCOME": "Welcome ^5{{ClientName}}^7, this is your ^5{{TimesConnected}} ^7time connecting!",
|
||||
"PLUGINS_WELCOME_PRIVANNOUNCE": "{{ClientLevel}} {{ClientName}} has joined the server",
|
||||
"PLUGINS_LOGIN_AUTH": "not logged in",
|
||||
"PLUGINS_PROFANITY_SETUP_ENABLE": "Enable profanity deterring",
|
||||
"PLUGINS_PROFANITY_WARNMSG": "Please do not use profanity on this server",
|
||||
"PLUGINS_PROFANITY_KICKMSG": "Excessive use of profanity",
|
||||
"GLOBAL_DEBUG": "Debug",
|
||||
"COMMANDS_UNFLAG_DESC": "Remove flag for client",
|
||||
"COMMANDS_UNFLAG_FAIL": "You cannot unflag",
|
||||
"COMMANDS_UNFLAG_NOTFLAGGED": "Client is not flagged",
|
||||
"COMMANDS_FLAG_ALREADYFLAGGED": "Client is already flagged",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT": "Most Played",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC": "view the top 5 dedicated players on the server",
|
||||
"WEBFRONT_PROFILE_MESSAGES": "Messages",
|
||||
"WEBFRONT_CLIENT_META_CONNECTIONS": "Connections",
|
||||
"PLUGINS_STATS_COMMANDS_TOPSTATS_RATING": "Rating",
|
||||
"PLUGINS_STATS_COMMANDS_PERFORMANCE": "Performance"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
{
|
||||
"LocalizationName": "es-EC",
|
||||
"LocalizationIndex": {
|
||||
"Set": {
|
||||
"BROADCAST_OFFLINE": "^5IW4MAdmin ^7está ^1DESCONECTANDOSE",
|
||||
"BROADCAST_ONLINE": "^5IW4MADMIN ^7está ahora ^2en línea",
|
||||
"COMMAND_HELP_OPTIONAL": "opcional",
|
||||
"COMMAND_HELP_SYNTAX": "sintaxis:",
|
||||
"COMMAND_MISSINGARGS": "No se han proporcionado suficientes argumentos",
|
||||
"COMMAND_NOACCESS": "Tú no tienes acceso a ese comando",
|
||||
"COMMAND_NOTAUTHORIZED": "Tú no estás autorizado para ejecutar ese comando",
|
||||
"COMMAND_TARGET_MULTI": "Múltiples jugadores coinciden con ese nombre",
|
||||
"COMMAND_TARGET_NOTFOUND": "No se puede encontrar el jugador especificado",
|
||||
"COMMAND_UNKNOWN": "Has ingresado un comando desconocido",
|
||||
"COMMANDS_ADMINS_DESC": "enlistar clientes privilegiados actualmente conectados",
|
||||
"COMMANDS_ADMINS_NONE": "No hay administradores visibles en línea",
|
||||
"COMMANDS_ALIAS_ALIASES": "Aliases",
|
||||
"COMMANDS_ALIAS_DESC": "obtener alias e ips anteriores de un cliente",
|
||||
"COMMANDS_ALIAS_IPS": "IPs",
|
||||
"COMMANDS_ARGS_CLEAR": "borrar",
|
||||
"COMMANDS_ARGS_CLIENTID": "id del cliente",
|
||||
"COMMANDS_ARGS_COMMANDS": "comandos",
|
||||
"COMMANDS_ARGS_DURATION": "duración (m|h|d|w|y)",
|
||||
"COMMANDS_ARGS_INACTIVE": "días inactivo",
|
||||
"COMMANDS_ARGS_LEVEL": "nivel",
|
||||
"COMMANDS_ARGS_MAP": "mapa",
|
||||
"COMMANDS_ARGS_MESSAGE": "mensaje",
|
||||
"COMMANDS_ARGS_PASSWORD": "contraseña",
|
||||
"COMMANDS_ARGS_PLAYER": "jugador",
|
||||
"COMMANDS_ARGS_REASON": "razón",
|
||||
"COMMANDS_BAN_DESC": "banear permanentemente un cliente del servidor",
|
||||
"COMMANDS_BAN_FAIL": "Tú no puedes banear",
|
||||
"COMMANDS_BAN_SUCCESS": "ha sido baneado permanentemente",
|
||||
"COMMANDS_BANINFO_DESC": "obtener información sobre el ban de un cliente",
|
||||
"COMMANDS_BANINFO_NONE": "No se encontró ban activo para ese jugador",
|
||||
"COMMANDS_BANINO_SUCCESS": "fue baneado por ^5{0} ^7debido a:",
|
||||
"COMMANDS_FASTRESTART_DESC": "dar reinicio rápido al mapa actial",
|
||||
"COMMANDS_FASTRESTART_MASKED": "Al mapa se le ha dado un reinicio rápido",
|
||||
"COMMANDS_FASTRESTART_UNMASKED": "ha dado rápido reinicio al mapa",
|
||||
"COMMANDS_FIND_DESC": "encontrar cliente en la base de datos",
|
||||
"COMMANDS_FIND_EMPTY": "No se encontraron jugadores",
|
||||
"COMMANDS_FIND_MIN": "Por Favor introduzca al menos 3 caracteres",
|
||||
"COMMANDS_FLAG_DESC": "marcar un cliente sospechoso y anunciar a los administradores al unirse",
|
||||
"COMMANDS_FLAG_FAIL": "Tú no puedes marcar",
|
||||
"COMMANDS_FLAG_SUCCESS": "Has marcado a",
|
||||
"COMMANDS_FLAG_UNFLAG": "Has desmarcado a",
|
||||
"COMMANDS_HELP_DESC": "enlistar todos los comandos disponibles",
|
||||
"COMMANDS_HELP_MOREINFO": "Escribe !help <nombre del comando> para obtener la sintaxis de uso del comando",
|
||||
"COMMANDS_HELP_NOTFOUND": "No se ha podido encontrar ese comando",
|
||||
"COMMANDS_IP_DESC": "ver tu dirección IP externa",
|
||||
"COMMANDS_IP_SUCCESS": "Tu IP externa es",
|
||||
"COMMANDS_KICK_DESC": "expulsar a un cliente por su nombre",
|
||||
"COMMANDS_KICK_FAIL": "No tienes los privilegios necesarios para expulsar a",
|
||||
"COMMANDS_KICK_SUCCESS": "ha sido expulsado",
|
||||
"COMMANDS_LIST_DESC": "enlistar clientes activos",
|
||||
"COMMANDS_MAP_DESC": "cambiar al mapa especificado",
|
||||
"COMMANDS_MAP_SUCCESS": "Cambiando al mapa",
|
||||
"COMMANDS_MAP_UKN": "Intentando cambiar a un mapa desconocido",
|
||||
"COMMANDS_MAPROTATE": "Rotación de mapa en ^55 ^7segundos",
|
||||
"COMMANDS_MAPROTATE_DESC": "pasar al siguiente mapa en rotación",
|
||||
"COMMANDS_MASK_DESC": "esconde tu presencia como un cliente privilegiado",
|
||||
"COMMANDS_MASK_OFF": "Ahora estás desenmascarado",
|
||||
"COMMANDS_MASK_ON": "Ahora estás enmascarado",
|
||||
"COMMANDS_OWNER_DESC": "reclamar la propiedad del servidor",
|
||||
"COMMANDS_OWNER_FAIL": "Este servidor ya tiene un propietario",
|
||||
"COMMANDS_OWNER_SUCCESS": "¡Felicidades, has reclamado la propiedad de este servidor!",
|
||||
"COMMANDS_PASSWORD_FAIL": "Tu contraseña debe tener al menos 5 caracteres de largo",
|
||||
"COMMANDS_PASSWORD_SUCCESS": "Su contraseña ha sido establecida con éxito",
|
||||
"COMMANDS_PING_DESC": "obtener ping del cliente",
|
||||
"COMMANDS_PING_SELF": "Tu ping es",
|
||||
"COMMANDS_PING_TARGET": "ping es",
|
||||
"COMMANDS_PLUGINS_DESC": "ver todos los complementos cargados",
|
||||
"COMMANDS_PLUGINS_LOADED": "Complementos cargados",
|
||||
"COMMANDS_PM_DESC": "enviar mensaje a otro cliente",
|
||||
"COMMANDS_PRUNE_DESC": "degradar a los clientes con privilegios que no se hayan conectado recientemente (el valor predeterminado es 30 días)",
|
||||
"COMMANDS_PRUNE_FAIL": "Número inválido de días inactivos",
|
||||
"COMMANDS_PRUNE_SUCCESS": "los usuarios privilegiados inactivos fueron podados",
|
||||
"COMMANDS_QUIT_DESC": "salir de IW4MAdmin",
|
||||
"COMMANDS_RCON_DESC": "enviar el comando rcon al servidor",
|
||||
"COMMANDS_RCON_SUCCESS": "Exitosamente enviado el comando RCon",
|
||||
"COMMANDS_REPORT_DESC": "reportar un cliente por comportamiento sospechoso",
|
||||
"COMMANDS_REPORT_FAIL": "Tú no puedes reportar",
|
||||
"COMMANDS_REPORT_FAIL_CAMP": "No puedes reportar a un jugador por campear",
|
||||
"COMMANDS_REPORT_FAIL_DUPLICATE": "Ya has reportado a este jugador",
|
||||
"COMMANDS_REPORT_FAIL_SELF": "No puedes reportarte a ti mismo",
|
||||
"COMMANDS_REPORT_SUCCESS": "Gracias por su reporte, un administrador ha sido notificado",
|
||||
"COMMANDS_REPORTS_CLEAR_SUCCESS": "Reportes borrados con éxito",
|
||||
"COMMANDS_REPORTS_DESC": "obtener o borrar informes recientes",
|
||||
"COMMANDS_REPORTS_NONE": "No hay jugadores reportados aun",
|
||||
"COMMANDS_RULES_DESC": "enlistar reglas del servidor",
|
||||
"COMMANDS_RULES_NONE": "El propietario del servidor no ha establecido ninguna regla",
|
||||
"COMMANDS_SAY_DESC": "transmitir el mensaje a todos los clientes",
|
||||
"COMMANDS_SETLEVEL_DESC": "establecer el cliente al nivel de privilegio especificado",
|
||||
"COMMANDS_SETLEVEL_FAIL": "Grupo inválido especificado",
|
||||
"COMMANDS_SETLEVEL_LEVELTOOHIGH": "Tú solo puedes promover ^5{0} ^7a ^5{1} ^7o menor privilegio",
|
||||
"COMMANDS_SETLEVEL_OWNER": "Solo puede haber un propietario. Modifica tu configuración si múltiples propietarios son requeridos",
|
||||
"COMMANDS_SETLEVEL_SELF": "No puedes cambiar tu propio nivel",
|
||||
"COMMANDS_SETLEVEL_STEPPEDDISABLED": "Este servidor no te permite promover",
|
||||
"COMMANDS_SETLEVEL_SUCCESS": "fue promovido con éxito",
|
||||
"COMMANDS_SETLEVEL_SUCCESS_TARGET": "¡Felicitaciones! has ha sido promovido a",
|
||||
"COMMANDS_SETPASSWORD_DESC": "configura tu contraseña de autenticación",
|
||||
"COMMANDS_TEMPBAN_DESC": "banear temporalmente a un cliente por el tiempo especificado (predeterminado en 1 hora)",
|
||||
"COMMANDS_TEMPBAN_FAIL": "Tú no puedes banear temporalmente",
|
||||
"COMMANDS_TEMPBAN_SUCCESS": "ha sido baneado temporalmente por",
|
||||
"COMMANDS_UNBAN_DESC": "desbanear al cliente por ID",
|
||||
"COMMANDS_UNBAN_FAIL": "no está baneado",
|
||||
"COMMANDS_UNBAN_SUCCESS": "Exitosamente desbaneado",
|
||||
"COMMANDS_UPTIME_DESC": "obtener el tiempo de ejecución de la aplicación actual",
|
||||
"COMMANDS_UPTIME_TEXT": "ha estado en línea por",
|
||||
"COMMANDS_USAGE_DESC": "obtener uso de la memoria de la aplicación",
|
||||
"COMMANDS_USAGE_TEXT": "está usando",
|
||||
"COMMANDS_WARN_DESC": "advertir al cliente por infringir las reglas",
|
||||
"COMMANDS_WARN_FAIL": "No tiene los privilegios necesarios para advertir a",
|
||||
"COMMANDS_WARNCLEAR_DESC": "eliminar todas las advertencias de un cliente",
|
||||
"COMMANDS_WARNCLEAR_SUCCESS": "Todas las advertencias borradas para",
|
||||
"COMMANDS_WHO_DESC": "da información sobre ti",
|
||||
"GLOBAL_TIME_DAYS": "días",
|
||||
"GLOBAL_ERROR": "Error",
|
||||
"GLOBAL_TIME_HOURS": "horas",
|
||||
"GLOBAL_INFO": "Información",
|
||||
"GLOBAL_TIME_MINUTES": "minutos",
|
||||
"GLOBAL_REPORT": "Si sospechas que alguien ^5usa cheats ^7usa el comando ^5!report",
|
||||
"GLOBAL_VERBOSE": "Detallado",
|
||||
"GLOBAL_WARNING": "Advertencia",
|
||||
"MANAGER_CONNECTION_REST": "La conexión ha sido restablecida con",
|
||||
"MANAGER_CONSOLE_NOSERV": "No hay servidores que estén siendo monitoreados en este momento",
|
||||
"MANAGER_EXIT": "Presione cualquier tecla para salir...",
|
||||
"MANAGER_INIT_FAIL": "Error fatal durante la inicialización",
|
||||
"MANAGER_MONITORING_TEXT": "Ahora monitoreando",
|
||||
"MANAGER_SHUTDOWN_SUCCESS": "Apagado completo",
|
||||
"MANAGER_VERSION_CURRENT": "Tu versión es",
|
||||
"MANAGER_VERSION_FAIL": "No se ha podido conseguir la última versión de IW4MAdmin",
|
||||
"MANAGER_VERSION_SUCCESS": "IW4MAdmin está actualizado",
|
||||
"MANAGER_VERSION_UPDATE": "tiene una actualización. La última versión es",
|
||||
"PLUGIN_IMPORTER_NOTFOUND": "No se encontraron complementos para cargar",
|
||||
"PLUGIN_IMPORTER_REGISTERCMD": "Comando registrado",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_DESC": "iniciar sesión usando la contraseña",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL": "tu contraseña es incorrecta",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS": "Ahora está conectado",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_DESC": "restablece tus estadísticas a las nuevas de fábrica",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_FAIL": "Debes estar conectado a un servidor para restablecer tus estadísticas",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_SUCCESS": "Tus estadísticas para este servidor se han restablecido",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_DESC": "ver los 5 mejores jugadores en este servidor",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_TEXT": "Mejores Jugadores",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_DESC": "ver tus estadísticas",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL": "No se puede encontrar el jugador que especificó",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME": "El jugador especificado debe estar dentro del juego",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME_SELF": "Debes estar dentro del juego para ver tus estadísticas",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_SUCCESS": "Estadísticas para",
|
||||
"PLUGINS_STATS_TEXT_DEATHS": "Muertes",
|
||||
"PLUGINS_STATS_TEXT_KILLS": "Asesinatos",
|
||||
"PLUGINS_STATS_TEXT_NOQUALIFY": "No hay jugadores que califiquen para los primeros lugares aun",
|
||||
"PLUGINS_STATS_TEXT_SKILL": "Habilidad",
|
||||
"SERVER_BAN_APPEAL": "apela en",
|
||||
"SERVER_BAN_PREV": "Baneado anteriormente por",
|
||||
"SERVER_BAN_TEXT": "Estás baneado",
|
||||
"SERVER_ERROR_ADDPLAYER": "Incapaz de añadir al jugador",
|
||||
"SERVER_ERROR_COMMAND_INGAME": "Un error interno ocurrió mientras se procesaba tu comando",
|
||||
"SERVER_ERROR_COMMAND_LOG": "Comando generó error",
|
||||
"SERVER_ERROR_COMMUNICATION": "No se ha podido comunicar con",
|
||||
"SERVER_ERROR_DNE": "No existe",
|
||||
"SERVER_ERROR_DVAR": "No se pudo obtener el valor dvar",
|
||||
"SERVER_ERROR_DVAR_HELP": "asegúrate de que el servidor tenga un mapa cargado",
|
||||
"SERVER_ERROR_EXCEPTION": "Excepción inesperada en",
|
||||
"SERVER_ERROR_LOG": "Archivo de registro del juego invalido",
|
||||
"SERVER_ERROR_PLUGIN": "Un error ocurrió mientras se cargaba el complemente",
|
||||
"SERVER_ERROR_POLLING": "reduciendo la tasa de sondeo",
|
||||
"SERVER_ERROR_UNFIXABLE": "No se está supervisando el servidor debido a errores incorregibles",
|
||||
"SERVER_KICK_CONTROLCHARS": "Tu nombre no puede contener caracteres de control",
|
||||
"SERVER_KICK_GENERICNAME": "Por favor cambia tu nombre usando /name",
|
||||
"SERVER_KICK_MINNAME": "Tu nombre debe contener al menos 3 caracteres",
|
||||
"SERVER_KICK_NAME_INUSE": "Tu nombre está siendo usado por alguien más",
|
||||
"SERVER_KICK_TEXT": "Fuiste expulsado",
|
||||
"SERVER_KICK_VPNS_NOTALLOWED": "Las VPNs no están permitidas en este servidor",
|
||||
"SERVER_PLUGIN_ERROR": "Un complemento generó un error",
|
||||
"SERVER_REPORT_COUNT": "Hay ^5{0} ^7reportes recientes",
|
||||
"SERVER_TB_REMAIN": "Tú estás temporalmente baneado",
|
||||
"SERVER_TB_TEXT": "Estás temporalmente baneado",
|
||||
"SERVER_WARNING": "ADVERTENCIA",
|
||||
"SERVER_WARNLIMT_REACHED": "Muchas advertencias",
|
||||
"SERVER_WEBSITE_GENERIC": "el sitio web de este servidor",
|
||||
"SETUP_DISPLAY_SOCIAL": "Mostrar el link del medio de comunicación en la parte frontal de la web. (discord, website, VK, etc..)",
|
||||
"SETUP_ENABLE_CUSTOMSAY": "Habilitar nombre a decir personalizado",
|
||||
"SETUP_ENABLE_MULTIOWN": "Habilitar múltiples propietarios",
|
||||
"SETUP_ENABLE_STEPPEDPRIV": "Habilitar jerarquía de privilegios por escalones",
|
||||
"SETUP_ENABLE_VPNS": "Habilitar VPNs clientes",
|
||||
"SETUP_ENABLE_WEBFRONT": "Habilitar frente de la web",
|
||||
"SETUP_ENCODING_STRING": "Ingresar cadena de codificación",
|
||||
"SETUP_IPHUB_KEY": "Ingresar clave api de iphub.info",
|
||||
"SETUP_SAY_NAME": "Ingresar nombre a decir personalizado",
|
||||
"SETUP_SERVER_IP": "Ingresar Dirección IP del servidor",
|
||||
"SETUP_SERVER_MANUALLOG": "Ingresar manualmente la ruta del archivo de registro",
|
||||
"SETUP_SERVER_PORT": "Ingresar puerto del servidor",
|
||||
"SETUP_SERVER_RCON": "Ingresar contraseña RCon del servidor",
|
||||
"SETUP_SERVER_SAVE": "Configuración guardada, añadir otra",
|
||||
"SETUP_SERVER_USEIW5M": "Usar analizador Pluto IW5",
|
||||
"SETUP_SERVER_USET6M": "Usar analizador Pluto T6",
|
||||
"SETUP_SOCIAL_LINK": "Ingresar link del medio de comunicación",
|
||||
"SETUP_SOCIAL_TITLE": "Ingresa el nombre de la red de comunicación",
|
||||
"SETUP_USE_CUSTOMENCODING": "Usar analizador de codificación personalizado",
|
||||
"WEBFRONT_ACTION_BAN_NAME": "Ban",
|
||||
"WEBFRONT_ACTION_LABEL_ID": "ID del Cliente",
|
||||
"WEBFRONT_ACTION_LABEL_PASSWORD": "Contraseña",
|
||||
"WEBFRONT_ACTION_LABEL_REASON": "Razón",
|
||||
"WEBFRONT_ACTION_LOGIN_NAME": "Inicio de sesión",
|
||||
"WEBFRONT_ACTION_UNBAN_NAME": "Desban",
|
||||
"WEBFRONT_CLIENT_META_FALSE": "No está",
|
||||
"WEBFRONT_CLIENT_META_JOINED": "Se unió con el alias",
|
||||
"WEBFRONT_CLIENT_META_MASKED": "Enmascarado",
|
||||
"WEBFRONT_CLIENT_META_TRUE": "Está",
|
||||
"WEBFRONT_CLIENT_PRIVILEGED_TITLE": "Clientes privilegiados",
|
||||
"WEBFRONT_CLIENT_PROFILE_TITLE": "Perfil",
|
||||
"WEBFRONT_CLIENT_SEARCH_MATCHING": "Clientes que concuerdan",
|
||||
"WEBFRONT_CONSOLE_EXECUTE": "Ejecutar",
|
||||
"WEBFRONT_CONSOLE_TITLE": "Consola Web",
|
||||
"WEBFRONT_ERROR_DESC": "IW4MAdmin encontró un error",
|
||||
"WEBFRONT_ERROR_GENERIC_DESC": "Un error ha ocurrido mientras se procesaba tu solicitud",
|
||||
"WEBFRONT_ERROR_GENERIC_TITLE": "¡Lo lamento!",
|
||||
"WEBFRONT_ERROR_TITLE": "¡Error!",
|
||||
"WEBFRONT_HOME_TITLE": "Vista general del servidor",
|
||||
"WEBFRONT_NAV_CONSOLE": "Consola",
|
||||
"WEBFRONT_NAV_DISCORD": "Discord",
|
||||
"WEBFRONT_NAV_HOME": "Inicio",
|
||||
"WEBFRONT_NAV_LOGOUT": "Cerrar sesión",
|
||||
"WEBFRONT_NAV_PENALTIES": "Sanciones",
|
||||
"WEBFRONT_NAV_PRIVILEGED": "Administradores",
|
||||
"WEBFRONT_NAV_PROFILE": "Perfil del cliente",
|
||||
"WEBFRONT_NAV_SEARCH": "Encontrar cliente",
|
||||
"WEBFRONT_NAV_SOCIAL": "Social",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_ADMIN": "Administrador",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_AGO": "atrás",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_NAME": "Nombre",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_OFFENSE": "Ofensa",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_REMAINING": "restante",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOW": "Mostrar",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOWONLY": "Mostrar solamente",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TIME": "Tiempo/Restante",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TYPE": "Tipo",
|
||||
"WEBFRONT_PENALTY_TITLE": "Faltas del cliente",
|
||||
"WEBFRONT_PROFILE_FSEEN": "Primera vez visto hace",
|
||||
"WEBFRONT_PROFILE_LEVEL": "Nivel",
|
||||
"WEBFRONT_PROFILE_LSEEN": "Última vez visto hace",
|
||||
"WEBFRONT_PROFILE_PLAYER": "Jugadas",
|
||||
"PLUGIN_STATS_SETUP_ENABLEAC": "Habilitar anti-trampas junto al servidor (solo IW4)",
|
||||
"PLUGIN_STATS_ERROR_ADD": "No se puedo añadir servidor a los estados del servidor",
|
||||
"PLUGIN_STATS_CHEAT_DETECTED": "Pareces estar haciendo trampa",
|
||||
"PLUGINS_STATS_TEXT_KDR": "KDR",
|
||||
"PLUGINS_STATS_META_SPM": "Puntaje por minuto",
|
||||
"PLUGINS_WELCOME_USERANNOUNCE": "^5{{ClientName}} ^7llega desde ^5{{ClientLocation}}",
|
||||
"PLUGINS_WELCOME_USERWELCOME": "¡Bienvenido ^5{{ClientName}}^7, esta es tu visita numero ^5{{TimesConnected}} ^7 en el servidor!",
|
||||
"PLUGINS_WELCOME_PRIVANNOUNCE": "{{ClientLevel}} {{ClientName}} Se ha unido al servidor",
|
||||
"PLUGINS_LOGIN_AUTH": "No registrado",
|
||||
"PLUGINS_PROFANITY_SETUP_ENABLE": "Habilitar la disuasión de blasfemias",
|
||||
"PLUGINS_PROFANITY_WARNMSG": "Por favor no uses blasfemias en este servidor",
|
||||
"PLUGINS_PROFANITY_KICKMSG": "Excesivo uso de blasfemias",
|
||||
"GLOBAL_DEBUG": "Depurar",
|
||||
"COMMANDS_UNFLAG_DESC": "Remover marca del cliente",
|
||||
"COMMANDS_UNFLAG_FAIL": "Tu no puedes desmarcar",
|
||||
"COMMANDS_UNFLAG_NOTFLAGGED": "El cliente no está marcado",
|
||||
"COMMANDS_FLAG_ALREADYFLAGGED": "El cliente yá se encuentra marcado",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT": "Más jugado",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC": "ver el Top 5 de jugadores dedicados en el servidor",
|
||||
"WEBFRONT_PROFILE_MESSAGES": "Mensajes",
|
||||
"WEBFRONT_CLIENT_META_CONNECTIONS": "Conexiones",
|
||||
"PLUGINS_STATS_COMMANDS_TOPSTATS_RATING": "Clasificación",
|
||||
"PLUGINS_STATS_COMMANDS_PERFORMANCE": "Desempeño"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
{
|
||||
"LocalizationName": "pt-BR",
|
||||
"LocalizationIndex": {
|
||||
"Set": {
|
||||
"BROADCAST_OFFLINE": "IW4MAdmin ficou offline",
|
||||
"BROADCAST_ONLINE": "^5IW4MADMIN ^7agora está ^2ONLINE",
|
||||
"COMMAND_HELP_OPTIONAL": "opcional",
|
||||
"COMMAND_HELP_SYNTAX": "sintaxe:",
|
||||
"COMMAND_MISSINGARGS": "Não foram oferecidos argumentos suficientes",
|
||||
"COMMAND_NOACCESS": "Você não tem acesso a este comando",
|
||||
"COMMAND_NOTAUTHORIZED": "Você não está autorizado a executar este comando",
|
||||
"COMMAND_TARGET_MULTI": "Vários jogadores correspondem a esse nome",
|
||||
"COMMAND_TARGET_NOTFOUND": "Não é possível encontrar o jogador especificado",
|
||||
"COMMAND_UNKNOWN": "Você digitou um comando desconhecido",
|
||||
"COMMANDS_ADMINS_DESC": "lista os clientes privilegiados conectados no momento",
|
||||
"COMMANDS_ADMINS_NONE": "Não há administradores visíveis online",
|
||||
"COMMANDS_ALIAS_ALIASES": "Nomes registrados",
|
||||
"COMMANDS_ALIAS_DESC": "obtém a lista de histórico de nomes que o jogador usou no servidor",
|
||||
"COMMANDS_ALIAS_IPS": "IPs",
|
||||
"COMMANDS_ARGS_CLEAR": "apagar",
|
||||
"COMMANDS_ARGS_CLIENTID": "id do jogador",
|
||||
"COMMANDS_ARGS_COMMANDS": "comandos",
|
||||
"COMMANDS_ARGS_DURATION": "duração (m|h|d|w|y)",
|
||||
"COMMANDS_ARGS_INACTIVE": "dias inativos",
|
||||
"COMMANDS_ARGS_LEVEL": "nível",
|
||||
"COMMANDS_ARGS_MAP": "mapa",
|
||||
"COMMANDS_ARGS_MESSAGE": "mensagem",
|
||||
"COMMANDS_ARGS_PASSWORD": "senha",
|
||||
"COMMANDS_ARGS_PLAYER": "jogador",
|
||||
"COMMANDS_ARGS_REASON": "razão",
|
||||
"COMMANDS_BAN_DESC": "banir permanentemente um cliente do servidor",
|
||||
"COMMANDS_BAN_FAIL": "Você não pode banir permanentemente",
|
||||
"COMMANDS_BAN_SUCCESS": "foi banido permanentemente",
|
||||
"COMMANDS_BANINFO_DESC": "obtém informações sobre um banimento para um jogador",
|
||||
"COMMANDS_BANINFO_NONE": "Nenhum banimento ativo foi encontrado para esse jogador",
|
||||
"COMMANDS_BANINO_SUCCESS": "foi banido por ^5{0} ^7por:",
|
||||
"COMMANDS_FASTRESTART_DESC": "reinicializa rapidamente o mapa atual, não recomendável o uso várias vezes seguidas",
|
||||
"COMMANDS_FASTRESTART_MASKED": "O mapa foi reiniciado rapidamente",
|
||||
"COMMANDS_FASTRESTART_UNMASKED": "reiniciou rapidamente o mapa",
|
||||
"COMMANDS_FIND_DESC": "acha o jogador na base de dados",
|
||||
"COMMANDS_FIND_EMPTY": "Nenhum jogador foi encontrado",
|
||||
"COMMANDS_FIND_MIN": "Por favor, insira pelo menos 3 caracteres",
|
||||
"COMMANDS_FLAG_DESC": "sinaliza um cliente suspeito e anuncia aos administradores ao entrar no servidor",
|
||||
"COMMANDS_FLAG_FAIL": "Você não pode sinalizar",
|
||||
"COMMANDS_FLAG_SUCCESS": "Você sinalizou",
|
||||
"COMMANDS_FLAG_UNFLAG": "Você tirou a sinalização de",
|
||||
"COMMANDS_HELP_DESC": "lista todos os comandos disponíveis",
|
||||
"COMMANDS_HELP_MOREINFO": "Digite !help <comando> para saber como usar o comando",
|
||||
"COMMANDS_HELP_NOTFOUND": "Não foi possível encontrar esse comando",
|
||||
"COMMANDS_IP_DESC": "mostrar o seu endereço IP externo",
|
||||
"COMMANDS_IP_SUCCESS": "Seu endereço IP externo é",
|
||||
"COMMANDS_KICK_DESC": "expulsa o jogador pelo nome",
|
||||
"COMMANDS_KICK_FAIL": "Você não tem os privilégios necessários para expulsar",
|
||||
"COMMANDS_KICK_SUCCESS": "foi expulso",
|
||||
"COMMANDS_LIST_DESC": "lista os jogadores ativos na partida",
|
||||
"COMMANDS_MAP_DESC": "muda para o mapa especificado",
|
||||
"COMMANDS_MAP_SUCCESS": "Mudando o mapa para",
|
||||
"COMMANDS_MAP_UKN": "Tentando mudar para o mapa desconhecido",
|
||||
"COMMANDS_MAPROTATE": "Rotacionando o mapa em ^55 ^7segundos",
|
||||
"COMMANDS_MAPROTATE_DESC": "avança para o próximo mapa da rotação",
|
||||
"COMMANDS_MASK_DESC": "esconde a sua presença como um jogador privilegiado",
|
||||
"COMMANDS_MASK_OFF": "Você foi desmascarado",
|
||||
"COMMANDS_MASK_ON": "Você agora está mascarado",
|
||||
"COMMANDS_OWNER_DESC": "reivindica a propriedade do servidor",
|
||||
"COMMANDS_OWNER_FAIL": "Este servidor já tem um dono",
|
||||
"COMMANDS_OWNER_SUCCESS": "Parabéns, você reivindicou a propriedade deste servidor!",
|
||||
"COMMANDS_PASSWORD_FAIL": "Sua senha deve ter pelo menos 5 caracteres",
|
||||
"COMMANDS_PASSWORD_SUCCESS": "Sua senha foi configurada com sucesso",
|
||||
"COMMANDS_PING_DESC": "mostra o quanto de latência tem o jogador",
|
||||
"COMMANDS_PING_SELF": "Sua latência é",
|
||||
"COMMANDS_PING_TARGET": "latência é",
|
||||
"COMMANDS_PLUGINS_DESC": "mostra todos os plugins que estão carregados",
|
||||
"COMMANDS_PLUGINS_LOADED": "Plugins carregados",
|
||||
"COMMANDS_PM_DESC": "envia a mensagem para o outro jogador de maneira privada, use /!pm para ter efeito, se possível",
|
||||
"COMMANDS_PRUNE_DESC": "rebaixa qualquer jogador privilegiado que não tenha se conectado recentemente (o padrão é 30 dias)",
|
||||
"COMMANDS_PRUNE_FAIL": "Número inválido de dias ativo",
|
||||
"COMMANDS_PRUNE_SUCCESS": "usuários privilegiados inativos foram removidos",
|
||||
"COMMANDS_QUIT_DESC": "sair do IW4MAdmin",
|
||||
"COMMANDS_RCON_DESC": "envia o comando Rcon para o servidor",
|
||||
"COMMANDS_RCON_SUCCESS": "O comando para o RCon foi enviado com sucesso!",
|
||||
"COMMANDS_REPORT_DESC": "denuncia o jogador por comportamento suspeito",
|
||||
"COMMANDS_REPORT_FAIL": "Você não pode reportar",
|
||||
"COMMANDS_REPORT_FAIL_CAMP": "Você não pode denunciar o jogador por camperar",
|
||||
"COMMANDS_REPORT_FAIL_DUPLICATE": "Você já denunciou o jogador",
|
||||
"COMMANDS_REPORT_FAIL_SELF": "Você não pode reportar a si mesmo",
|
||||
"COMMANDS_REPORT_SUCCESS": "Obrigado pela sua denúncia, um administrador foi notificado",
|
||||
"COMMANDS_REPORTS_CLEAR_SUCCESS": "Lista de denúncias limpa com sucesso",
|
||||
"COMMANDS_REPORTS_DESC": "obtém ou limpa as denúncias recentes",
|
||||
"COMMANDS_REPORTS_NONE": "Ninguém foi denunciado ainda",
|
||||
"COMMANDS_RULES_DESC": "lista as regras do servidor",
|
||||
"COMMANDS_RULES_NONE": "O proprietário do servidor não definiu nenhuma regra, sinta-se livre",
|
||||
"COMMANDS_SAY_DESC": "transmite mensagem para todos os jogadores",
|
||||
"COMMANDS_SETLEVEL_DESC": "define o jogador para o nível de privilégio especificado",
|
||||
"COMMANDS_SETLEVEL_FAIL": "grupo especificado inválido",
|
||||
"COMMANDS_SETLEVEL_LEVELTOOHIGH": "Você só pode promover do ^5{0} ^7para ^5{1} ^7ou um nível menor",
|
||||
"COMMANDS_SETLEVEL_OWNER": "Só pode haver 1 dono. Modifique suas configurações se vários proprietários forem necessários",
|
||||
"COMMANDS_SETLEVEL_SELF": "Você não pode mudar seu próprio nível",
|
||||
"COMMANDS_SETLEVEL_STEPPEDDISABLED": "Este servidor não permite que você promova",
|
||||
"COMMANDS_SETLEVEL_SUCCESS": "foi promovido com sucesso",
|
||||
"COMMANDS_SETLEVEL_SUCCESS_TARGET": "Parabéns! Você foi promovido para",
|
||||
"COMMANDS_SETPASSWORD_DESC": "define sua senha de autenticação",
|
||||
"COMMANDS_TEMPBAN_DESC": "bane temporariamente um jogador por tempo especificado (o padrão é 1 hora)",
|
||||
"COMMANDS_TEMPBAN_FAIL": "Você não pode banir temporariamente",
|
||||
"COMMANDS_TEMPBAN_SUCCESS": "foi banido temporariamente por",
|
||||
"COMMANDS_UNBAN_DESC": "retira o banimento de um jogador pelo seu ID",
|
||||
"COMMANDS_UNBAN_FAIL": "não está banido",
|
||||
"COMMANDS_UNBAN_SUCCESS": "Foi retirado o banimento com sucesso",
|
||||
"COMMANDS_UPTIME_DESC": "obtém o tempo de execução do aplicativo a quando aberto",
|
||||
"COMMANDS_UPTIME_TEXT": "está online por",
|
||||
"COMMANDS_USAGE_DESC": "vê quanto o aplicativo está usando de memória RAM do seu computador",
|
||||
"COMMANDS_USAGE_TEXT": "está usando",
|
||||
"COMMANDS_WARN_DESC": "adverte o cliente por infringir as regras",
|
||||
"COMMANDS_WARN_FAIL": "Você não tem os privilégios necessários para advertir",
|
||||
"COMMANDS_WARNCLEAR_DESC": "remove todos os avisos para um cliente",
|
||||
"COMMANDS_WARNCLEAR_SUCCESS": "Todos as advertências foram apagados para",
|
||||
"COMMANDS_WHO_DESC": "dá informações sobre você",
|
||||
"GLOBAL_TIME_DAYS": "dias",
|
||||
"GLOBAL_ERROR": "Erro",
|
||||
"GLOBAL_TIME_HOURS": "horas",
|
||||
"GLOBAL_INFO": "Informação",
|
||||
"GLOBAL_TIME_MINUTES": "minutos",
|
||||
"GLOBAL_REPORT": "Se você está suspeitando alguém de alguma ^5TRAPAÇA ^7use o comando ^5!report",
|
||||
"GLOBAL_VERBOSE": "Detalhe",
|
||||
"GLOBAL_WARNING": "AVISO",
|
||||
"MANAGER_CONNECTION_REST": "A conexão foi reestabelecida com",
|
||||
"MANAGER_CONSOLE_NOSERV": "Não há servidores sendo monitorados neste momento",
|
||||
"MANAGER_EXIT": "Pressione qualquer tecla para sair...",
|
||||
"MANAGER_INIT_FAIL": "Erro fatal durante a inicialização",
|
||||
"MANAGER_MONITORING_TEXT": "Agora monitorando",
|
||||
"MANAGER_SHUTDOWN_SUCCESS": "Desligamento concluído",
|
||||
"MANAGER_VERSION_CURRENT": "Está é a sua versão",
|
||||
"MANAGER_VERSION_FAIL": "Não foi possível obter a versão mais recente do IW4MAdmin",
|
||||
"MANAGER_VERSION_SUCCESS": "O IW4MAdmin está atualizado",
|
||||
"MANAGER_VERSION_UPDATE": "Há uma atualização disponível. A versão mais recente é",
|
||||
"PLUGIN_IMPORTER_NOTFOUND": "Não foram encontrados plugins para carregar",
|
||||
"PLUGIN_IMPORTER_REGISTERCMD": "Comando registrado",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_DESC": "Inicie a sua sessão usando a senha",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL": "Sua senha está errada",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS": "Você agora está conectado",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_DESC": "reinicia suas estatísticas para uma nova",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_FAIL": "Você deve estar conectado a um servidor para reiniciar as suas estatísticas",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_SUCCESS": "Suas estatísticas nesse servidor foram reiniciadas",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_DESC": "visualiza os 5 melhores jogadores do servidor",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_TEXT": "Top Jogadores",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_DESC": "mostra suas estatísticas",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL": "Não foi encontrado o jogador que você especificou",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME": "o jogador especificado deve estar dentro do jogo",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME_SELF": "Você deve estar no jogo para ver suas estatísticas",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_SUCCESS": "Estatísticas para",
|
||||
"PLUGINS_STATS_TEXT_DEATHS": "MORTES",
|
||||
"PLUGINS_STATS_TEXT_KILLS": "BAIXAS",
|
||||
"PLUGINS_STATS_TEXT_NOQUALIFY": "Não há ainda jogadores qualificados para os primeiros lugares",
|
||||
"PLUGINS_STATS_TEXT_SKILL": "HABILIDADE",
|
||||
"SERVER_BAN_APPEAL": "apele em",
|
||||
"SERVER_BAN_PREV": "Banido preventivamente por",
|
||||
"SERVER_BAN_TEXT": "Você está banido",
|
||||
"SERVER_ERROR_ADDPLAYER": "Não foi possível adicionar o jogador",
|
||||
"SERVER_ERROR_COMMAND_INGAME": "Ocorreu um erro interno ao processar seu comando",
|
||||
"SERVER_ERROR_COMMAND_LOG": "o comando gerou um erro",
|
||||
"SERVER_ERROR_COMMUNICATION": "Não foi possível fazer a comunicação com",
|
||||
"SERVER_ERROR_DNE": "não existe",
|
||||
"SERVER_ERROR_DVAR": "Não foi possível obter o valor de dvar para",
|
||||
"SERVER_ERROR_DVAR_HELP": "garanta que o servidor tenha um mapa carregado",
|
||||
"SERVER_ERROR_EXCEPTION": "Exceção inesperada em",
|
||||
"SERVER_ERROR_LOG": "Log do jogo inválido",
|
||||
"SERVER_ERROR_PLUGIN": "Ocorreu um erro ao carregar o plug-in",
|
||||
"SERVER_ERROR_POLLING": "reduzir a taxa de sondagem do server",
|
||||
"SERVER_ERROR_UNFIXABLE": "Não monitorando o servidor devido a erros incorrigíveis",
|
||||
"SERVER_KICK_CONTROLCHARS": "Seu nome não pode conter caracteres de controle",
|
||||
"SERVER_KICK_GENERICNAME": "Por favor, mude o seu nome usando o comando /name no console",
|
||||
"SERVER_KICK_MINNAME": "Seu nome deve conter no mínimo três caracteres",
|
||||
"SERVER_KICK_NAME_INUSE": "Seu nome já está sendo usado por outra pessoa",
|
||||
"SERVER_KICK_TEXT": "Você foi expulso",
|
||||
"SERVER_KICK_VPNS_NOTALLOWED": "VPNs não são permitidas neste servidor",
|
||||
"SERVER_PLUGIN_ERROR": "Um plugin gerou erro",
|
||||
"SERVER_REPORT_COUNT": "Você tem ^5{0} ^7denúncias recentes",
|
||||
"SERVER_TB_REMAIN": "Você está banido temporariamente",
|
||||
"SERVER_TB_TEXT": "Você está banido temporariamente",
|
||||
"SERVER_WARNING": "AVISO",
|
||||
"SERVER_WARNLIMT_REACHED": "Avisos demais! Leia o chat da próxima vez",
|
||||
"SERVER_WEBSITE_GENERIC": "este é o site do servidor",
|
||||
"SETUP_DISPLAY_SOCIAL": "Digitar link do convite do seu site no módulo da web (Discord, YouTube, etc.)",
|
||||
"SETUP_ENABLE_CUSTOMSAY": "Habilitar a customização do nome do comando say",
|
||||
"SETUP_ENABLE_MULTIOWN": "Habilitar vários proprietários",
|
||||
"SETUP_ENABLE_STEPPEDPRIV": "Ativar hierarquia de privilégios escalonada",
|
||||
"SETUP_ENABLE_VPNS": "Habilitar que os usuários usem VPN",
|
||||
"SETUP_ENABLE_WEBFRONT": "Habilitar o módulo da web do IW4MAdmin",
|
||||
"SETUP_ENCODING_STRING": "Digite sequência de codificação",
|
||||
"SETUP_IPHUB_KEY": "Digite iphub.info api key",
|
||||
"SETUP_SAY_NAME": "Habilitar a customização do nome do comando say",
|
||||
"SETUP_SERVER_IP": "Digite o endereço IP do servidor",
|
||||
"SETUP_SERVER_MANUALLOG": "Insira o caminho do arquivo de log manualmente",
|
||||
"SETUP_SERVER_PORT": "Digite a porta do servidor",
|
||||
"SETUP_SERVER_RCON": "Digite a senha do RCon do servidor",
|
||||
"SETUP_SERVER_SAVE": "Configuração salva, adicionar outra",
|
||||
"SETUP_SERVER_USEIW5M": "Usar analisador Pluto IW5 ",
|
||||
"SETUP_SERVER_USET6M": "Usar analisador Pluto T6 ",
|
||||
"SETUP_SOCIAL_LINK": "Digite o link da Rede Social",
|
||||
"SETUP_SOCIAL_TITLE": "Digite o nome da rede social",
|
||||
"SETUP_USE_CUSTOMENCODING": "Usar o analisador de codificação customizado",
|
||||
"WEBFRONT_ACTION_BAN_NAME": "Banir",
|
||||
"WEBFRONT_ACTION_LABEL_ID": "ID do cliente",
|
||||
"WEBFRONT_ACTION_LABEL_PASSWORD": "Senha",
|
||||
"WEBFRONT_ACTION_LABEL_REASON": "Razão",
|
||||
"WEBFRONT_ACTION_LOGIN_NAME": "Iniciar a sessão",
|
||||
"WEBFRONT_ACTION_UNBAN_NAME": "Retirar o banimento",
|
||||
"WEBFRONT_CLIENT_META_FALSE": "Não está",
|
||||
"WEBFRONT_CLIENT_META_JOINED": "Entrou com o nome",
|
||||
"WEBFRONT_CLIENT_META_MASKED": "Mascarado",
|
||||
"WEBFRONT_CLIENT_META_TRUE": "Está",
|
||||
"WEBFRONT_CLIENT_PRIVILEGED_TITLE": "Jogadores Privilegiados",
|
||||
"WEBFRONT_CLIENT_PROFILE_TITLE": "Pefil",
|
||||
"WEBFRONT_CLIENT_SEARCH_MATCHING": "Jogadores correspondidos",
|
||||
"WEBFRONT_CONSOLE_EXECUTE": "Executar",
|
||||
"WEBFRONT_CONSOLE_TITLE": "Console da Web",
|
||||
"WEBFRONT_ERROR_DESC": "O IW4MAdmin encontrou um erro",
|
||||
"WEBFRONT_ERROR_GENERIC_DESC": "Ocorreu um erro ao processar seu pedido",
|
||||
"WEBFRONT_ERROR_GENERIC_TITLE": "Desculpe!",
|
||||
"WEBFRONT_ERROR_TITLE": "Erro!",
|
||||
"WEBFRONT_HOME_TITLE": "Visão geral do servidor",
|
||||
"WEBFRONT_NAV_CONSOLE": "Console",
|
||||
"WEBFRONT_NAV_DISCORD": "Discord",
|
||||
"WEBFRONT_NAV_HOME": "Início",
|
||||
"WEBFRONT_NAV_LOGOUT": "Encerrar a sessão",
|
||||
"WEBFRONT_NAV_PENALTIES": "Penalidades",
|
||||
"WEBFRONT_NAV_PRIVILEGED": "Administradores",
|
||||
"WEBFRONT_NAV_PROFILE": "Perfil do Jogador",
|
||||
"WEBFRONT_NAV_SEARCH": "Achar jogador",
|
||||
"WEBFRONT_NAV_SOCIAL": "Rede Social",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_ADMIN": "Administrador",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_AGO": "atrás",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_NAME": "Nome",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_OFFENSE": "Ofensa",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_REMAINING": "restantes",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOW": "Mostrar",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOWONLY": "Mostrar somente",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TIME": "Tempo/Restante",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TYPE": "Tipo",
|
||||
"WEBFRONT_PENALTY_TITLE": "Penalidades dos jogadores",
|
||||
"WEBFRONT_PROFILE_FSEEN": "Visto primeiro em",
|
||||
"WEBFRONT_PROFILE_LEVEL": "Nível",
|
||||
"WEBFRONT_PROFILE_LSEEN": "Visto por último em",
|
||||
"WEBFRONT_PROFILE_PLAYER": "Jogou",
|
||||
"PLUGIN_STATS_SETUP_ENABLEAC": "Habilitar a anti-trapaça no servidor (Somente IW4/MW2)",
|
||||
"PLUGIN_STATS_ERROR_ADD": "Não foi possível adicionar o servidor para as estatísticas do servidor",
|
||||
"PLUGIN_STATS_CHEAT_DETECTED": "Aparentemente você está trapaceando",
|
||||
"PLUGINS_STATS_TEXT_KDR": "KDR",
|
||||
"PLUGINS_STATS_META_SPM": "Pontuação por minuto",
|
||||
"PLUGINS_WELCOME_USERANNOUNCE": "^5{{ClientName}} ^7 vem de ^5{{ClientLocation}}",
|
||||
"PLUGINS_WELCOME_USERWELCOME": "Bem-vindo ^5{{ClientName}}^7, esta é a sua visita de número ^5{{TimesConnected}} ^7 no servidor!",
|
||||
"PLUGINS_WELCOME_PRIVANNOUNCE": "{{ClientLevel}} {{ClientName}} entrou no servidor",
|
||||
"PLUGINS_LOGIN_AUTH": "não está registrado",
|
||||
"PLUGINS_PROFANITY_SETUP_ENABLE": "Habilitar o plugin de anti-palavrão",
|
||||
"PLUGINS_PROFANITY_WARNMSG": "Por favor, não use palavras ofensivas neste servidor",
|
||||
"PLUGINS_PROFANITY_KICKMSG": "Uso excessivo de palavrão, lave a boca da próxima vez",
|
||||
"GLOBAL_DEBUG": "Depuração",
|
||||
"COMMANDS_UNFLAG_DESC": "Remover a sinalização do jogador",
|
||||
"COMMANDS_UNFLAG_FAIL": "Você não pode retirar a sinalização do jogador",
|
||||
"COMMANDS_UNFLAG_NOTFLAGGED": "O jogador não está sinalizado",
|
||||
"COMMANDS_FLAG_ALREADYFLAGGED": "O jogador já está sinalizado",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT": "Mais jogado",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC": "ver o top 5 de jogadores mais dedicados no servidor",
|
||||
"WEBFRONT_PROFILE_MESSAGES": "Mensagens",
|
||||
"WEBFRONT_CLIENT_META_CONNECTIONS": "Conexões",
|
||||
"PLUGINS_STATS_COMMANDS_TOPSTATS_RATING": "Classificação",
|
||||
"PLUGINS_STATS_COMMANDS_PERFORMANCE": "Desempenho"
|
||||
}
|
||||
}
|
||||
}
|
@ -1,269 +0,0 @@
|
||||
{
|
||||
"LocalizationName": "ru-RU",
|
||||
"LocalizationIndex": {
|
||||
"Set": {
|
||||
"BROADCAST_OFFLINE": "^5IW4MAdmin ^1ВЫКЛЮЧАЕТСЯ",
|
||||
"BROADCAST_ONLINE": "^5IW4MADMIN ^7сейчас В СЕТИ",
|
||||
"COMMAND_HELP_OPTIONAL": "опционально",
|
||||
"COMMAND_HELP_SYNTAX": "Проблема с выражением мысли ( пересмотри слова)",
|
||||
"COMMAND_MISSINGARGS": "Приведено недостаточно аргументов",
|
||||
"COMMAND_NOACCESS": "У вас нет доступа к этой команде",
|
||||
"COMMAND_NOTAUTHORIZED": "Вы не авторизованы для исполнения этой команды",
|
||||
"COMMAND_TARGET_MULTI": "Несколько игроков соответствуют этому имени",
|
||||
"COMMAND_TARGET_NOTFOUND": "Невозможно найти указанного игрока",
|
||||
"COMMAND_UNKNOWN": "Вы ввели неизвестную команду",
|
||||
"COMMANDS_ADMINS_DESC": "перечислить присоединенных на данный момент игроков с правами",
|
||||
"COMMANDS_ADMINS_NONE": "Нет видимых администраторов в сети",
|
||||
"COMMANDS_ALIAS_ALIASES": "Имена",
|
||||
"COMMANDS_ALIAS_DESC": "получить прошлые имена и IP игрока",
|
||||
"COMMANDS_ALIAS_IPS": "IP",
|
||||
"COMMANDS_ARGS_CLEAR": "очистить",
|
||||
"COMMANDS_ARGS_CLIENTID": "ID игрока",
|
||||
"COMMANDS_ARGS_COMMANDS": "команды",
|
||||
"COMMANDS_ARGS_DURATION": "длительность (m|h|d|w|y)",
|
||||
"COMMANDS_ARGS_INACTIVE": "дни бездействия",
|
||||
"COMMANDS_ARGS_LEVEL": "уровень",
|
||||
"COMMANDS_ARGS_MAP": "карта",
|
||||
"COMMANDS_ARGS_MESSAGE": "сообщение",
|
||||
"COMMANDS_ARGS_PASSWORD": "пароль",
|
||||
"COMMANDS_ARGS_PLAYER": "игрок",
|
||||
"COMMANDS_ARGS_REASON": "причина",
|
||||
"COMMANDS_BAN_DESC": "навсегда забанить игрока на сервере",
|
||||
"COMMANDS_BAN_FAIL": "Вы не можете выдавать бан",
|
||||
"COMMANDS_BAN_SUCCESS": "был забанен навсегда",
|
||||
"COMMANDS_BANINFO_DESC": "получить информацию о бане игрока",
|
||||
"COMMANDS_BANINFO_NONE": "Не найдено действующего бана для этого игрока",
|
||||
"COMMANDS_BANINO_SUCCESS": "был забанен игроком ^5{0} ^7на:",
|
||||
"COMMANDS_FASTRESTART_DESC": "перезапустить нынешнюю карту",
|
||||
"COMMANDS_FASTRESTART_MASKED": "Карта была перезапущена",
|
||||
"COMMANDS_FASTRESTART_UNMASKED": "перезапустил карту",
|
||||
"COMMANDS_FIND_DESC": "найти игрока в базе данных",
|
||||
"COMMANDS_FIND_EMPTY": "Не найдено игроков",
|
||||
"COMMANDS_FIND_MIN": "Пожалуйста, введите хотя бы 3 символа",
|
||||
"COMMANDS_FLAG_DESC": "отметить подозрительного игрока и сообщить администраторам, чтобы присоединились",
|
||||
"COMMANDS_FLAG_FAIL": "Вы не можете ставить отметки",
|
||||
"COMMANDS_FLAG_SUCCESS": "Вы отметили",
|
||||
"COMMANDS_FLAG_UNFLAG": "Вы сняли отметку",
|
||||
"COMMANDS_HELP_DESC": "перечислить все доступные команды",
|
||||
"COMMANDS_HELP_MOREINFO": "Введите !help <имя команды>, чтобы узнать синтаксис для использования команды",
|
||||
"COMMANDS_HELP_NOTFOUND": "Не удалось найти эту команду",
|
||||
"COMMANDS_IP_DESC": "просмотреть ваш внешний IP-адрес",
|
||||
"COMMANDS_IP_SUCCESS": "Ваш внешний IP:",
|
||||
"COMMANDS_KICK_DESC": "исключить игрока по имени",
|
||||
"COMMANDS_KICK_FAIL": "У вас нет достаточных прав, чтобы исключать",
|
||||
"COMMANDS_KICK_SUCCESS": "был исключен",
|
||||
"COMMANDS_LIST_DESC": "перечислить действующих игроков",
|
||||
"COMMANDS_MAP_DESC": "сменить на определенную карту",
|
||||
"COMMANDS_MAP_SUCCESS": "Смена карты на",
|
||||
"COMMANDS_MAP_UKN": "Попытка сменить на неизвестную карту",
|
||||
"COMMANDS_MAPROTATE": "Смена карты через ^55 ^7секунд",
|
||||
"COMMANDS_MAPROTATE_DESC": "переключиться на следующую карту в ротации",
|
||||
"COMMANDS_MASK_DESC": "скрыть свое присутствие как игрока с правами",
|
||||
"COMMANDS_MASK_OFF": "Вы теперь демаскированы",
|
||||
"COMMANDS_MASK_ON": "Вы теперь замаскированы",
|
||||
"COMMANDS_OWNER_DESC": "утверить владение сервером",
|
||||
"COMMANDS_OWNER_FAIL": "Этот сервер уже имеет владельца",
|
||||
"COMMANDS_OWNER_SUCCESS": "Поздравляю, вы утвердили владение этим сервером!",
|
||||
"COMMANDS_PASSWORD_FAIL": "Ваш пароль должен быть хотя бы 5 символов в длину",
|
||||
"COMMANDS_PASSWORD_SUCCESS": "Ваш пароль был успешно установлен",
|
||||
"COMMANDS_PING_DESC": "получить пинг игрока",
|
||||
"COMMANDS_PING_SELF": "Ваш пинг:",
|
||||
"COMMANDS_PING_TARGET": "пинг:",
|
||||
"COMMANDS_PLUGINS_DESC": "просмотреть все загруженные плагины",
|
||||
"COMMANDS_PLUGINS_LOADED": "Загруженные плагины",
|
||||
"COMMANDS_PM_DESC": "отправить сообщение другому игроку",
|
||||
"COMMANDS_PRUNE_DESC": "понизить любых игроков с правами, которые не подключались за последнее время (по умолчанию: 30 дней)",
|
||||
"COMMANDS_PRUNE_FAIL": "Неверное количество дней бездействия",
|
||||
"COMMANDS_PRUNE_SUCCESS": "бездействующих пользователей с правами было сокращено",
|
||||
"COMMANDS_QUIT_DESC": "покинуть IW4MAdmin",
|
||||
"COMMANDS_RCON_DESC": "отправить RCon команду на сервер",
|
||||
"COMMANDS_RCON_SUCCESS": "Успешно отправлена команда RCon",
|
||||
"COMMANDS_REPORT_DESC": "пожаловаться на игрока за подозрительное поведение",
|
||||
"COMMANDS_REPORT_FAIL": "Вы не можете пожаловаться",
|
||||
"COMMANDS_REPORT_FAIL_CAMP": "Вы не можете пожаловаться на игрока за кемперство",
|
||||
"COMMANDS_REPORT_FAIL_DUPLICATE": "Вы уже пожаловались на этого игрока",
|
||||
"COMMANDS_REPORT_FAIL_SELF": "Вы не можете пожаловаться на самого себя",
|
||||
"COMMANDS_REPORT_SUCCESS": "Спасибо за вашу жалобу, администратор оповещен",
|
||||
"COMMANDS_REPORTS_CLEAR_SUCCESS": "Жалобы успешно очищены",
|
||||
"COMMANDS_REPORTS_DESC": "получить или очистить последние жалобы",
|
||||
"COMMANDS_REPORTS_NONE": "Пока нет жалоб на игроков",
|
||||
"COMMANDS_RULES_DESC": "перечислить правила сервера",
|
||||
"COMMANDS_RULES_NONE": "Владелец сервера не установил никаких правил",
|
||||
"COMMANDS_SAY_DESC": "транслировать сообщения всем игрокам",
|
||||
"COMMANDS_SETLEVEL_DESC": "установить особый уровень прав игроку",
|
||||
"COMMANDS_SETLEVEL_FAIL": "Указана неверная группа",
|
||||
"COMMANDS_SETLEVEL_LEVELTOOHIGH": "Вы только можете повысить ^5{0} ^7до ^5{1} ^7или понизить в правах",
|
||||
"COMMANDS_SETLEVEL_OWNER": "Может быть только 1 владелец. Измените настройки, если требуется несколько владельцов",
|
||||
"COMMANDS_SETLEVEL_SELF": "Вы не можете изменить свой уровень",
|
||||
"COMMANDS_SETLEVEL_STEPPEDDISABLED": "Этот сервер не разрешает вам повыситься",
|
||||
"COMMANDS_SETLEVEL_SUCCESS": "был успешно повышен",
|
||||
"COMMANDS_SETLEVEL_SUCCESS_TARGET": "Поздравляю! Вы были повышены до",
|
||||
"COMMANDS_SETPASSWORD_DESC": "установить свой пароль аутентификации",
|
||||
"COMMANDS_TEMPBAN_DESC": "временно забанить игрока на определенное время (по умолчанию: 1 час)",
|
||||
"COMMANDS_TEMPBAN_FAIL": "Вы не можете выдавать временный бан",
|
||||
"COMMANDS_TEMPBAN_SUCCESS": "был временно забанен за",
|
||||
"COMMANDS_UNBAN_DESC": "разбанить игрока по ID игрока",
|
||||
"COMMANDS_UNBAN_FAIL": "не забанен",
|
||||
"COMMANDS_UNBAN_SUCCESS": "Успешно разбанен",
|
||||
"COMMANDS_UPTIME_DESC": "получить время с начала запуска текущего приложения",
|
||||
"COMMANDS_UPTIME_TEXT": "был в сети",
|
||||
"COMMANDS_USAGE_DESC": "узнать о потреблении памяти приложением",
|
||||
"COMMANDS_USAGE_TEXT": "используется",
|
||||
"COMMANDS_WARN_DESC": "предупредить игрока за нарушение правил",
|
||||
"COMMANDS_WARN_FAIL": "У вас недостаточно прав, чтобы выносить предупреждения",
|
||||
"COMMANDS_WARNCLEAR_DESC": "удалить все предупреждения у игрока",
|
||||
"COMMANDS_WARNCLEAR_SUCCESS": "Все предупреждения очищены у",
|
||||
"COMMANDS_WHO_DESC": "предоставить информацию о себе",
|
||||
"GLOBAL_TIME_DAYS": "дней",
|
||||
"GLOBAL_ERROR": "Ошибка",
|
||||
"GLOBAL_TIME_HOURS": "часов",
|
||||
"GLOBAL_INFO": "Информация",
|
||||
"GLOBAL_TIME_MINUTES": "минут",
|
||||
"GLOBAL_REPORT": "Если вы подозреваете кого-то в ^5ЧИТЕРСТВЕ^7, используйте команду ^5!report",
|
||||
"GLOBAL_VERBOSE": "Подробно",
|
||||
"GLOBAL_WARNING": "Предупреждение",
|
||||
"MANAGER_CONNECTION_REST": "Соединение было восстановлено с помощью",
|
||||
"MANAGER_CONSOLE_NOSERV": "На данный момент нет серверов под мониторингом",
|
||||
"MANAGER_EXIT": "Нажмите любую клавишу, чтобы выйти...",
|
||||
"MANAGER_INIT_FAIL": "Критическая ошибка во время инициализации",
|
||||
"MANAGER_MONITORING_TEXT": "Идет мониторинг",
|
||||
"MANAGER_SHUTDOWN_SUCCESS": "Выключение завершено",
|
||||
"MANAGER_VERSION_CURRENT": "Ваша версия:",
|
||||
"MANAGER_VERSION_FAIL": "Не удалось получить последнюю версию IW4MAdmin",
|
||||
"MANAGER_VERSION_SUCCESS": "IW4MAdmin обновлен",
|
||||
"MANAGER_VERSION_UPDATE": "- есть обновление. Последняя версия:",
|
||||
"PLUGIN_IMPORTER_NOTFOUND": "Не найдено плагинов для загрузки",
|
||||
"PLUGIN_IMPORTER_REGISTERCMD": "Зарегистрированная команда",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_DESC": "войти, используя пароль",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL": "Ваш пароль неверный",
|
||||
"PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS": "Вы теперь вошли",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_DESC": "сбросить вашу статистику под ноль",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_FAIL": "Вы должны быть подключены к серверу, чтобы сбросить свою статистику",
|
||||
"PLUGINS_STATS_COMMANDS_RESET_SUCCESS": "Ваша статистика на этом сервере была сброшена",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_DESC": "показать топ-5 лучших игроков на этом сервере",
|
||||
"PLUGINS_STATS_COMMANDS_TOP_TEXT": "Лучшие игроки",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_DESC": "просмотреть свою статистику",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL": "Не удается найти игрока, которого вы указали.",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME": "Указанный игрок должен быть в игре",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_FAIL_INGAME_SELF": "Вы должны быть в игре, чтобы просмотреть свою статистику",
|
||||
"PLUGINS_STATS_COMMANDS_VIEW_SUCCESS": "Статистика",
|
||||
"PLUGINS_STATS_TEXT_DEATHS": "СМЕРТЕЙ",
|
||||
"PLUGINS_STATS_TEXT_KILLS": "УБИЙСТВ",
|
||||
"PLUGINS_STATS_TEXT_NOQUALIFY": "Ещё нет совернующихся игроков за лучшую статистику",
|
||||
"PLUGINS_STATS_TEXT_SKILL": "МАСТЕРСТВО",
|
||||
"SERVER_BAN_APPEAL": "оспорить:",
|
||||
"SERVER_BAN_PREV": "Ранее забанены за",
|
||||
"SERVER_BAN_TEXT": "Вы забанены",
|
||||
"SERVER_ERROR_ADDPLAYER": "Не удалось добавить игрока",
|
||||
"SERVER_ERROR_COMMAND_INGAME": "Произошла внутренняя ошибка при обработке вашей команды",
|
||||
"SERVER_ERROR_COMMAND_LOG": "команда сгенерировала ошибку",
|
||||
"SERVER_ERROR_COMMUNICATION": "Не удалось связаться с",
|
||||
"SERVER_ERROR_DNE": "не существует",
|
||||
"SERVER_ERROR_DVAR": "Не удалось получить значение dvar:",
|
||||
"SERVER_ERROR_DVAR_HELP": "убедитесь, что на сервере загружена карта",
|
||||
"SERVER_ERROR_EXCEPTION": "Неожиданное исключение на",
|
||||
"SERVER_ERROR_LOG": "Неверный игровой лог-файл",
|
||||
"SERVER_ERROR_PLUGIN": "Произошла ошибка загрузки плагина",
|
||||
"SERVER_ERROR_POLLING": "снижение частоты обновления данных",
|
||||
"SERVER_ERROR_UNFIXABLE": "Мониторинг сервера выключен из-за неисправимых ошибок",
|
||||
"SERVER_KICK_CONTROLCHARS": "Ваше имя не должно содержать спецсимволы",
|
||||
"SERVER_KICK_GENERICNAME": "Пожалуйста, смените ваше имя, используя /name",
|
||||
"SERVER_KICK_MINNAME": "Ваше имя должно содержать хотя бы 3 символа",
|
||||
"SERVER_KICK_NAME_INUSE": "Ваше имя используется кем-то другим",
|
||||
"SERVER_KICK_TEXT": "Вы были исключены",
|
||||
"SERVER_KICK_VPNS_NOTALLOWED": "Использование VPN не разрешено на этом сервере",
|
||||
"SERVER_PLUGIN_ERROR": "Плагин образовал ошибку",
|
||||
"SERVER_REPORT_COUNT": "Имеется ^5{0} ^7жалоб за последнее время",
|
||||
"SERVER_TB_REMAIN": "Вы временно забанены",
|
||||
"SERVER_TB_TEXT": "Вы временно забанены",
|
||||
"SERVER_WARNING": "ПРЕДУПРЕЖДЕНИЕ",
|
||||
"SERVER_WARNLIMT_REACHED": "Слишком много предупреждений",
|
||||
"SERVER_WEBSITE_GENERIC": "веб-сайт этого сервера",
|
||||
"SETUP_DISPLAY_SOCIAL": "Отображать ссылку на социальную сеть в веб-интерфейсе (Discord, веб-сайт, ВК, и т.д.)",
|
||||
"SETUP_ENABLE_CUSTOMSAY": "Включить кастомное имя для чата",
|
||||
"SETUP_ENABLE_MULTIOWN": "Включить поддержку нескольких владельцев",
|
||||
"SETUP_ENABLE_STEPPEDPRIV": "Включить последовательную иерархию прав",
|
||||
"SETUP_ENABLE_VPNS": "Включить поддержку VPN у игроков",
|
||||
"SETUP_ENABLE_WEBFRONT": "Включить веб-интерфейс",
|
||||
"SETUP_ENCODING_STRING": "Введите кодировку",
|
||||
"SETUP_IPHUB_KEY": "Введите iphub.info api-ключ",
|
||||
"SETUP_SAY_NAME": "Введите кастомное имя для чата",
|
||||
"SETUP_SERVER_IP": "Введите IP-адрес сервера",
|
||||
"SETUP_SERVER_MANUALLOG": "Введите путь для лог-файла",
|
||||
"SETUP_SERVER_PORT": "Введите порт сервера",
|
||||
"SETUP_SERVER_RCON": "Введите RCon пароль сервера",
|
||||
"SETUP_SERVER_SAVE": "Настройки сохранены, добавить",
|
||||
"SETUP_SERVER_USEIW5M": "Использовать парсер Pluto IW5",
|
||||
"SETUP_SERVER_USET6M": "Использовать парсер Pluto T6",
|
||||
"SETUP_SOCIAL_LINK": "Ввести ссылку на социальную сеть",
|
||||
"SETUP_SOCIAL_TITLE": "Ввести имя социальной сети",
|
||||
"SETUP_USE_CUSTOMENCODING": "Использовать кастомную кодировку парсера",
|
||||
"WEBFRONT_ACTION_BAN_NAME": "Забанить",
|
||||
"WEBFRONT_ACTION_LABEL_ID": "ID игрока",
|
||||
"WEBFRONT_ACTION_LABEL_PASSWORD": "Пароль",
|
||||
"WEBFRONT_ACTION_LABEL_REASON": "Причина",
|
||||
"WEBFRONT_ACTION_LOGIN_NAME": "Войти",
|
||||
"WEBFRONT_ACTION_UNBAN_NAME": "Разбанить",
|
||||
"WEBFRONT_CLIENT_META_FALSE": "не",
|
||||
"WEBFRONT_CLIENT_META_JOINED": "Присоединился с именем",
|
||||
"WEBFRONT_CLIENT_META_MASKED": "Замаскирован",
|
||||
"WEBFRONT_CLIENT_META_TRUE": "Это",
|
||||
"WEBFRONT_CLIENT_PRIVILEGED_TITLE": "Игроки с правами",
|
||||
"WEBFRONT_CLIENT_PROFILE_TITLE": "Профиль",
|
||||
"WEBFRONT_CLIENT_SEARCH_MATCHING": "Подходящие игроки",
|
||||
"WEBFRONT_CONSOLE_EXECUTE": "Выполнить",
|
||||
"WEBFRONT_CONSOLE_TITLE": "Веб-консоль",
|
||||
"WEBFRONT_ERROR_DESC": "IW4MAdmin столкнулся с ошибкой",
|
||||
"WEBFRONT_ERROR_GENERIC_DESC": "Произошла ошибка во время обработки вашего запроса",
|
||||
"WEBFRONT_ERROR_GENERIC_TITLE": "Извините!",
|
||||
"WEBFRONT_ERROR_TITLE": "Ошибка!",
|
||||
"WEBFRONT_HOME_TITLE": "Обзор сервера",
|
||||
"WEBFRONT_NAV_CONSOLE": "Консоль",
|
||||
"WEBFRONT_NAV_DISCORD": "Дискорд ",
|
||||
"WEBFRONT_NAV_HOME": "Обзор Серверов ",
|
||||
"WEBFRONT_NAV_LOGOUT": "Выйти",
|
||||
"WEBFRONT_NAV_PENALTIES": "Наказания",
|
||||
"WEBFRONT_NAV_PRIVILEGED": "Админы",
|
||||
"WEBFRONT_NAV_PROFILE": "Профиль игрока",
|
||||
"WEBFRONT_NAV_SEARCH": "Найти игрока",
|
||||
"WEBFRONT_NAV_SOCIAL": "Соц. сети",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_ADMIN": "Админ",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_AGO": "назад",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_NAME": "Имя",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_OFFENSE": "Нарушение",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_REMAINING": "осталось",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOW": "Показывать",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_SHOWONLY": "Показывать только",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TIME": "Время/Осталось",
|
||||
"WEBFRONT_PENALTY_TEMPLATE_TYPE": "Тип",
|
||||
"WEBFRONT_PENALTY_TITLE": "Наказания игроков",
|
||||
"WEBFRONT_PROFILE_FSEEN": "Впервые заходил",
|
||||
"WEBFRONT_PROFILE_LEVEL": "Уровень",
|
||||
"WEBFRONT_PROFILE_LSEEN": "Последний раз заходил",
|
||||
"WEBFRONT_PROFILE_PLAYER": "Наиграл",
|
||||
"PLUGIN_STATS_SETUP_ENABLEAC": "Включить серверный античит (только IW4)",
|
||||
"PLUGIN_STATS_ERROR_ADD": "Не удалось добавить сервер в статистику серверов",
|
||||
"PLUGIN_STATS_CHEAT_DETECTED": "Кажется, вы читерите",
|
||||
"PLUGINS_STATS_TEXT_KDR": "Вот так ..",
|
||||
"PLUGINS_STATS_META_SPM": "Счёт за минуту",
|
||||
"PLUGINS_WELCOME_USERANNOUNCE": "^5{{ClientName}} ^7из ^5{{ClientLocation}}",
|
||||
"PLUGINS_WELCOME_USERWELCOME": "Добро пожаловать, ^5{{ClientName}}^7. Это ваше ^5{{TimesConnected}} ^7подключение по счёту!",
|
||||
"PLUGINS_WELCOME_PRIVANNOUNCE": "{{ClientLevel}} {{ClientName}} присоединился к серверу",
|
||||
"PLUGINS_LOGIN_AUTH": "Сперва Подключись",
|
||||
"PLUGINS_PROFANITY_SETUP_ENABLE": "Включить сдерживание ненормативной лексики",
|
||||
"PLUGINS_PROFANITY_WARNMSG": "Пожалуйта, не ругайтесь на этом сервере",
|
||||
"PLUGINS_PROFANITY_KICKMSG": "Чрезмерное употребление ненормативной лексики",
|
||||
"GLOBAL_DEBUG": "Отлаживание ",
|
||||
"COMMANDS_UNFLAG_DESC": "Снять все подозрение с игрока !",
|
||||
"COMMANDS_UNFLAG_FAIL": "Вы не можете снять подозрения..",
|
||||
"COMMANDS_UNFLAG_NOTFLAGGED": "Игрок без подозрения !",
|
||||
"COMMANDS_FLAG_ALREADYFLAGGED": "Игрок помечен ! ",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_TEXT": "Самые популярные",
|
||||
"PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC": "просмотр 5 лучших игроков на сервере",
|
||||
"WEBFRONT_PROFILE_MESSAGES": "Сообщения",
|
||||
"WEBFRONT_CLIENT_META_CONNECTIONS": "Подключения",
|
||||
"PLUGINS_STATS_COMMANDS_TOPSTATS_RATING": "Рейтинг",
|
||||
"PLUGINS_STATS_COMMANDS_PERFORMANCE": "Эффективность"
|
||||
}
|
||||
}
|
||||
}
|
@ -34,10 +34,11 @@ namespace IW4MAdmin.Application
|
||||
try
|
||||
{
|
||||
ServerManager = ApplicationManager.GetInstance();
|
||||
var configuration = ServerManager.GetApplicationSettings().Configuration();
|
||||
|
||||
if (ServerManager.GetApplicationSettings().Configuration() != null)
|
||||
if (configuration != null)
|
||||
{
|
||||
Localization.Configure.Initialize(ServerManager.GetApplicationSettings().Configuration().CustomLocale);
|
||||
Localization.Configure.Initialize(configuration.EnableCustomLocale ? (configuration.CustomLocale ?? "windows-1252") : "windows-1252");
|
||||
}
|
||||
|
||||
else
|
||||
@ -90,7 +91,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
|
||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}]");
|
||||
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}]"));
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
#else
|
||||
@ -98,7 +99,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
|
||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}-pr]");
|
||||
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}-pr]"));
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
#endif
|
||||
|
@ -119,9 +119,9 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
}
|
||||
|
||||
int validMatches = 0;
|
||||
foreach (string S in Status)
|
||||
foreach (string statusLine in Status)
|
||||
{
|
||||
string responseLine = S.Trim();
|
||||
string responseLine = statusLine.Trim();
|
||||
|
||||
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);
|
||||
|
||||
@ -158,11 +158,11 @@ namespace IW4MAdmin.Application.RconParsers
|
||||
State = EFClient.ClientState.Connecting
|
||||
};
|
||||
|
||||
// they've not fully connected yet
|
||||
if (!client.IsBot && ping == 999)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
//// they've not fully connected yet
|
||||
//if (!client.IsBot && ping == 999)
|
||||
//{
|
||||
// continue;
|
||||
//}
|
||||
|
||||
StatusPlayers.Add(client);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<SuppressCollectPythonCloudServiceFiles>true</SuppressCollectPythonCloudServiceFiles>
|
||||
<Name>GameLogServer</Name>
|
||||
<RootNamespace>GameLogServer</RootNamespace>
|
||||
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
|
||||
<InterpreterId>MSBuild|log_env|$(MSBuildProjectFullPath)</InterpreterId>
|
||||
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
|
||||
<Environment>DEBUG=True</Environment>
|
||||
</PropertyGroup>
|
||||
@ -50,7 +50,6 @@
|
||||
<Folder Include="GameLogServer\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="requirements.txt" />
|
||||
<None Include="Stable.pubxml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -63,6 +62,18 @@
|
||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||
<Architecture>X64</Architecture>
|
||||
</Interpreter>
|
||||
<Interpreter Include="log_env\">
|
||||
<Id>log_env</Id>
|
||||
<Version>3.6</Version>
|
||||
<Description>log_env (Python 3.6 (64-bit))</Description>
|
||||
<InterpreterPath>Scripts\python.exe</InterpreterPath>
|
||||
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
|
||||
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
|
||||
<Architecture>X64</Architecture>
|
||||
</Interpreter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="requirements.txt" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.Web.targets" />
|
||||
<!-- Specify pre- and post-build commands in the BeforeBuild and
|
||||
|
@ -5,55 +5,53 @@ import time
|
||||
class LogReader(object):
|
||||
def __init__(self):
|
||||
self.log_file_sizes = {}
|
||||
# (if the file changes more than this, ignore ) - 0.125 MB
|
||||
self.max_file_size_change = 125000
|
||||
# (if the time between checks is greater, ignore ) - 5 minutes
|
||||
self.max_file_time_change = 60
|
||||
# (if the time between checks is greater, ignore ) - in seconds
|
||||
self.max_file_time_change = 30
|
||||
|
||||
def read_file(self, path):
|
||||
# this removes old entries that are no longer valid
|
||||
try:
|
||||
self._clear_old_logs()
|
||||
except Exception as e:
|
||||
print('could not clear old logs')
|
||||
print(e)
|
||||
|
||||
if os.name != 'nt':
|
||||
path = re.sub(r'^[A-Z]\:', '', path)
|
||||
path = re.sub(r'\\+', '/', path)
|
||||
|
||||
# prevent traversing directories
|
||||
if re.search('r^.+\.\.\\.+$', path):
|
||||
return False
|
||||
# must be a valid log path and log file
|
||||
if not re.search(r'^.+[\\|\/](.+)[\\|\/].+.log$', path):
|
||||
return False
|
||||
# set the initialze size to the current file size
|
||||
file_size = 0
|
||||
|
||||
if path not in self.log_file_sizes:
|
||||
self.log_file_sizes[path] = {
|
||||
'length' : self.file_length(path),
|
||||
'read': time.time()
|
||||
}
|
||||
return True
|
||||
|
||||
# grab the previous values
|
||||
last_length = self.log_file_sizes[path]['length']
|
||||
last_read = self.log_file_sizes[path]['read']
|
||||
|
||||
# the file is being tracked already
|
||||
# get the new file size
|
||||
new_file_size = self.file_length(path)
|
||||
|
||||
# the log size was unable to be read (probably the wrong path)
|
||||
# the log size was unable to be read (probably the wrong path)
|
||||
if new_file_size < 0:
|
||||
return False
|
||||
|
||||
now = time.time()
|
||||
|
||||
# this is the first time the log has been requested
|
||||
if path not in self.log_file_sizes:
|
||||
self.log_file_sizes[path] = {
|
||||
'length' : new_file_size,
|
||||
'read': time.time()
|
||||
}
|
||||
return ''
|
||||
|
||||
# grab the previous values
|
||||
last_length = self.log_file_sizes[path]['length']
|
||||
file_size_difference = new_file_size - last_length
|
||||
time_difference = now - last_read
|
||||
|
||||
# update the new size and actually read the data
|
||||
self.log_file_sizes[path] = {
|
||||
'length': new_file_size,
|
||||
'read': now
|
||||
'read': time.time()
|
||||
}
|
||||
|
||||
# if it's been too long since we read and the amount changed is too great, discard it
|
||||
# todo: do we really want old events? maybe make this an "or"
|
||||
if file_size_difference > self.max_file_size_change or time_difference > self.max_file_time_change:
|
||||
return True
|
||||
|
||||
new_log_info = self.get_file_lines(path, file_size_difference)
|
||||
return new_log_info
|
||||
|
||||
@ -63,14 +61,25 @@ class LogReader(object):
|
||||
file_handle.seek(-length, 2)
|
||||
file_data = file_handle.read(length)
|
||||
file_handle.close()
|
||||
return file_data.decode('utf-8')
|
||||
except:
|
||||
# using ignore errors omits the pesky 0xb2 bytes we're reading in for some reason
|
||||
return file_data.decode('utf-8', errors='ignore')
|
||||
except Exception as e:
|
||||
print('could not read the log file at {0}, wanted to read {1} bytes'.format(path, length))
|
||||
print(e)
|
||||
return False
|
||||
|
||||
def _clear_old_logs(self):
|
||||
expired_logs = [path for path in self.log_file_sizes if int(time.time() - self.log_file_sizes[path]['read']) > self.max_file_time_change]
|
||||
for log in expired_logs:
|
||||
print('removing expired log {0}'.format(log))
|
||||
del self.log_file_sizes[log]
|
||||
|
||||
def file_length(self, path):
|
||||
try:
|
||||
return os.stat(path).st_size
|
||||
except:
|
||||
except Exception as e:
|
||||
print('could not get the size of the log file at {0}'.format(path))
|
||||
print(e)
|
||||
return -1
|
||||
|
||||
reader = LogReader()
|
||||
|
@ -7,13 +7,8 @@ class LogResource(Resource):
|
||||
path = urlsafe_b64decode(path).decode('utf-8')
|
||||
log_info = reader.read_file(path)
|
||||
|
||||
if log_info is False:
|
||||
print('could not read log file ' + path)
|
||||
|
||||
empty_read = (log_info == False) or (log_info == True)
|
||||
|
||||
return {
|
||||
'success' : log_info is not False,
|
||||
'length': -1 if empty_read else len(log_info),
|
||||
'length': 0 if log_info is False else len(log_info),
|
||||
'data': log_info
|
||||
}
|
||||
|
@ -1,29 +1,29 @@
|
||||
from flask_restful import Resource
|
||||
from flask import request
|
||||
import requests
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
#from flask_restful import Resource
|
||||
#from flask import request
|
||||
#import requests
|
||||
#import os
|
||||
#import subprocess
|
||||
#import re
|
||||
|
||||
def get_pid_of_server_windows(port):
|
||||
process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
||||
output = process.communicate()[0]
|
||||
matches = re.search(' *(UDP) +([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):'+ str(port) + ' +[^\w]*([0-9]+)', output.decode('utf-8'))
|
||||
if matches is not None:
|
||||
return matches.group(3)
|
||||
else:
|
||||
return 0
|
||||
#def get_pid_of_server_windows(port):
|
||||
# process = subprocess.Popen('netstat -aon', shell=True, stdout=subprocess.PIPE)
|
||||
# output = process.communicate()[0]
|
||||
# matches = re.search(' *(UDP) +([0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}):'+ str(port) + ' +[^\w]*([0-9]+)', output.decode('utf-8'))
|
||||
# if matches is not None:
|
||||
# return matches.group(3)
|
||||
# else:
|
||||
# return 0
|
||||
|
||||
class RestartResource(Resource):
|
||||
def get(self):
|
||||
try:
|
||||
response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
||||
if response.status_code == 200:
|
||||
pid = get_pid_of_server_windows(response.json()['port'])
|
||||
subprocess.check_output("Taskkill /PID %s /F" % pid)
|
||||
else:
|
||||
return {}, 400
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {}, 500
|
||||
return {}, 200
|
||||
#class RestartResource(Resource):
|
||||
# def get(self):
|
||||
# try:
|
||||
# response = requests.get('http://' + request.remote_addr + ':1624/api/restartapproved')
|
||||
# if response.status_code == 200:
|
||||
# pid = get_pid_of_server_windows(response.json()['port'])
|
||||
# subprocess.check_output("Taskkill /PID %s /F" % pid)
|
||||
# else:
|
||||
# return {}, 400
|
||||
# except Exception as e:
|
||||
# print(e)
|
||||
# return {}, 500
|
||||
# return {}, 200
|
||||
|
@ -1,11 +1,14 @@
|
||||
from flask import Flask
|
||||
from flask_restful import Api
|
||||
from .log_resource import LogResource
|
||||
from .restart_resource import RestartResource
|
||||
#from .restart_resource import RestartResource
|
||||
import logging
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
def init():
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
api = Api(app)
|
||||
api.add_resource(LogResource, '/log/<string:path>')
|
||||
api.add_resource(RestartResource, '/restart')
|
||||
#api.add_resource(RestartResource, '/restart')
|
||||
|
@ -1,26 +1,12 @@
|
||||
aniso8601==3.0.2
|
||||
APScheduler==3.5.3
|
||||
certifi==2018.10.15
|
||||
chardet==3.0.4
|
||||
click==6.7
|
||||
aniso8601==6.0.0
|
||||
Click==7.0
|
||||
Flask==1.0.2
|
||||
Flask-JWT==0.3.2
|
||||
Flask-JWT-Extended==3.8.1
|
||||
Flask-RESTful==0.3.6
|
||||
idna==2.7
|
||||
itsdangerous==0.24
|
||||
Flask-RESTful==0.3.7
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.10
|
||||
MarkupSafe==1.0
|
||||
marshmallow==3.0.0b8
|
||||
pip==9.0.3
|
||||
psutil==5.4.8
|
||||
pygal==2.4.0
|
||||
PyJWT==1.4.2
|
||||
pytz==2018.7
|
||||
requests==2.20.0
|
||||
setuptools==40.5.0
|
||||
six==1.11.0
|
||||
timeago==1.0.8
|
||||
tzlocal==1.5.1
|
||||
urllib3==1.24
|
||||
Werkzeug==0.14.1
|
||||
MarkupSafe==1.1.1
|
||||
pip==10.0.1
|
||||
pytz==2018.9
|
||||
setuptools==39.0.1
|
||||
six==1.12.0
|
||||
Werkzeug==0.15.2
|
||||
|
@ -41,6 +41,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug
|
||||
Plugins\ScriptPlugins\ParserCoD4x.js = Plugins\ScriptPlugins\ParserCoD4x.js
|
||||
Plugins\ScriptPlugins\ParserIW4x.js = Plugins\ScriptPlugins\ParserIW4x.js
|
||||
Plugins\ScriptPlugins\ParserPT6.js = Plugins\ScriptPlugins\ParserPT6.js
|
||||
Plugins\ScriptPlugins\ParserRektT5M.js = Plugins\ScriptPlugins\ParserRektT5M.js
|
||||
Plugins\ScriptPlugins\ParserTeknoMW3.js = Plugins\ScriptPlugins\ParserTeknoMW3.js
|
||||
Plugins\ScriptPlugins\SharedGUIDKick.js = Plugins\ScriptPlugins\SharedGUIDKick.js
|
||||
Plugins\ScriptPlugins\VPNDetection.js = Plugins\ScriptPlugins\VPNDetection.js
|
||||
@ -52,6 +53,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{A848FCF1-852
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StatsWeb", "Plugins\Web\StatsWeb\StatsWeb.csproj", "{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@ -290,8 +293,8 @@ Global
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{6C706CE5-A206-4E46-8712-F8C48D526091}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -310,7 +313,7 @@ Global
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU
|
||||
@ -319,15 +322,13 @@ Global
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.Build.0 = Release|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{42EFDA12-10D3-4C40-A210-9483520116BC}.Prerelease|x64.ActiveCfg = Release|Any CPU
|
||||
@ -350,8 +351,8 @@ Global
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||
@ -366,6 +367,30 @@ Global
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x64.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x64.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x86.ActiveCfg = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Prerelease|x86.Build.0 = Debug|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -380,6 +405,7 @@ Global
|
||||
{3F9ACC27-26DB-49FA-BCD2-50C54A49C9FA} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||
{A848FCF1-8527-4AA8-A1AA-50D29695C678} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||
{776B348B-F818-4A0F-A625-D0AF8BAD3E9B} = {A848FCF1-8527-4AA8-A1AA-50D29695C678}
|
||||
{F5815359-CFC7-44B4-9A3B-C04BACAD5836} = {26E8B310-269E-46D4-A612-24601F16065F}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
||||
|
21
Plugins/AutomessageFeed/AutomessageFeed.csproj
Normal file
21
Plugins/AutomessageFeed/AutomessageFeed.csproj
Normal file
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\SharedLibraryCore\SharedLibraryCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="copy "$(TargetPath)" "$(SolutionDir)BUILD\Plugins""/>
|
||||
<Exec Command="copy "$(TargetDir)Microsoft.SyndicationFeed.ReaderWriter.dll" "$(SolutionDir)BUILD\Plugins""/>
|
||||
</Target>
|
||||
|
||||
</Project>
|
29
Plugins/AutomessageFeed/Configuration.cs
Normal file
29
Plugins/AutomessageFeed/Configuration.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
|
||||
namespace AutomessageFeed
|
||||
{
|
||||
class Configuration : IBaseConfiguration
|
||||
{
|
||||
public bool EnableFeed { get; set; }
|
||||
public string FeedUrl { get; set; }
|
||||
public int MaxFeedItems { get; set; }
|
||||
|
||||
public IBaseConfiguration Generate()
|
||||
{
|
||||
EnableFeed = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_ENABLE"]);
|
||||
|
||||
if (EnableFeed)
|
||||
{
|
||||
FeedUrl = Utilities.PromptString(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_URL"]);
|
||||
MaxFeedItems = Utilities.PromptInt(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_MAXITEMS"],
|
||||
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_PROMPT_MAXITEMS_DESC"],
|
||||
0, int.MaxValue, 0);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public string Name() => "AutomessageFeedConfiguration";
|
||||
}
|
||||
}
|
85
Plugins/AutomessageFeed/Plugin.cs
Normal file
85
Plugins/AutomessageFeed/Plugin.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.SyndicationFeed.Rss;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using System.Xml;
|
||||
using Microsoft.SyndicationFeed;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AutomessageFeed
|
||||
{
|
||||
public class Plugin : IPlugin
|
||||
{
|
||||
public string Name => "Automessage Feed";
|
||||
|
||||
public float Version => (float)Utilities.GetVersionAsDouble();
|
||||
|
||||
public string Author => "RaidMax";
|
||||
|
||||
private Configuration _configuration;
|
||||
private int _currentFeedItem;
|
||||
|
||||
private async Task<string> GetNextFeedItem(Server server)
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
using (var reader = XmlReader.Create(_configuration.FeedUrl, new XmlReaderSettings() { Async = true }))
|
||||
{
|
||||
var feedReader = new RssFeedReader(reader);
|
||||
|
||||
while (await feedReader.Read())
|
||||
{
|
||||
switch (feedReader.ElementType)
|
||||
{
|
||||
case SyndicationElementType.Item:
|
||||
var item = await feedReader.ReadItem();
|
||||
items.Add(Regex.Replace(item.Title, @"\<.+\>.*\</.+\>", ""));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentFeedItem < items.Count && (_configuration.MaxFeedItems == 0 || _currentFeedItem < _configuration.MaxFeedItems))
|
||||
{
|
||||
_currentFeedItem++;
|
||||
return items[_currentFeedItem - 1];
|
||||
}
|
||||
|
||||
_currentFeedItem = 0;
|
||||
return Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_AUTOMESSAGEFEED_NO_ITEMS"];
|
||||
}
|
||||
|
||||
public Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OnLoadAsync(IManager manager)
|
||||
{
|
||||
var cfg = new BaseConfigurationHandler<Configuration>("AutomessageFeedPluginSettings");
|
||||
if (cfg.Configuration() == null)
|
||||
{
|
||||
cfg.Set((Configuration)new Configuration().Generate());
|
||||
await cfg.Save();
|
||||
}
|
||||
|
||||
_configuration = cfg.Configuration();
|
||||
|
||||
manager.GetMessageTokens().Add(new MessageToken("FEED", GetNextFeedItem));
|
||||
}
|
||||
|
||||
public Task OnTickAsync(Server S)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task OnUnloadAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -24,12 +24,12 @@ namespace IW4MAdmin.Plugins.Login.Commands
|
||||
if (!success)
|
||||
{
|
||||
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, client.PasswordSalt));
|
||||
success = hashedPassword[0] == client.Password;
|
||||
}
|
||||
|
||||
if (hashedPassword[0] == client.Password)
|
||||
{
|
||||
success = true;
|
||||
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
|
||||
}
|
||||
|
||||
_ = success ?
|
||||
|
@ -7,7 +7,6 @@ using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
@ -49,11 +48,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
|
||||
if (containsObjectionalWord)
|
||||
{
|
||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
CurrentServer = E.Owner
|
||||
});
|
||||
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, Utilities.IW4MAdminClient(E.Owner));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'FrenchFry, RaidMax',
|
||||
version: 0.2,
|
||||
version: 0.3,
|
||||
name: 'CoD4x Parser',
|
||||
isParser: true,
|
||||
|
||||
@ -27,6 +27,7 @@ var plugin = {
|
||||
eventParser.Configuration.GameDirectory = 'main';
|
||||
eventParser.Version = 'CoD4 X 1.8 win_mingw-x86 build 2055 May 2 2017';
|
||||
eventParser.GameName = 1; // IW3
|
||||
eventParser.URLProtocolFormat = 'cod4://{{ip}}:{{port}}';
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
|
@ -3,8 +3,8 @@ var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
name: 'IW3 Parser',
|
||||
version: 0.3,
|
||||
name: 'IW4 Parser',
|
||||
isParser: true,
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
@ -25,6 +25,7 @@ var plugin = {
|
||||
rconParser.GameName = 2; // IW4x
|
||||
eventParser.Version = 'IW4x (v0.6.0)';
|
||||
eventParser.GameName = 2; // IW4x
|
||||
eventParser.URLProtocolFormat = 'iw4x://{{ip}}:{{port}}';
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
|
@ -4,7 +4,7 @@ var eventParser;
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.2,
|
||||
name: 'Plutoniun T6 Parser',
|
||||
name: 'Plutonium T6 Parser',
|
||||
isParser: true,
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
|
34
Plugins/ScriptPlugins/ParserRektT5M.js
Normal file
34
Plugins/ScriptPlugins/ParserRektT5M.js
Normal file
@ -0,0 +1,34 @@
|
||||
var rconParser;
|
||||
var eventParser;
|
||||
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.1,
|
||||
name: 'RektT5m Parser',
|
||||
isParser: true,
|
||||
|
||||
onEventAsync: function (gameEvent, server) {
|
||||
},
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
rconParser = manager.GenerateDynamicRConParser();
|
||||
eventParser = manager.GenerateDynamicEventParser();
|
||||
|
||||
eventParser.Configuration.GameDirectory = 'data';
|
||||
|
||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xff\1print';
|
||||
rconParser.Configuration.CommandPrefixes.Tell = 'tell {0} {1}';
|
||||
rconParser.Configuration.CommandPrefixes.RConGetInfo = undefined;
|
||||
|
||||
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
|
||||
eventParser.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';
|
||||
eventParser.GameName = 6; // T5
|
||||
},
|
||||
|
||||
onUnloadAsync: function () {
|
||||
},
|
||||
|
||||
onTickAsync: function (server) {
|
||||
}
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
var plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 1.0,
|
||||
version: 1.1,
|
||||
name: 'VPN Detection Plugin',
|
||||
|
||||
manager: null,
|
||||
@ -33,7 +33,7 @@ var plugin = {
|
||||
cl.Dispose();
|
||||
usingVPN = parsedJSON.success && parsedJSON.proxy;
|
||||
} catch (e) {
|
||||
this.logger.WriteError(e.message);
|
||||
this.logger.WriteWarning('There was a problem checking client IP for VPN ' + e.message);
|
||||
}
|
||||
|
||||
if (usingVPN) {
|
||||
|
@ -39,7 +39,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
where stats.ServerId == serverId
|
||||
where client.Level != EFClient.Permission.Banned
|
||||
where client.LastConnection >= thirtyDaysAgo
|
||||
orderby stats.Kills descending
|
||||
orderby stats.TimePlayed descending
|
||||
select new
|
||||
{
|
||||
alias.Name,
|
||||
|
@ -69,7 +69,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
{
|
||||
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
|
||||
E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7");
|
||||
E.Owner.Broadcast(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(name));
|
||||
E.Owner.Broadcast(statLine);
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
{
|
||||
if (E.Target != null)
|
||||
{
|
||||
E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7");
|
||||
E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
E.Origin.Tell(statLine);
|
||||
|
@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -12,25 +14,51 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
public class StatsController : BaseController
|
||||
{
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> TopPlayersAsync()
|
||||
public IActionResult TopPlayersAsync()
|
||||
{
|
||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_TITLE"];
|
||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex.Set["WEBFRONT_STATS_INDEX_DESC"];
|
||||
ViewBag.Title = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_TITLE"];
|
||||
ViewBag.Description = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_STATS_INDEX_DESC"];
|
||||
ViewBag.Servers = Manager.GetServers().Select(_server => new ServerInfo() { Name = _server.Hostname, ID = _server.EndPoint });
|
||||
|
||||
return View("Index", await Plugin.Manager.GetTopStats(0, 50));
|
||||
return View("Index");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetTopPlayersAsync(int count, int offset)
|
||||
public async Task<IActionResult> GetTopPlayersAsync(int count, int offset, long? serverId = null)
|
||||
{
|
||||
return View("_List", await Plugin.Manager.GetTopStats(offset, count));
|
||||
// this prevents empty results when we really want aggregate
|
||||
if (serverId == 0)
|
||||
{
|
||||
serverId = null;
|
||||
}
|
||||
|
||||
var server = Manager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
|
||||
|
||||
if (server != null)
|
||||
{
|
||||
serverId = await StatManager.GetIdForServer(server);
|
||||
}
|
||||
|
||||
var results = await Plugin.Manager.GetTopStats(offset, count, serverId);
|
||||
|
||||
// this returns an empty result so we know to stale the loader
|
||||
if (results.Count == 0 && offset > 0)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return View("Components/TopPlayers/_List", results);
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetMessageAsync(int serverId, DateTime when)
|
||||
public async Task<IActionResult> GetMessageAsync(int serverId, long when)
|
||||
{
|
||||
var whenUpper = when.AddMinutes(5);
|
||||
var whenLower = when.AddMinutes(-5);
|
||||
var whenTime = DateTime.FromFileTimeUtc(when);
|
||||
var whenUpper = whenTime.AddMinutes(5);
|
||||
var whenLower = whenTime.AddMinutes(-5);
|
||||
|
||||
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
||||
{
|
||||
@ -38,20 +66,36 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
where message.ServerId == serverId
|
||||
where message.TimeSent >= whenLower
|
||||
where message.TimeSent <= whenUpper
|
||||
select new SharedLibraryCore.Dtos.ChatInfo()
|
||||
select new ChatInfo()
|
||||
{
|
||||
ClientId = message.ClientId,
|
||||
Message = message.Message,
|
||||
Name = message.Client.CurrentAlias.Name,
|
||||
Time = message.TimeSent
|
||||
Time = message.TimeSent,
|
||||
ServerGame = message.Server.GameName ?? Server.Game.IW4
|
||||
};
|
||||
|
||||
#if DEBUG == true
|
||||
var messagesSql = iqMessages.ToSql();
|
||||
#endif
|
||||
|
||||
var messages = await iqMessages.ToListAsync();
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
if (message.Message.IsQuickMessage())
|
||||
{
|
||||
try
|
||||
{
|
||||
var quickMessages = Manager.GetApplicationSettings().Configuration()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == message.ServerGame);
|
||||
message.Message = quickMessages.Messages[message.Message.Substring(1)];
|
||||
message.IsQuickMessage = true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
return View("_MessageContext", messages);
|
||||
}
|
||||
}
|
||||
@ -62,16 +106,30 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
{
|
||||
using (var ctx = new SharedLibraryCore.Database.DatabaseContext(true))
|
||||
{
|
||||
var penaltyInfo = await ctx.Set<Models.EFACSnapshot>()
|
||||
.Where(s => s.ClientId == clientId)
|
||||
int linkId = await ctx.Clients
|
||||
.Where(_client => _client.ClientId == clientId)
|
||||
.Select(_client => _client.AliasLinkId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
var clientIds = await ctx.Clients.Where(_client => _client.AliasLinkId == linkId)
|
||||
.Select(_client => _client.ClientId)
|
||||
.ToListAsync();
|
||||
|
||||
var iqPenaltyInfo = ctx.Set<Models.EFACSnapshot>()
|
||||
.Where(s => clientIds.Contains(s.ClientId))
|
||||
.Include(s => s.LastStrainAngle)
|
||||
.Include(s => s.HitOrigin)
|
||||
.Include(s => s.HitDestination)
|
||||
.Include(s => s.CurrentViewAngle)
|
||||
.Include(s => s.PredictedViewAngles)
|
||||
.OrderBy(s => s.When)
|
||||
.ThenBy(s => s.Hits)
|
||||
.ToListAsync();
|
||||
.ThenBy(s => s.Hits);
|
||||
|
||||
#if DEBUG == true
|
||||
var sql = iqPenaltyInfo.ToSql();
|
||||
#endif
|
||||
|
||||
var penaltyInfo = await iqPenaltyInfo.ToListAsync();
|
||||
|
||||
return View("_PenaltyInfo", penaltyInfo);
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@ -82,13 +81,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count)
|
||||
public async Task<List<TopStatsInfo>> GetTopStats(int start, int count, long? serverId = null)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
// setup the query for the clients within the given rating range
|
||||
var iqClientRatings = (from rating in context.Set<EFRating>()
|
||||
.Where(GetRankingFunc())
|
||||
.Where(GetRankingFunc(serverId))
|
||||
select new
|
||||
{
|
||||
rating.RatingHistory.ClientId,
|
||||
@ -113,7 +112,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var iqRatingInfo = from rating in context.Set<EFRating>()
|
||||
where clientIds.Contains(rating.RatingHistory.ClientId)
|
||||
where rating.ServerId == null
|
||||
where rating.ServerId == serverId
|
||||
select new
|
||||
{
|
||||
rating.Ranking,
|
||||
@ -155,6 +154,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var finished = topPlayers.Select(s => new TopStatsInfo()
|
||||
{
|
||||
ClientId = s.ClientId,
|
||||
Id = (int?)serverId ?? 0,
|
||||
Deaths = s.Deaths,
|
||||
Kills = s.Kills,
|
||||
KDR = Math.Round(s.KDR, 2),
|
||||
@ -221,13 +221,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
Port = sv.GetPort(),
|
||||
EndPoint = sv.ToString(),
|
||||
ServerId = serverId
|
||||
ServerId = serverId,
|
||||
GameName = sv.GameName
|
||||
};
|
||||
|
||||
server = serverSet.Add(server).Entity;
|
||||
// this doesn't need to be async as it's during initialization
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
|
||||
// we want to set the gamename up if it's never been set, or it changed
|
||||
else if (!server.GameName.HasValue || server.GameName.HasValue && server.GameName.Value != sv.GameName)
|
||||
{
|
||||
server.GameName = sv.GameName;
|
||||
ctx.Entry(server).Property(_prop => _prop.GameName).IsModified = true;
|
||||
ctx.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// check to see if the stats have ever been initialized
|
||||
@ -527,7 +536,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
return;
|
||||
}
|
||||
|
||||
// incase the add palyer event get delayed
|
||||
// incase the add player event get delayed
|
||||
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
|
||||
{
|
||||
await AddPlayer(attacker);
|
||||
@ -561,7 +570,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ctx.Set<EFClientKill>().Add(hit);
|
||||
}
|
||||
|
||||
if (Plugin.Config.Configuration().EnableAntiCheat)
|
||||
if (Plugin.Config.Configuration().EnableAntiCheat && !attacker.IsBot)
|
||||
{
|
||||
if (clientDetection.QueuedHits.Count > Detection.QUEUE_COUNT)
|
||||
{
|
||||
@ -570,7 +579,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
clientDetection.QueuedHits = clientDetection.QueuedHits.OrderBy(_hits => _hits.TimeOffset).ToList();
|
||||
var oldestHit = clientDetection.QueuedHits.First();
|
||||
clientDetection.QueuedHits.RemoveAt(0);
|
||||
ApplyPenalty(clientDetection.ProcessHit(oldestHit, isDamage), clientDetection, attacker, ctx);
|
||||
await ApplyPenalty(clientDetection.ProcessHit(oldestHit, isDamage), clientDetection, attacker, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@ -579,7 +588,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
clientDetection.QueuedHits.Add(hit);
|
||||
}
|
||||
|
||||
ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||
await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
|
||||
}
|
||||
|
||||
ctx.Set<EFHitLocationCount>().UpdateRange(clientStats.HitLocations);
|
||||
@ -597,8 +606,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyPenalty(DetectionPenaltyResult penalty, Detection clientDetection, EFClient attacker, DatabaseContext ctx)
|
||||
async Task ApplyPenalty(DetectionPenaltyResult penalty, Detection clientDetection, EFClient attacker, DatabaseContext ctx)
|
||||
{
|
||||
var penaltyClient = Utilities.IW4MAdminClient(attacker.CurrentServer);
|
||||
switch (penalty.ClientPenalty)
|
||||
{
|
||||
case Penalty.PenaltyType.Ban:
|
||||
@ -606,22 +616,19 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
{
|
||||
break;
|
||||
}
|
||||
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
AdministeredPenalties = new List<EFPenalty>()
|
||||
{
|
||||
new EFPenalty()
|
||||
{
|
||||
AutomatedOffense = penalty.Type == Cheat.Detection.DetectionType.Bone ?
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
}
|
||||
},
|
||||
Level = EFClient.Permission.Console,
|
||||
CurrentServer = attacker.CurrentServer,
|
||||
|
||||
}, false);
|
||||
penaltyClient.AdministeredPenalties = new List<EFPenalty>()
|
||||
{
|
||||
new EFPenalty()
|
||||
{
|
||||
AutomatedOffense = penalty.Type == Detection.DetectionType.Bone ?
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}",
|
||||
}
|
||||
};
|
||||
|
||||
await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], penaltyClient, false).WaitAsync();
|
||||
|
||||
if (clientDetection.Tracker.HasChanges)
|
||||
{
|
||||
SaveTrackedSnapshots(clientDetection, ctx);
|
||||
@ -637,23 +644,17 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
|
||||
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
|
||||
|
||||
attacker.Flag(flagReason, new EFClient()
|
||||
{
|
||||
ClientId = 1,
|
||||
Level = EFClient.Permission.Console,
|
||||
CurrentServer = attacker.CurrentServer,
|
||||
});
|
||||
await attacker.Flag(flagReason, penaltyClient).WaitAsync();
|
||||
|
||||
if (clientDetection.Tracker.HasChanges)
|
||||
{
|
||||
SaveTrackedSnapshots(clientDetection, ctx);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveTrackedSnapshots(Cheat.Detection clientDetection, DatabaseContext ctx)
|
||||
void SaveTrackedSnapshots(Detection clientDetection, DatabaseContext ctx)
|
||||
{
|
||||
// todo: why does this cause duplicate primary key
|
||||
var change = clientDetection.Tracker.GetNextChange();
|
||||
|
@ -2,6 +2,7 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats.Models
|
||||
{
|
||||
@ -13,5 +14,6 @@ namespace IW4MAdmin.Plugins.Stats.Models
|
||||
[Required]
|
||||
public int Port { get; set; }
|
||||
public string EndPoint { get; set; }
|
||||
public Game? GameName { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
@ -26,7 +27,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
public string Author => "RaidMax";
|
||||
|
||||
public static StatManager Manager { get; private set; }
|
||||
private IManager ServerManager;
|
||||
public static IManager ServerManager;
|
||||
public static BaseConfigurationHandler<StatsConfiguration> Config { get; private set; }
|
||||
|
||||
public async Task OnEventAsync(GameEvent E, Server S)
|
||||
@ -51,7 +52,6 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
{
|
||||
await Manager.AddMessageAsync(E.Origin.ClientId, await StatManager.GetIdForServer(E.Owner), E.Data);
|
||||
}
|
||||
|
||||
break;
|
||||
case GameEvent.EventType.MapChange:
|
||||
Manager.SetTeamBased(await StatManager.GetIdForServer(E.Owner), E.Owner.Gametype != "dm");
|
||||
@ -77,28 +77,72 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
break;
|
||||
case GameEvent.EventType.ScriptKill:
|
||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 14)
|
||||
if (killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target))
|
||||
{
|
||||
// this treats "world" damage as self damage
|
||||
if (E.Origin.ClientId <= 1)
|
||||
{
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
if (E.Target.ClientId <= 1)
|
||||
{
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
|
||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||
}
|
||||
break;
|
||||
case GameEvent.EventType.Kill:
|
||||
if (!E.Owner.CustomCallback)
|
||||
if (!E.Owner.CustomCallback && !ShouldIgnoreEvent(E.Origin, E.Target))
|
||||
{
|
||||
// this treats "world" damage as self damage
|
||||
if (E.Origin.ClientId <= 1)
|
||||
{
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
if (E.Target.ClientId <= 1)
|
||||
{
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
|
||||
await Manager.AddStandardKill(E.Origin, E.Target);
|
||||
}
|
||||
break;
|
||||
case GameEvent.EventType.Damage:
|
||||
if (!E.Owner.CustomCallback)
|
||||
if (!E.Owner.CustomCallback && !ShouldIgnoreEvent(E.Origin, E.Target))
|
||||
{
|
||||
// this treats "world" damage as self damage
|
||||
if (E.Origin.ClientId <= 1)
|
||||
{
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
if (E.Target.ClientId <= 1)
|
||||
{
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
|
||||
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, await StatManager.GetIdForServer(E.Owner));
|
||||
}
|
||||
break;
|
||||
case GameEvent.EventType.ScriptDamage:
|
||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 14)
|
||||
if (killInfo.Length >= 14 && !ShouldIgnoreEvent(E.Origin, E.Target))
|
||||
{
|
||||
// this treats "world" damage as self damage
|
||||
if (E.Origin.ClientId <= 1)
|
||||
{
|
||||
E.Origin = E.Target;
|
||||
}
|
||||
|
||||
if (E.Target.ClientId <= 1)
|
||||
{
|
||||
E.Target = E.Origin;
|
||||
}
|
||||
|
||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, await StatManager.GetIdForServer(E.Owner), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13], killInfo[14], killInfo[15]);
|
||||
}
|
||||
@ -124,8 +168,13 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
"/Stats/TopPlayersAsync");
|
||||
|
||||
// meta data info
|
||||
async Task<List<ProfileMeta>> getStats(int clientId)
|
||||
async Task<List<ProfileMeta>> getStats(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
IList<EFClientStatistics> clientStats;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
@ -145,39 +194,63 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_RANKING"],
|
||||
Value = "#" + await StatManager.GetClientOverallRanking(clientId),
|
||||
Value = "#" + (await StatManager.GetClientOverallRanking(clientId)).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 0,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"],
|
||||
Value = kills
|
||||
Value = kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 1,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_DEATHS"],
|
||||
Value = deaths
|
||||
Value = deaths.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 2,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"],
|
||||
Value = kdr
|
||||
Value = kdr.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 3,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"],
|
||||
Value = performance
|
||||
Value = performance.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_META_SPM"],
|
||||
Value = spm
|
||||
Value = spm.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 0,
|
||||
Order = 5,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId)
|
||||
async Task<List<ProfileMeta>> getAnticheatInfo(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
if (count > 1)
|
||||
{
|
||||
return new List<ProfileMeta>();
|
||||
}
|
||||
|
||||
IList<EFClientStatistics> clientStats;
|
||||
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
clientStats = await ctx.Set<EFClientStatistics>()
|
||||
@ -195,21 +268,21 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
|
||||
if (clientStats.Where(cs => cs.HitLocations.Count > 0).FirstOrDefault() != null)
|
||||
{
|
||||
chestRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
chestRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0)
|
||||
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
|
||||
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
||||
|
||||
abdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
abdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(c =>
|
||||
c.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
||||
|
||||
chestAbdomenRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount), 2);
|
||||
chestAbdomenRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_upper).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.torso_lower).HitCount)) * 100.0, 0);
|
||||
|
||||
headRatio = Math.Round(clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.head).HitCount) /
|
||||
headRatio = Math.Round((clientStats.Where(c => c.HitLocations.Count > 0).Sum(cs => cs.HitLocations.First(hl => hl.Location == IW4Info.HitLocation.head).HitCount) /
|
||||
(double)clientStats.Where(c => c.HitLocations.Count > 0)
|
||||
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount)), 2);
|
||||
.Sum(c => c.HitLocations.Where(hl => hl.Location != IW4Info.HitLocation.none).Sum(f => f.HitCount))) * 100.0, 0);
|
||||
|
||||
var validOffsets = clientStats.Where(c => c.HitLocations.Count(hl => hl.HitCount > 0) > 0).SelectMany(hl => hl.HitLocations);
|
||||
hitOffsetAverage = validOffsets.Sum(o => o.HitCount * o.HitOffsetAverage) / (double)validOffsets.Sum(o => o.HitCount);
|
||||
@ -219,104 +292,172 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
{
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Chest Ratio",
|
||||
Value = chestRatio,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 1",
|
||||
Value = chestRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 0,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM1"],
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Abdomen Ratio",
|
||||
Value = abdomenRatio,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 2",
|
||||
Value = abdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 1,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM2"],
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Chest To Abdomen Ratio",
|
||||
Value = chestAbdomenRatio,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 3",
|
||||
Value = chestAbdomenRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 2,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM3"],
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Headshot Ratio",
|
||||
Value = headRatio,
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 4",
|
||||
Value = headRatio.ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)) + '%',
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 3,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM4"],
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Hit Offset Average",
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 5",
|
||||
// todo: make sure this is wrapped somewhere else
|
||||
Value = $"{Math.Round(((float)hitOffsetAverage), 4).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName))}°",
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 4,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM5"],
|
||||
Sensitive = true
|
||||
},
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = "Max Strain",
|
||||
Value = Math.Round(maxStrain, 3),
|
||||
Key = $"{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_META_AC_METRIC"]} 6",
|
||||
Value = Math.Round(maxStrain, 3).ToString(new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Type = ProfileMeta.MetaType.Information,
|
||||
Column = 2,
|
||||
Order = 5,
|
||||
Extra = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_CLIENT_TITLE_ACM6"],
|
||||
Sensitive = true
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async Task<List<ProfileMeta>> getMessages(int clientId)
|
||||
async Task<List<ProfileMeta>> getMessages(int clientId, int offset, int count, DateTime? startAt)
|
||||
{
|
||||
if (count <= 1)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
return new List<ProfileMeta>
|
||||
{
|
||||
new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||
Value = (await ctx.Set<EFClientMessage>()
|
||||
.CountAsync(_message => _message.ClientId == clientId))
|
||||
.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName)),
|
||||
Column = 1,
|
||||
Order= 4,
|
||||
Type = ProfileMeta.MetaType.Information
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
List<ProfileMeta> messageMeta;
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var messages = ctx.Set<EFClientMessage>().Where(m => m.ClientId == clientId);
|
||||
var messages = ctx.Set<EFClientMessage>()
|
||||
.Where(m => m.ClientId == clientId)
|
||||
.Where(_message => _message.TimeSent < startAt)
|
||||
.OrderByDescending(_message => _message.TimeSent)
|
||||
.Skip(offset)
|
||||
.Take(count);
|
||||
|
||||
messageMeta = await messages.Select(m => new ProfileMeta()
|
||||
{
|
||||
Key = "EventMessage",
|
||||
Value = m.Message,
|
||||
Key = null,
|
||||
Value = new { m.Message, m.Server.GameName },
|
||||
When = m.TimeSent,
|
||||
Extra = m.ServerId.ToString()
|
||||
Extra = m.ServerId.ToString(),
|
||||
Type = ProfileMeta.MetaType.ChatMessage
|
||||
}).ToListAsync();
|
||||
}
|
||||
|
||||
messageMeta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_MESSAGES"],
|
||||
Value = messageMeta.Count
|
||||
});
|
||||
foreach (var message in messageMeta)
|
||||
{
|
||||
if ((message.Value.Message as string).IsQuickMessage())
|
||||
{
|
||||
try
|
||||
{
|
||||
var quickMessages = ServerManager.GetApplicationSettings().Configuration()
|
||||
.QuickMessages
|
||||
.First(_qm => _qm.Game == message.Value.GameName);
|
||||
message.Value = quickMessages.Messages[(message.Value.Message as string).Substring(1)];
|
||||
message.Type = ProfileMeta.MetaType.QuickMessage;
|
||||
}
|
||||
catch
|
||||
{
|
||||
message.Value = message.Value.Message;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
message.Value = message.Value.Message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return messageMeta;
|
||||
}
|
||||
|
||||
MetaService.AddMeta(getStats);
|
||||
|
||||
if (Config.Configuration().EnableAntiCheat)
|
||||
{
|
||||
MetaService.AddMeta(getAnticheatInfo);
|
||||
MetaService.AddRuntimeMeta(getAnticheatInfo);
|
||||
}
|
||||
|
||||
MetaService.AddMeta(getMessages);
|
||||
MetaService.AddRuntimeMeta(getStats);
|
||||
MetaService.AddRuntimeMeta(getMessages);
|
||||
|
||||
string totalKills(Server server)
|
||||
async Task<string> totalKills(Server server)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
long kills = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalKills);
|
||||
return kills.ToString("#,##0");
|
||||
long kills = await ctx.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalKills);
|
||||
return kills.ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
||||
}
|
||||
}
|
||||
|
||||
string totalPlayTime(Server server)
|
||||
async Task<string> totalPlayTime(Server server)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
long playTime = ctx.Set<EFServerStatistics>().Where(s => s.Active).Sum(s => s.TotalPlayTime);
|
||||
return (playTime / 3600.0).ToString("#,##0");
|
||||
long playTime = await ctx.Set<EFServerStatistics>().Where(s => s.Active).SumAsync(s => s.TotalPlayTime);
|
||||
return (playTime / 3600.0).ToString("#,##0", new System.Globalization.CultureInfo(Utilities.CurrentLocalization.LocalizationName));
|
||||
}
|
||||
}
|
||||
|
||||
string topStats(Server s)
|
||||
async Task<string> topStats(Server s)
|
||||
{
|
||||
return String.Join(Environment.NewLine, Commands.TopStats.GetTopStats(s).Result);
|
||||
return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s));
|
||||
}
|
||||
|
||||
string mostPlayed(Server s)
|
||||
async Task<string> mostPlayed(Server s)
|
||||
{
|
||||
return String.Join(Environment.NewLine, Commands.MostPlayed.GetMostPlayed(s).Result);
|
||||
return string.Join(Environment.NewLine, await Commands.MostPlayed.GetMostPlayed(s));
|
||||
}
|
||||
|
||||
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", totalKills));
|
||||
@ -325,7 +466,6 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
manager.GetMessageTokens().Add(new MessageToken("MOSTPLAYED", mostPlayed));
|
||||
|
||||
ServerManager = manager;
|
||||
|
||||
Manager = new StatManager(manager);
|
||||
}
|
||||
|
||||
@ -341,5 +481,17 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
await Manager.Sync(sv);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the event should be ignored
|
||||
/// (If the client id or target id is not a real client or the target/origin is a bot and ignore bots is turned on)
|
||||
/// </summary>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
private bool ShouldIgnoreEvent(EFClient origin, EFClient target)
|
||||
{
|
||||
return ((origin.ClientId <= 1 && target.ClientId <= 1) || (target.IsBot || origin.IsBot) && ServerManager.GetApplicationSettings().Configuration().IgnoreBots);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
28
Plugins/Stats/ViewComponents/TopPlayersViewComponent.cs
Normal file
28
Plugins/Stats/ViewComponents/TopPlayersViewComponent.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using IW4MAdmin.Plugins.Stats;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Stats.ViewComponents
|
||||
{
|
||||
public class TopPlayersViewComponent : ViewComponent
|
||||
{
|
||||
public async Task<IViewComponentResult> InvokeAsync(int count, int offset, long? serverId = null)
|
||||
{
|
||||
if (serverId == 0)
|
||||
{
|
||||
serverId = null;
|
||||
}
|
||||
|
||||
var server = Plugin.ServerManager.GetServers().FirstOrDefault(_server => _server.EndPoint == serverId);
|
||||
|
||||
if (server != null)
|
||||
{
|
||||
serverId = await StatManager.GetIdForServer(server);
|
||||
}
|
||||
|
||||
return View("_List", await Plugin.Manager.GetTopStats(offset, count, serverId));
|
||||
}
|
||||
}
|
||||
}
|
@ -56,10 +56,10 @@ namespace Tests
|
||||
var warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
||||
Assert.True((client.Warnings == 1 ||
|
||||
warnEvent.Failed) &&
|
||||
Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
||||
"warning did not get applied");
|
||||
//Assert.True((client.Warnings == 1 ||
|
||||
// warnEvent.Failed) &&
|
||||
// Manager.GetPenaltyService().GetClientPenaltiesAsync(client.ClientId).Result.Count(p => p.Type == Penalty.PenaltyType.Warning) == 1,
|
||||
// "warning did not get applied");
|
||||
|
||||
warnEvent = client.Warn("test warn", new EFClient() { ClientId = 1, Level = EFClient.Permission.Banned, CurrentServer = client.CurrentServer });
|
||||
warnEvent.OnProcessed.Wait();
|
||||
|
@ -18,7 +18,7 @@ namespace Tests
|
||||
{
|
||||
File.WriteAllText("test_mp.log", "test_log_file");
|
||||
|
||||
IW4MAdmin.Application.Localization.Configure.Initialize("en-US");
|
||||
//IW4MAdmin.Application.Localization.Configure.Initialize("en-US");
|
||||
|
||||
Manager = ApplicationManager.GetInstance();
|
||||
|
||||
@ -41,7 +41,7 @@ namespace Tests
|
||||
Maps = new List<MapConfiguration>(),
|
||||
RConPollRate = 10000
|
||||
};
|
||||
Manager.ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("test.json");
|
||||
Manager.ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("test");
|
||||
Manager.ConfigHandler.Set(config);
|
||||
|
||||
Manager.Init().Wait();
|
||||
|
@ -34,7 +34,7 @@ namespace Tests
|
||||
[Fact]
|
||||
public void AreCommandAliasesUnique()
|
||||
{
|
||||
var mgr = IW4MAdmin.Application.Program.ServerManager;
|
||||
var mgr = Program.ServerManager;
|
||||
bool test = mgr.GetCommands().Count == mgr.GetCommands().Select(c => c.Alias).Distinct().Count();
|
||||
|
||||
Assert.True(test, "command aliases are not unique");
|
||||
|
@ -13,8 +13,12 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,8 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<RazorCompileOnBuild>true</RazorCompileOnBuild>
|
||||
<ResolvedRazorCompileToolset>RazorSdk</ResolvedRazorCompileToolset>
|
||||
<RazorCompileOnBuild Condition="'$(CONFIG)'!='Debug'">true</RazorCompileOnBuild>
|
||||
<RazorCompiledOnPublish Condition="'$(CONFIG)'!='Debug'">true</RazorCompiledOnPublish>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -14,11 +15,9 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<Content Update="Views\_ViewImports.cshtml">
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
|
@ -25,6 +25,11 @@
|
||||
return "0_no-place/menu_div_no_place.png";
|
||||
}
|
||||
}
|
||||
|
||||
@if (Model.Count == 0)
|
||||
{
|
||||
<div class="p-2 text-center">@SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_NOQUALIFY"]</div>
|
||||
}
|
||||
@foreach (var stat in Model)
|
||||
{
|
||||
<div class="row ml-0 mr-0 pt-2 pb-2">
|
||||
@ -57,7 +62,7 @@
|
||||
<span class="text-muted">@loc["WEBFRONT_PROFILE_LSEEN"]</span><span class="text-primary"> @stat.LastSeen </span><span class="text-muted">@loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]</span>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 client-rating-graph" id="rating_history_@stat.ClientId" data-history="@Html.Raw(Json.Serialize(stat.PerformanceHistory))">
|
||||
<div class="col-md-6 client-rating-graph" id="rating_history_@(stat.ClientId + "_" + stat.Id)" data-history="@Html.Raw(Json.Serialize(stat.PerformanceHistory))">
|
||||
|
||||
</div>
|
||||
|
@ -1,14 +1,32 @@
|
||||
@model List<IW4MAdmin.Plugins.Stats.Web.Dtos.TopStatsInfo>
|
||||
<h4 class="pb-2 text-center ">@ViewBag.Title</h4>
|
||||
<ul class="nav nav-tabs border-top border-bottom nav-fill row" role="tablist" id="stats_top_players">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active top-players-link" href="#server_0" role="tab" data-toggle="tab" aria-selected="true" data-serverid="0">All Servers</a>
|
||||
</li>
|
||||
|
||||
<div id="stats_top_players" class="striped border-top border-bottom">
|
||||
@await Html.PartialAsync("_List", Model)
|
||||
@foreach (var server in ViewBag.Servers)
|
||||
{
|
||||
<li class="nav-item ">
|
||||
<a class="nav-link top-players-link" href="#server_@server.ID" role="tab" data-toggle="tab" aria-selected="false" data-serverid="@server.ID">@server.Name</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<div class="tab-content border-bottom row">
|
||||
<div role="tabpanel" class="tab-pane active striped flex-fill" id="server_0">
|
||||
@await Component.InvokeAsync("TopPlayers", new { count = 10, offset = 0 })
|
||||
</div>
|
||||
|
||||
@foreach (var server in ViewBag.Servers)
|
||||
{
|
||||
<div role="tabpanel" class="tab-pane striped flex-fill" id="server_@server.ID">
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@section scripts {
|
||||
@section scripts
|
||||
{
|
||||
<environment include="Development">
|
||||
<script type="text/javascript" src="~/js/loader.js"></script>
|
||||
<script type="text/javascript" src="~/js/stats.js"></script>
|
||||
</environment>
|
||||
<script>initLoader('/Stats/GetTopPlayersAsync', '#stats_top_players', 50);</script>
|
||||
<script>initLoader('/Stats/GetTopPlayersAsync', '#server_0', 10);</script>
|
||||
}
|
||||
|
@ -3,10 +3,12 @@
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<div class="client-message-context bg-dark p-2 mt-2 mb-2 border-top border-bottom">
|
||||
<h5>@Model.First().Time.ToString()</h5>
|
||||
@foreach (var message in Model)
|
||||
{
|
||||
<span class="text-white">@Html.ActionLink(@message.Name, "ProfileAsync", "Client", new { id = message.ClientId})</span><span> — @message.Message</span><br />
|
||||
}
|
||||
<div class="client-message-context">
|
||||
<h5 class="bg-primary pt-2 pb-2 pl-3 mb-0 mt-2 text-white">@Model.First().Time.ToString()</h5>
|
||||
<div class="bg-dark p-3 mb-2 border-bottom">
|
||||
@foreach (var message in Model)
|
||||
{
|
||||
<span class="text-white">@message.Name</span><span> — <span class="@(message.IsQuickMessage ? "font-italic" : "")">@message.Message</span></span><br />
|
||||
}
|
||||
</div>
|
||||
</div>
|
1
Plugins/Web/StatsWeb/Views/_ViewImports.cshtml
Normal file
1
Plugins/Web/StatsWeb/Views/_ViewImports.cshtml
Normal file
@ -0,0 +1 @@
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@ -1,6 +1,5 @@
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -23,7 +22,9 @@ namespace SharedLibraryCore.Commands
|
||||
foreach (Command cmd in Manager.GetCommands())
|
||||
{
|
||||
if (cmd.Name == CommandString.ToLower() || cmd.Alias == CommandString.ToLower())
|
||||
{
|
||||
C = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
if (C == null)
|
||||
@ -34,6 +35,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
E.Data = E.Data.RemoveWords(1);
|
||||
String[] Args = E.Data.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
// todo: the code below can be cleaned up
|
||||
|
||||
if (E.Origin.Level < C.Permission)
|
||||
{
|
||||
@ -48,31 +50,36 @@ namespace SharedLibraryCore.Commands
|
||||
throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\"");
|
||||
}
|
||||
|
||||
if (C.RequiresTarget || Args.Length > 0)
|
||||
if (C.RequiresTarget)
|
||||
{
|
||||
if (!Int32.TryParse(Args[0], out int cNum))
|
||||
cNum = -1;
|
||||
|
||||
if (Args[0][0] == '@') // user specifying target by database ID
|
||||
if (Args.Length > 0)
|
||||
{
|
||||
int dbID = -1;
|
||||
int.TryParse(Args[0].Substring(1, Args[0].Length - 1), out dbID);
|
||||
|
||||
var found = await Manager.GetClientService().Get(dbID);
|
||||
if (found != null)
|
||||
if (!Int32.TryParse(Args[0], out int cNum))
|
||||
{
|
||||
E.Target = found;
|
||||
E.Target.CurrentServer = E.Owner;
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
cNum = -1;
|
||||
}
|
||||
}
|
||||
|
||||
else if (Args[0].Length < 3 && cNum > -1 && cNum < E.Owner.MaxClients) // user specifying target by client num
|
||||
{
|
||||
if (E.Owner.Clients[cNum] != null)
|
||||
if (Args[0][0] == '@') // user specifying target by database ID
|
||||
{
|
||||
E.Target = E.Owner.Clients[cNum];
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
int dbID = -1;
|
||||
int.TryParse(Args[0].Substring(1, Args[0].Length - 1), out dbID);
|
||||
|
||||
var found = await Manager.GetClientService().Get(dbID);
|
||||
if (found != null)
|
||||
{
|
||||
E.Target = found;
|
||||
E.Target.CurrentServer = E.Owner;
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
}
|
||||
}
|
||||
|
||||
else if (Args[0].Length < 3 && cNum > -1 && cNum < E.Owner.MaxClients) // user specifying target by client num
|
||||
{
|
||||
if (E.Owner.Clients[cNum] != null)
|
||||
{
|
||||
E.Target = E.Owner.Clients[cNum];
|
||||
E.Data = String.Join(" ", Args.Skip(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,7 +110,7 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Target == null && C.RequiresTarget) // Find active player as single word
|
||||
if (E.Target == null && C.RequiresTarget && Args.Length > 0) // Find active player as single word
|
||||
{
|
||||
matchingPlayers = E.Owner.GetClientByName(Args[0]);
|
||||
if (matchingPlayers.Count > 1)
|
||||
@ -115,6 +122,7 @@ namespace SharedLibraryCore.Commands
|
||||
}
|
||||
throw new CommandException($"{E.Origin} had multiple players found for {C.Name}");
|
||||
}
|
||||
|
||||
else if (matchingPlayers.Count == 1)
|
||||
{
|
||||
E.Target = matchingPlayers.First();
|
||||
@ -141,6 +149,7 @@ namespace SharedLibraryCore.Commands
|
||||
throw new CommandException($"{E.Origin} specified invalid player for \"{C.Name}\"");
|
||||
}
|
||||
}
|
||||
|
||||
E.Data = E.Data.Trim();
|
||||
return C;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Objects;
|
||||
using SharedLibraryCore.Services;
|
||||
using System;
|
||||
@ -18,7 +17,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CQuit : Command
|
||||
{
|
||||
public CQuit() :
|
||||
base("quit", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_QUIT_DESC"], "q", EFClient.Permission.Owner, false)
|
||||
base("quit", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_QUIT_DESC"], "q", EFClient.Permission.Owner, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -30,32 +29,15 @@ namespace SharedLibraryCore.Commands
|
||||
public class COwner : Command
|
||||
{
|
||||
public COwner() :
|
||||
base("owner", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_DESC"], "iamgod", EFClient.Permission.User, false)
|
||||
base("owner", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_DESC"], "iamgod", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
|
||||
if (await (E.Owner.Manager.GetClientService() as ClientService).GetOwnerCount() == 0 &&
|
||||
!E.Origin.SetLevel(EFClient.Permission.Owner, Utilities.IW4MAdminClient(E.Owner)).Failed)
|
||||
{
|
||||
var oldPermission = E.Origin.Level;
|
||||
E.Origin.Level = EFClient.Permission.Owner;
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]);
|
||||
await E.Owner.Manager.GetClientService().Update(E.Origin);
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ChangePermission,
|
||||
Origin = E.Origin,
|
||||
Target = E.Origin,
|
||||
Owner = E.Owner,
|
||||
Extra = new Change()
|
||||
{
|
||||
PreviousValue = oldPermission.ToString(),
|
||||
NewValue = E.Origin.Level.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -67,7 +49,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWarn : Command
|
||||
{
|
||||
public CWarn() :
|
||||
base("warn", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_DESC"], "w", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
base("warn", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_DESC"], "w", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -86,7 +68,7 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
if (E.Target.Warn(E.Data, E.Origin).Failed)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"]} {E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -96,7 +78,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWarnClear : Command
|
||||
{
|
||||
public CWarnClear() :
|
||||
base("warnclear", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_DESC"], "wc", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
base("warnclear", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_DESC"], "wc", EFClient.Permission.Trusted, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -110,7 +92,7 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
if (!E.Target.WarnClear(E.Origin).Failed)
|
||||
{
|
||||
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"]} {E.Target.Name}");
|
||||
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -120,7 +102,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CKick : Command
|
||||
{
|
||||
public CKick() :
|
||||
base("kick", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_DESC"], "k", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("kick", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_DESC"], "k", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -138,15 +120,15 @@ namespace SharedLibraryCore.Commands
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
var _ = !(await E.Target.Kick(E.Data, E.Origin).WaitAsync()).Failed ?
|
||||
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}") :
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"].FormatExt(E.Target.Name)) :
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
}
|
||||
|
||||
public class CSay : Command
|
||||
{
|
||||
public CSay() :
|
||||
base("say", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SAY_DESC"], "s", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
base("say", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SAY_DESC"], "s", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -166,7 +148,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CTempBan : Command
|
||||
{
|
||||
public CTempBan() :
|
||||
base("tempban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_DESC"], "tb", EFClient.Permission.Administrator, true, new CommandArgument[]
|
||||
base("tempban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_DESC"], "tb", EFClient.Permission.Administrator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -204,8 +186,8 @@ namespace SharedLibraryCore.Commands
|
||||
else
|
||||
{
|
||||
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ?
|
||||
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") :
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(E.Target, length.TimeSpanText())) :
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,7 +196,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CBan : Command
|
||||
{
|
||||
public CBan() :
|
||||
base("ban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_DESC"], "b", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
base("ban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_DESC"], "b", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -232,15 +214,15 @@ namespace SharedLibraryCore.Commands
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
var _ = !(await E.Target.Ban(E.Data, E.Origin, false).WaitAsync()).Failed ?
|
||||
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"]}") :
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"]} {E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"].FormatExt(E.Target.Name)) :
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
}
|
||||
|
||||
public class CUnban : Command
|
||||
{
|
||||
public CUnban() :
|
||||
base("unban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_DESC"], "ub", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
base("unban", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_DESC"], "ub", EFClient.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -261,11 +243,11 @@ namespace SharedLibraryCore.Commands
|
||||
if (penalties.Where(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan).FirstOrDefault() != null)
|
||||
{
|
||||
await E.Target.Unban(E.Data, E.Origin).WaitAsync();
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"]} {E.Target}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"].FormatExt(E.Target));
|
||||
}
|
||||
else
|
||||
{
|
||||
E.Origin.Tell($"{E.Target} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"]}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"].FormatExt(E.Target));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -273,7 +255,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CWhoAmI : Command
|
||||
{
|
||||
public CWhoAmI() :
|
||||
base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", EFClient.Permission.User, false)
|
||||
base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -288,7 +270,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CList : Command
|
||||
{
|
||||
public CList() :
|
||||
base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", EFClient.Permission.Moderator, false)
|
||||
base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -307,11 +289,11 @@ namespace SharedLibraryCore.Commands
|
||||
// todo: make this better :)
|
||||
if (P.Masked)
|
||||
{
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor( EFClient.Permission.User, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces( EFClient.Permission.SeniorAdmin.ToString().Length - EFClient.Permission.User.ToString().Length));
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(EFClient.Permission.User, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces(EFClient.Permission.SeniorAdmin.ToString().Length - EFClient.Permission.User.ToString().Length));
|
||||
}
|
||||
else
|
||||
{
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(P.Level, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces( EFClient.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
|
||||
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.ConvertLevelToColor(P.Level, P.ClientPermission.Name), P.ClientNumber, P.Name, Utilities.GetSpaces(EFClient.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
|
||||
}
|
||||
|
||||
if (count == 2 || E.Owner.GetClientsAsList().Count == 1)
|
||||
@ -339,7 +321,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CHelp : Command
|
||||
{
|
||||
public CHelp() :
|
||||
base("help", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_DESC"], "h", EFClient.Permission.User, false, new CommandArgument[]
|
||||
base("help", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_DESC"], "h", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -404,7 +386,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFastRestart : Command
|
||||
{
|
||||
public CFastRestart() :
|
||||
base("fastrestart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_DESC"], "fr", EFClient.Permission.Moderator, false)
|
||||
base("fastrestart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_DESC"], "fr", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -420,7 +402,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CMapRotate : Command
|
||||
{
|
||||
public CMapRotate() :
|
||||
base("maprotate", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE_DESC"], "mr", EFClient.Permission.Administrator, false)
|
||||
base("maprotate", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE_DESC"], "mr", EFClient.Permission.Administrator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -437,7 +419,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CSetLevel : Command
|
||||
{
|
||||
public CSetLevel() :
|
||||
base("setlevel", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DESC"], "sl", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("setlevel", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DESC"], "sl", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -452,98 +434,89 @@ namespace SharedLibraryCore.Commands
|
||||
})
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
|
||||
EFClient.Permission oldPerm = E.Target.Level;
|
||||
EFClient.Permission newPerm = Utilities.MatchPermission(E.Data);
|
||||
|
||||
if (E.Target == E.Origin)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SELF"]);
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
EFClient.Permission oldPerm = E.Target.Level;
|
||||
EFClient.Permission newPerm = Utilities.MatchPermission(E.Data);
|
||||
|
||||
if (newPerm == EFClient.Permission.Owner &&
|
||||
else if (newPerm == EFClient.Permission.Owner &&
|
||||
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners)
|
||||
{
|
||||
// only one owner is allowed
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]);
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (E.Origin.Level < EFClient.Permission.Owner &&
|
||||
else if (E.Origin.Level < EFClient.Permission.Owner &&
|
||||
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy)
|
||||
{
|
||||
// only the owner is allowed to set levels
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}");
|
||||
return;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (newPerm >= E.Origin.Level)
|
||||
else if (E.Origin.Level <= newPerm &&
|
||||
E.Origin.Level < EFClient.Permission.Owner)
|
||||
{
|
||||
if (E.Origin.Level < EFClient.Permission.Owner)
|
||||
{
|
||||
E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString()));
|
||||
return;
|
||||
}
|
||||
// can't promote a client to higher than your current perms
|
||||
E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString()));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
else if (newPerm > EFClient.Permission.Banned)
|
||||
else if (newPerm > EFClient.Permission.Banned)
|
||||
{
|
||||
var ActiveClient = E.Owner.Manager.GetActiveClients()
|
||||
.FirstOrDefault(p => p.NetworkId == E.Target.NetworkId);
|
||||
|
||||
if (ActiveClient != null)
|
||||
{
|
||||
ActiveClient.Level = newPerm;
|
||||
await E.Owner.Manager.GetClientService().Update(ActiveClient);
|
||||
ActiveClient.SetLevel(newPerm, E.Origin);
|
||||
|
||||
// inform the client that they were promoted
|
||||
// we don't really want to tell them if they're demoted haha
|
||||
if (newPerm > oldPerm)
|
||||
{
|
||||
ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
|
||||
ActiveClient.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"].FormatExt(newPerm));
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
E.Target.Level = newPerm;
|
||||
await E.Owner.Manager.GetClientService().Update(E.Target);
|
||||
E.Target.SetLevel(newPerm, E.Origin);
|
||||
}
|
||||
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Origin = E.Origin,
|
||||
Target = E.Target,
|
||||
Owner = E.Owner,
|
||||
Type = GameEvent.EventType.ChangePermission,
|
||||
Extra = new Change()
|
||||
{
|
||||
PreviousValue = oldPerm.ToString(),
|
||||
NewValue = newPerm.ToString()
|
||||
}
|
||||
};
|
||||
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||
|
||||
var _ = newPerm < oldPerm ?
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DEMOTE_SUCCESS"]} {E.Target.Name}") :
|
||||
E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}");
|
||||
// inform the origin that the client has been updated
|
||||
_ = newPerm < oldPerm ?
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_DEMOTE_SUCCESS"].FormatExt(E.Target.Name)) :
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_FAIL"]);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public class CUsage : Command
|
||||
{
|
||||
public CUsage() :
|
||||
base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", EFClient.Permission.Moderator, false)
|
||||
base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
E.Origin.Tell($"IW4MAdmin {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"]} " + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"].FormatExt(Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1)));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@ -551,14 +524,14 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUptime : Command
|
||||
{
|
||||
public CUptime() :
|
||||
base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", EFClient.Permission.Moderator, false)
|
||||
base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
TimeSpan uptime = DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime;
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
E.Origin.Tell($"IW4M Admin {loc["COMMANDS_UPTIME_TEXT"]} {uptime.Days} {loc["GLOBAL_TIME_DAYS"]}, {uptime.Hours} {loc["GLOBAL_TIME_HOURS"]}, {uptime.Minutes} {loc["GLOBAL_TIME_MINUTES"]}");
|
||||
E.Origin.Tell(loc["COMMANDS_UPTIME_TEXT"].FormatExt(uptime.Days, uptime.Hours, uptime.Minutes));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@ -566,13 +539,13 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListAdmins : Command
|
||||
{
|
||||
public CListAdmins() :
|
||||
base("admins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_DESC"], "a", EFClient.Permission.User, false)
|
||||
base("admins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_DESC"], "a", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public static string OnlineAdmins(Server S)
|
||||
{
|
||||
var onlineAdmins = S.GetClientsAsList()
|
||||
.Where(p => p.Level > EFClient.Permission.Flagged)
|
||||
.Where(p => p.Level > EFClient.Permission.Flagged)
|
||||
.Where(p => !p.Masked)
|
||||
.Select(p => $"[^3{Utilities.ConvertLevelToColor(p.Level, p.ClientPermission.Name)}^7] {p.Name}");
|
||||
|
||||
@ -595,7 +568,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CLoadMap : Command
|
||||
{
|
||||
public CLoadMap() :
|
||||
base("map", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_DESC"], "m", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
base("map", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_DESC"], "m", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -612,14 +585,14 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
|
||||
{
|
||||
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}");
|
||||
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"].FormatExt(m.Alias));
|
||||
await Task.Delay(5000);
|
||||
await E.Owner.LoadMap(m.Name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}");
|
||||
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"].FormatExt(newMap));
|
||||
await Task.Delay(5000);
|
||||
await E.Owner.LoadMap(newMap);
|
||||
}
|
||||
@ -628,7 +601,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFindPlayer : Command
|
||||
{
|
||||
public CFindPlayer() :
|
||||
base("find", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_DESC"], "f", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
base("find", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_DESC"], "f", EFClient.Permission.Administrator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -672,7 +645,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListRules : Command
|
||||
{
|
||||
public CListRules() :
|
||||
base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", EFClient.Permission.User, false)
|
||||
base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -707,7 +680,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CPrivateMessage : Command
|
||||
{
|
||||
public CPrivateMessage() :
|
||||
base("privatemessage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PM_DESC"], "pm", EFClient.Permission.User, true, new CommandArgument[]
|
||||
base("privatemessage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PM_DESC"], "pm", EFClient.Permission.User, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -734,7 +707,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CFlag : Command
|
||||
{
|
||||
public CFlag() :
|
||||
base("flag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_DESC"], "fp", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("flag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_DESC"], "fp", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -755,7 +728,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
if (E.FailReason == GameEvent.EventFailReason.Permission)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
else if (E.FailReason == GameEvent.EventFailReason.Invalid)
|
||||
@ -765,7 +738,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
else
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -776,7 +749,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUnflag : Command
|
||||
{
|
||||
public CUnflag() :
|
||||
base("unflag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_DESC"], "uf", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("unflag", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_DESC"], "uf", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -792,7 +765,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
if (unflagEvent.FailReason == GameEvent.EventFailReason.Permission)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"]} ^5{E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid)
|
||||
@ -802,7 +775,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
else
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"].FormatExt(E.Target.Name));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -814,7 +787,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CReport : Command
|
||||
{
|
||||
public CReport() :
|
||||
base("report", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_DESC"], "rep", EFClient.Permission.User, true, new CommandArgument[]
|
||||
base("report", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_DESC"], "rep", EFClient.Permission.User, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -841,7 +814,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
if (reportEvent.FailReason == GameEvent.EventFailReason.Permission)
|
||||
{
|
||||
commandEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"]} {commandEvent.Target.Name}");
|
||||
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"].FormatExt(commandEvent.Target.Name));
|
||||
}
|
||||
|
||||
else if (reportEvent.FailReason == GameEvent.EventFailReason.Invalid)
|
||||
@ -885,7 +858,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListReports : Command
|
||||
{
|
||||
public CListReports() :
|
||||
base("reports", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_DESC"], "reps", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
base("reports", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_DESC"], "reps", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -922,7 +895,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CMask : Command
|
||||
{
|
||||
public CMask() :
|
||||
base("mask", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_DESC"], "hide", EFClient.Permission.Moderator, false)
|
||||
base("mask", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_DESC"], "hide", EFClient.Permission.Moderator, false)
|
||||
{ }
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
@ -945,7 +918,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CListBanInfo : Command
|
||||
{
|
||||
public CListBanInfo() :
|
||||
base("baninfo", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_DESC"], "bi", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("baninfo", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_DESC"], "bi", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -957,10 +930,8 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
var B = await E.Owner.Manager.GetPenaltyService().GetClientPenaltiesAsync(E.Target.ClientId);
|
||||
|
||||
var penalty = B.FirstOrDefault(b => b.Type > Penalty.PenaltyType.Kick &&
|
||||
(b.Expires == null || b.Expires > DateTime.UtcNow));
|
||||
var existingPenalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId, E.Target.IPAddress);
|
||||
var penalty = existingPenalties.FirstOrDefault(b => b.Type > Penalty.PenaltyType.Kick);
|
||||
|
||||
if (penalty == null)
|
||||
{
|
||||
@ -968,17 +939,23 @@ namespace SharedLibraryCore.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
string timeRemaining = penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires.Value - DateTime.UtcNow).TimeSpanText()} remaining)" : "";
|
||||
string success = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_SUCCESS"];
|
||||
if (penalty.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_SUCCESS"].FormatExt(E.Target.Name, penalty.Offense));
|
||||
}
|
||||
|
||||
E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}");
|
||||
else
|
||||
{
|
||||
string remainingTime = (penalty.Expires.Value - DateTime.UtcNow).TimeSpanText();
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_TB_SUCCESS"].FormatExt(E.Target.Name, penalty.Offense, remainingTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CListAlias : Command
|
||||
{
|
||||
public CListAlias() :
|
||||
base("alias", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_DESC"], "known", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
base("alias", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_DESC"], "known", EFClient.Permission.Moderator, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1012,7 +989,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CExecuteRCON : Command
|
||||
{
|
||||
public CExecuteRCON() :
|
||||
base("rcon", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_DESC"], "rcon", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
base("rcon", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_DESC"], "rcon", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1040,7 +1017,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CPlugins : Command
|
||||
{
|
||||
public CPlugins() :
|
||||
base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", EFClient.Permission.Administrator, false)
|
||||
base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", EFClient.Permission.Administrator, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
@ -1057,19 +1034,19 @@ namespace SharedLibraryCore.Commands
|
||||
public class CIP : Command
|
||||
{
|
||||
public CIP() :
|
||||
base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", EFClient.Permission.User, false)
|
||||
base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", EFClient.Permission.User, false)
|
||||
{ }
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"]} ^5{E.Origin.IPAddressString}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"].FormatExt(E.Origin.IPAddressString));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
public class CPruneAdmins : Command
|
||||
{
|
||||
public CPruneAdmins() : base("prune", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_DESC"], "pa", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
public CPruneAdmins() : base("prune", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_DESC"], "pa", EFClient.Permission.Owner, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1108,19 +1085,19 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
var lastActive = DateTime.UtcNow.AddDays(-inactiveDays);
|
||||
inactiveUsers = await context.Clients
|
||||
.Where(c => c.Level > EFClient.Permission.Flagged && c.Level <= EFClient.Permission.Moderator)
|
||||
.Where(c => c.Level > EFClient.Permission.Flagged && c.Level <= EFClient.Permission.Moderator)
|
||||
.Where(c => c.LastConnection < lastActive)
|
||||
.ToListAsync();
|
||||
inactiveUsers.ForEach(c => c.Level = EFClient.Permission.User);
|
||||
inactiveUsers.ForEach(c => c.SetLevel(EFClient.Permission.User, E.Origin));
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}");
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"].FormatExt(inactiveUsers.Count));
|
||||
}
|
||||
}
|
||||
|
||||
public class CSetPassword : Command
|
||||
{
|
||||
public CSetPassword() : base("setpassword", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETPASSWORD_DESC"], "sp", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
public CSetPassword() : base("setpassword", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETPASSWORD_DESC"], "sp", EFClient.Permission.Moderator, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1154,7 +1131,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CKillServer : Command
|
||||
{
|
||||
public CKillServer() : base("killserver", "kill the game server", "kill", EFClient.Permission.Administrator, false)
|
||||
public CKillServer() : base("killserver", "kill the game server", "kill", EFClient.Permission.Administrator, false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -1201,7 +1178,7 @@ namespace SharedLibraryCore.Commands
|
||||
if (!E.Owner.Throttled)
|
||||
{
|
||||
#if !DEBUG
|
||||
await E.Owner.ExecuteCommandAsync("quit");
|
||||
await E.Owner.ExecuteCommandAsync("quit");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1236,7 +1213,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CPing : Command
|
||||
{
|
||||
public CPing() : base("ping", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_DESC"], "pi", EFClient.Permission.User, false, new CommandArgument[]
|
||||
public CPing() : base("ping", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_DESC"], "pi", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1248,27 +1225,18 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
if (E.Target == null)
|
||||
{
|
||||
if (E.Target == null)
|
||||
{
|
||||
E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
|
||||
}
|
||||
E.Target = E.Owner.GetClientByName(E.Data).FirstOrDefault();
|
||||
}
|
||||
|
||||
if (E.Target == null)
|
||||
{
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"].FormatExt(E.Origin.Ping));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (E.Target == null)
|
||||
{
|
||||
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
|
||||
}
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"].FormatExt(E.Target.Name, E.Target.Ping));
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
@ -1277,7 +1245,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public class CSetGravatar : Command
|
||||
{
|
||||
public CSetGravatar() : base("setgravatar", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_DESC"], "sg", EFClient.Permission.User, false, new CommandArgument[]
|
||||
public CSetGravatar() : base("setgravatar", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_DESC"], "sg", EFClient.Permission.User, false, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
@ -1289,50 +1257,16 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
using (var ctx = new DatabaseContext())
|
||||
var metaSvc = new MetaService();
|
||||
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
var iqMeta = from meta in ctx.EFMeta
|
||||
where meta.ClientId == E.Origin.ClientId
|
||||
where meta.Key == "GravatarEmail"
|
||||
select meta;
|
||||
|
||||
var gravatarMeta = await iqMeta.FirstOrDefaultAsync();
|
||||
|
||||
// gravatar meta has never been added
|
||||
if (gravatarMeta == null)
|
||||
{
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
gravatarMeta = new EFMeta()
|
||||
{
|
||||
Active = true,
|
||||
ClientId = E.Origin.ClientId,
|
||||
Key = "GravatarEmail",
|
||||
Value = string.Concat(md5.ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray())
|
||||
.Select(h => h.ToString("x2"))),
|
||||
};
|
||||
|
||||
ctx.EFMeta.Add(gravatarMeta);
|
||||
await ctx.SaveChangesAsync();
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ctx.EFMeta.Update(gravatarMeta);
|
||||
using (var md5 = MD5.Create())
|
||||
{
|
||||
gravatarMeta.Value = string.Concat(md5.ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray())
|
||||
string gravatarEmail = string.Concat(md5.ComputeHash(E.Data.ToLower().Select(d => Convert.ToByte(d)).ToArray())
|
||||
.Select(h => h.ToString("x2")));
|
||||
gravatarMeta.Updated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_UPDATE"]);
|
||||
}
|
||||
await metaSvc.AddPersistentMeta("GravatarEmail", gravatarEmail, E.Origin);
|
||||
}
|
||||
|
||||
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1341,11 +1275,11 @@ namespace SharedLibraryCore.Commands
|
||||
/// </summary>
|
||||
public class CNextMap : Command
|
||||
{
|
||||
public CNextMap() : base("nextmap", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_DESC"], "nm", EFClient.Permission.User, false) { }
|
||||
public CNextMap() : base("nextmap", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_DESC"], "nm", EFClient.Permission.User, false) { }
|
||||
public static async Task<string> GetNextMap(Server s)
|
||||
{
|
||||
string mapRotation = (await s.GetDvarAsync<string>("sv_mapRotation")).Value.ToLower();
|
||||
var regexMatches = Regex.Matches(mapRotation, @"(gametype +([a-z]{1,4}))? *map ([a-z|_]+)", RegexOptions.IgnoreCase).ToList();
|
||||
string mapRotation = (await s.GetDvarAsync<string>("sv_mapRotation")).Value?.ToLower() ?? "";
|
||||
var regexMatches = Regex.Matches(mapRotation, @"((?:gametype|exec) +(?:([a-z]{1,4})(?:.cfg)?))? *map ([a-z|_|\d]+)", RegexOptions.IgnoreCase).ToList();
|
||||
|
||||
// find the current map in the rotation
|
||||
var currentMap = regexMatches.Where(m => m.Groups[3].ToString() == s.CurrentMap.Name);
|
||||
@ -1355,7 +1289,7 @@ namespace SharedLibraryCore.Commands
|
||||
// no maprotation at all
|
||||
if (regexMatches.Count() == 0)
|
||||
{
|
||||
return $"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_SUCCESS"]} ^5{s.CurrentMap.Alias}/{Utilities.GetLocalizedGametype(s.Gametype)}";
|
||||
return Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_SUCCESS"].FormatExt(s.CurrentMap.Alias, Utilities.GetLocalizedGametype(s.Gametype));
|
||||
}
|
||||
|
||||
// the current map is not in rotation
|
||||
@ -1384,12 +1318,14 @@ namespace SharedLibraryCore.Commands
|
||||
regexMatches[regexMatches.IndexOf(currentMap.First()) + 1] :
|
||||
regexMatches.First();
|
||||
|
||||
nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? nextMap;
|
||||
string nextMapName = nextMapMatch.Groups[3].ToString();
|
||||
|
||||
nextMap = s.Maps.FirstOrDefault(m => m.Name == nextMapMatch.Groups[3].ToString()) ?? new Map() { Alias = nextMapName, Name = nextMapName };
|
||||
string nextGametype = nextMapMatch.Groups[2].ToString().Length == 0 ?
|
||||
Utilities.GetLocalizedGametype(s.Gametype) :
|
||||
Utilities.GetLocalizedGametype(nextMapMatch.Groups[2].ToString());
|
||||
|
||||
return $"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_SUCCESS"]} ^5{nextMap.Alias}/{nextGametype}";
|
||||
return Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_NEXTMAP_SUCCESS"].FormatExt(nextMap.Alias, nextGametype);
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
|
@ -1,38 +1,95 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Configuration.Attributes;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
public class ApplicationConfiguration : IBaseConfiguration
|
||||
{
|
||||
|
||||
[LocalizedDisplayName("SETUP_ENABLE_WEBFRONT")]
|
||||
[ConfiguratinLinked("WebfrontBindUrl", "ManualWebfrontUrl")]
|
||||
public bool EnableWebFront { get; set; }
|
||||
public bool EnableMultipleOwners { get; set; }
|
||||
public bool EnableSteppedHierarchy { get; set; }
|
||||
public bool EnableSocialLink { get; set; }
|
||||
public bool EnableCustomSayName { get; set; }
|
||||
public string CustomSayName { get; set; }
|
||||
public string SocialLinkAddress { get; set; }
|
||||
public string SocialLinkTitle { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BIND_URL")]
|
||||
public string WebfrontBindUrl { get; set; }
|
||||
[ConfigurationOptional]
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MANUAL_URL")]
|
||||
public string ManualWebfrontUrl { get; set; }
|
||||
public string WebfrontUrl => string.IsNullOrEmpty(ManualWebfrontUrl) ? WebfrontBindUrl.Replace("0.0.0.0", "127.0.0.1") : ManualWebfrontUrl;
|
||||
public string CustomParserEncoding { get; set; }
|
||||
public string CustomLocale { get; set; }
|
||||
public string DatabaseProvider { get; set; } = "sqlite";
|
||||
public string ConnectionString { get; set; }
|
||||
public int RConPollRate { get; set; } = 5000;
|
||||
|
||||
[LocalizedDisplayName("SETUP_ENABLE_MULTIOWN")]
|
||||
public bool EnableMultipleOwners { get; set; }
|
||||
[LocalizedDisplayName("SETUP_ENABLE_STEPPEDPRIV")]
|
||||
public bool EnableSteppedHierarchy { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_USE_LOCAL_TRANSLATIONS")]
|
||||
public bool UseLocalTranslations { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_IGNORE_BOTS")]
|
||||
public bool IgnoreBots { get; set; }
|
||||
public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0);
|
||||
|
||||
[ConfiguratinLinked("CustomSayName")]
|
||||
[LocalizedDisplayName("SETUP_ENABLE_CUSTOMSAY")]
|
||||
public bool EnableCustomSayName { get; set; }
|
||||
[LocalizedDisplayName("SETUP_SAY_NAME")]
|
||||
public string CustomSayName { get; set; }
|
||||
|
||||
[LocalizedDisplayName("SETUP_DISPLAY_SOCIAL")]
|
||||
[ConfiguratinLinked("SocialLinkAddress", "SocialLinkTitle")]
|
||||
public bool EnableSocialLink { get; set; }
|
||||
[LocalizedDisplayName("SETUP_SOCIAL_LINK")]
|
||||
public string SocialLinkAddress { get; set; }
|
||||
[LocalizedDisplayName("SETUP_SOCIAL_TITLE")]
|
||||
public string SocialLinkTitle { get; set; }
|
||||
|
||||
[LocalizedDisplayName("SETUP_USE_CUSTOMENCODING")]
|
||||
[ConfiguratinLinked("CustomParserEncoding")]
|
||||
public bool EnableCustomParserEncoding { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENCODING")]
|
||||
public string CustomParserEncoding { get; set; }
|
||||
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_ENABLE_WHITELIST")]
|
||||
[ConfiguratinLinked("WebfrontConnectionWhitelist")]
|
||||
public bool EnableWebfrontConnectionWhitelist { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_WHITELIST_LIST")]
|
||||
public List<string> WebfrontConnectionWhitelist { get; set; }
|
||||
public string Id { get; set; }
|
||||
public List<ServerConfiguration> Servers { get; set; }
|
||||
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_LOCALE")]
|
||||
[ConfiguratinLinked("CustomLocale")]
|
||||
public bool EnableCustomLocale { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_CUSTOM_LOCALE")]
|
||||
public string CustomLocale { get; set; }
|
||||
|
||||
[ConfigurationOptional]
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_DB_PROVIDER")]
|
||||
public string DatabaseProvider { get; set; } = "sqlite";
|
||||
[ConfigurationOptional]
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_CONNECTION_STRING")]
|
||||
public string ConnectionString { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_RCON_POLLRATE")]
|
||||
public int RConPollRate { get; set; } = 5000;
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MAX_TB")]
|
||||
public TimeSpan MaximumTempBanTime { get; set; } = new TimeSpan(24 * 30, 0, 0);
|
||||
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGE_PERIOD")]
|
||||
public int AutoMessagePeriod { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_AUTOMESSAGES")]
|
||||
public List<string> AutoMessages { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_GLOBAL_RULES")]
|
||||
public List<string> GlobalRules { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_DISALLOWED_NAMES")]
|
||||
public List<string> DisallowedClientNames { get; set; }
|
||||
[UIHint("ServerConfiguration")]
|
||||
public List<ServerConfiguration> Servers { get; set; }
|
||||
|
||||
|
||||
[ConfigurationIgnore]
|
||||
public string Id { get; set; }
|
||||
[ConfigurationIgnore]
|
||||
public List<MapConfiguration> Maps { get; set; }
|
||||
[ConfigurationIgnore]
|
||||
public List<QuickMessageConfiguration> QuickMessages { get; set; }
|
||||
[ConfigurationIgnore]
|
||||
public string WebfrontUrl => string.IsNullOrEmpty(ManualWebfrontUrl) ? WebfrontBindUrl?.Replace("0.0.0.0", "127.0.0.1") : ManualWebfrontUrl;
|
||||
|
||||
public IBaseConfiguration Generate()
|
||||
{
|
||||
@ -45,12 +102,17 @@ namespace SharedLibraryCore.Configuration
|
||||
EnableCustomSayName = Utilities.PromptBool(loc["SETUP_ENABLE_CUSTOMSAY"]);
|
||||
|
||||
bool useCustomParserEncoding = Utilities.PromptBool(loc["SETUP_USE_CUSTOMENCODING"]);
|
||||
CustomParserEncoding = useCustomParserEncoding ? Utilities.PromptString(loc["SETUP_ENCODING_STRING"]) : "windows-1252";
|
||||
if (useCustomParserEncoding)
|
||||
{
|
||||
CustomParserEncoding = Utilities.PromptString(loc["SETUP_ENCODING_STRING"]);
|
||||
}
|
||||
|
||||
WebfrontBindUrl = "http://0.0.0.0:1624";
|
||||
|
||||
if (EnableCustomSayName)
|
||||
{
|
||||
CustomSayName = Utilities.PromptString(loc["SETUP_SAY_NAME"]);
|
||||
}
|
||||
|
||||
EnableSocialLink = Utilities.PromptBool(loc["SETUP_DISPLAY_SOCIAL"]);
|
||||
|
||||
@ -61,10 +123,13 @@ namespace SharedLibraryCore.Configuration
|
||||
}
|
||||
|
||||
RConPollRate = 5000;
|
||||
|
||||
AutoMessagePeriod = 60;
|
||||
return this;
|
||||
}
|
||||
|
||||
public string Name() => "ApplicationConfiguration";
|
||||
public string Name()
|
||||
{
|
||||
return "ApplicationConfiguration";
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Configuration.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
||||
public class ConfiguratinLinked : Attribute
|
||||
{
|
||||
public string[] LinkedPropertyNames { get; set; }
|
||||
|
||||
public ConfiguratinLinked(params string[] linkedPropertyNames)
|
||||
{
|
||||
LinkedPropertyNames = linkedPropertyNames;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Configuration.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
||||
public class ConfigurationIgnore : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Configuration.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = false)]
|
||||
public class ConfigurationOptional : Attribute
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Configuration.Attributes
|
||||
{
|
||||
class LocalizedDisplayName : DisplayNameAttribute
|
||||
{
|
||||
private readonly string _localizationKey;
|
||||
public LocalizedDisplayName(string localizationKey)
|
||||
{
|
||||
_localizationKey = localizationKey;
|
||||
}
|
||||
|
||||
public override string DisplayName => Utilities.CurrentLocalization.LocalizationIndex[_localizationKey];
|
||||
}
|
||||
}
|
@ -7,10 +7,11 @@ namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
public class DefaultConfiguration : IBaseConfiguration
|
||||
{
|
||||
public int AutoMessagePeriod { get; set; }
|
||||
public List<string> AutoMessages { get; set; }
|
||||
public List<string> GlobalRules { get; set; }
|
||||
public List<MapConfiguration> Maps { get; set; }
|
||||
public List<QuickMessageConfiguration> QuickMessages {get; set;}
|
||||
public List<string> DisallowedClientNames { get; set; }
|
||||
|
||||
public IBaseConfiguration Generate() => this;
|
||||
|
||||
|
14
SharedLibraryCore/Configuration/QuickMessageConfiguration.cs
Normal file
14
SharedLibraryCore/Configuration/QuickMessageConfiguration.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
public class QuickMessageConfiguration
|
||||
{
|
||||
|
||||
public Game Game { get; set; }
|
||||
public Dictionary<string, string> Messages { get; set; }
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Configuration.Attributes;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@ -7,15 +8,27 @@ namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
public class ServerConfiguration : IBaseConfiguration
|
||||
{
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")]
|
||||
public string IPAddress { get; set; }
|
||||
public ushort Port { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PORT")]
|
||||
public int Port { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PASSWORD")]
|
||||
public string Password { get; set; }
|
||||
public IList<string> Rules { get; set; }
|
||||
public IList<string> AutoMessages { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")]
|
||||
public List<string> Rules { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")]
|
||||
public List<string> AutoMessages { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")]
|
||||
[ConfigurationOptional]
|
||||
public string ManualLogPath { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RCON_PARSER")]
|
||||
public string RConParserVersion { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_EVENT_PARSER")]
|
||||
public string EventParserVersion { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RESERVED_SLOT")]
|
||||
public int ReservedSlotNumber { get; set; }
|
||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_GAME_LOG_SERVER")]
|
||||
[ConfigurationOptional]
|
||||
public Uri GameLogServerUrl { get; set; }
|
||||
|
||||
private readonly IList<IRConParser> rconParsers;
|
||||
@ -25,10 +38,19 @@ namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
rconParsers = new List<IRConParser>();
|
||||
eventParsers = new List<IEventParser>();
|
||||
Rules = new List<string>();
|
||||
AutoMessages = new List<string>();
|
||||
}
|
||||
|
||||
public void AddRConParser(IRConParser parser) => rconParsers.Add(parser);
|
||||
public void AddEventParser(IEventParser parser) => eventParsers.Add(parser);
|
||||
public void AddRConParser(IRConParser parser)
|
||||
{
|
||||
rconParsers.Add(parser);
|
||||
}
|
||||
|
||||
public void AddEventParser(IEventParser parser)
|
||||
{
|
||||
eventParsers.Add(parser);
|
||||
}
|
||||
|
||||
public void ModifyParsers()
|
||||
{
|
||||
@ -65,26 +87,26 @@ namespace SharedLibraryCore.Configuration
|
||||
string input = Utilities.PromptString(loc["SETUP_SERVER_IP"]);
|
||||
|
||||
if (System.Net.IPAddress.TryParse(input, out System.Net.IPAddress ip))
|
||||
{
|
||||
IPAddress = input;
|
||||
}
|
||||
}
|
||||
|
||||
while(Port < 1)
|
||||
{
|
||||
string input = Utilities.PromptString(loc["SETUP_SERVER_PORT"]);
|
||||
if (UInt16.TryParse(input, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture, out ushort port))
|
||||
Port = port;
|
||||
}
|
||||
|
||||
Port = Utilities.PromptInt(loc["SETUP_SERVER_PORT"], null, 1, ushort.MaxValue);
|
||||
Password = Utilities.PromptString(loc["SETUP_SERVER_RCON"]);
|
||||
AutoMessages = new List<string>();
|
||||
Rules = new List<string>();
|
||||
ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(null, 0, 32);
|
||||
ManualLogPath = null;
|
||||
|
||||
ModifyParsers();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public string Name() => "ServerConfiguration";
|
||||
public string Name()
|
||||
{
|
||||
return "ServerConfiguration";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ namespace SharedLibraryCore.Database
|
||||
AliasId = 1,
|
||||
Active = true,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = 0,
|
||||
Name = "IW4MAdmin",
|
||||
LinkId = 1
|
||||
};
|
||||
|
@ -22,13 +22,35 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
static string _ConnectionString;
|
||||
static string _provider;
|
||||
private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins\";
|
||||
private static readonly string _migrationPluginDirectory = @"X:\IW4MAdmin\BUILD\Plugins";
|
||||
private static int activeContextCount;
|
||||
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt) { }
|
||||
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt)
|
||||
{
|
||||
#if DEBUG == true
|
||||
activeContextCount++;
|
||||
Console.WriteLine($"Initialized DB Context #{activeContextCount}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public DatabaseContext() { }
|
||||
public DatabaseContext()
|
||||
{
|
||||
#if DEBUG == true
|
||||
activeContextCount++;
|
||||
Console.WriteLine($"Initialized DB Context #{activeContextCount}");
|
||||
#endif
|
||||
}
|
||||
|
||||
public DatabaseContext(bool disableTracking)
|
||||
public override void Dispose()
|
||||
{
|
||||
#if DEBUG == true
|
||||
|
||||
Console.WriteLine($"Disposed DB Context #{activeContextCount}");
|
||||
activeContextCount--;
|
||||
#endif
|
||||
}
|
||||
|
||||
public DatabaseContext(bool disableTracking) : this()
|
||||
{
|
||||
if (disableTracking)
|
||||
{
|
||||
@ -45,7 +67,7 @@ namespace SharedLibraryCore.Database
|
||||
}
|
||||
}
|
||||
|
||||
public DatabaseContext(string connStr, string provider)
|
||||
public DatabaseContext(string connStr, string provider) : this()
|
||||
{
|
||||
_ConnectionString = connStr;
|
||||
_provider = provider;
|
||||
@ -123,6 +145,11 @@ namespace SharedLibraryCore.Database
|
||||
ent.HasIndex(a => a.Name);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<EFMeta>(ent =>
|
||||
{
|
||||
ent.HasIndex(_meta => _meta.Key);
|
||||
});
|
||||
|
||||
// force full name for database conversion
|
||||
modelBuilder.Entity<EFClient>().ToTable("EFClients");
|
||||
modelBuilder.Entity<EFAlias>().ToTable("EFAlias");
|
||||
@ -138,7 +165,6 @@ namespace SharedLibraryCore.Database
|
||||
#endif
|
||||
IEnumerable<string> directoryFiles = Directory.GetFiles(pluginDir).Where(f => f.EndsWith(".dll"));
|
||||
|
||||
|
||||
foreach (string dllPath in directoryFiles)
|
||||
{
|
||||
Assembly library;
|
||||
|
@ -4,7 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
public class EFAlias : SharedEntity
|
||||
public partial class EFAlias : SharedEntity
|
||||
{
|
||||
[Key]
|
||||
public int AliasId { get; set; }
|
||||
|
@ -40,8 +40,8 @@ namespace SharedLibraryCore.Database.Models
|
||||
[NotMapped]
|
||||
public virtual string Name
|
||||
{
|
||||
get { return CurrentAlias.Name; }
|
||||
set { CurrentAlias.Name = value; }
|
||||
get { return CurrentAlias?.Name ?? "--"; }
|
||||
set { if (CurrentAlias != null) CurrentAlias.Name = value; }
|
||||
}
|
||||
[NotMapped]
|
||||
public virtual int? IPAddress
|
||||
|
@ -23,6 +23,8 @@ namespace SharedLibraryCore.Database.Models
|
||||
public virtual EFClient Client { get; set; }
|
||||
[Required]
|
||||
[MinLength(3)]
|
||||
[StringLength(32)]
|
||||
[MaxLength(32)]
|
||||
public string Key { get; set; }
|
||||
[Required]
|
||||
public string Value { get; set; }
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
@ -8,5 +9,7 @@ namespace SharedLibraryCore.Dtos
|
||||
public string Message { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
public string Name { get; set; }
|
||||
public Game ServerGame { get; set; }
|
||||
public bool IsQuickMessage { get; set; }
|
||||
}
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
using static SharedLibraryCore.Objects.Penalty;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
@ -10,14 +8,25 @@ namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public string OffenderName { get; set; }
|
||||
public int OffenderId { get; set; }
|
||||
public ulong OffenderNetworkId { get; set; }
|
||||
public string OffenderIPAddress { get; set; }
|
||||
public string PunisherName { get; set; }
|
||||
public int PunisherId { get; set; }
|
||||
public string PunisherLevel { get; set; }
|
||||
public int PunisherLevelId { get; set; }
|
||||
public ulong PunisherNetworkId { get; set; }
|
||||
public string PunisherIPAddress { get; set; }
|
||||
public Permission PunisherLevel { get; set; }
|
||||
public string PunisherLevelText => PunisherLevel.ToLocalizedLevelName();
|
||||
public string Offense { get; set; }
|
||||
public string AutomatedOffense { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string TimePunished { get; set; }
|
||||
public string TimeRemaining { get; set; }
|
||||
public PenaltyType PenaltyType { get; set; }
|
||||
public string PenaltyTypeText => PenaltyType.ToString();
|
||||
public DateTime TimePunished { get; set; }
|
||||
public string TimePunishedString => Utilities.GetTimePassed(TimePunished, true);
|
||||
public string TimeRemaining => DateTime.UtcNow > Expires ? "" : $"{((Expires ?? DateTime.MaxValue).Year == DateTime.MaxValue.Year ? Utilities.GetTimePassed(TimePunished, true) : Utilities.TimeSpanText((Expires ?? DateTime.MaxValue) - DateTime.UtcNow))}";
|
||||
public bool Expired => Expires.HasValue && Expires <= DateTime.UtcNow;
|
||||
public DateTime? Expires { get; set; }
|
||||
public override bool Sensitive => PenaltyType == PenaltyType.Flag || PenaltyType == PenaltyType.Unflag;
|
||||
public bool IsEvade { get; set; }
|
||||
public string AdditionalPenaltyInformation => $"{(!string.IsNullOrEmpty(AutomatedOffense) ? $" ({AutomatedOffense})" : "")}{(IsEvade ? $" ({Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PENALTY_EVADE"]})" : "")}";
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,13 @@ namespace SharedLibraryCore.Dtos
|
||||
public List<string> Aliases { get; set; }
|
||||
public List<string> IPs { get; set; }
|
||||
public bool HasActivePenalty { get; set; }
|
||||
public int ConnectionCount { get; set; }
|
||||
public string LastSeen { get; set; }
|
||||
public string FirstSeen { get; set; }
|
||||
public string TimePlayed { get; set; }
|
||||
public string ActivePenaltyType { get; set; }
|
||||
public bool Authenticated { get; set; }
|
||||
public List<ProfileMeta> Meta { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string TimeOnline { get; set; }
|
||||
public DateTime LastConnection { get; set; }
|
||||
public string LastConnectionText => Utilities.GetTimePassed(LastConnection, true);
|
||||
public IDictionary<int, long> LinkedAccounts { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -8,11 +8,25 @@ namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class ProfileMeta : SharedInfo
|
||||
{
|
||||
public enum MetaType
|
||||
{
|
||||
Other,
|
||||
Information,
|
||||
AliasUpdate,
|
||||
ChatMessage,
|
||||
Penalized,
|
||||
ReceivedPenalty,
|
||||
QuickMessage
|
||||
}
|
||||
|
||||
public DateTime When { get; set; }
|
||||
public string WhenString => Utilities.GetTimePassed(When, false);
|
||||
public string Key { get; set; }
|
||||
public dynamic Value { get; set; }
|
||||
public string Extra { get; set; }
|
||||
public virtual string Class => Value.GetType().ToString();
|
||||
public MetaType Type { get; set; }
|
||||
public int? Column { get; set; }
|
||||
public int? Order { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,7 @@ namespace SharedLibraryCore.Dtos
|
||||
public Helpers.PlayerHistory[] PlayerHistory { get; set; }
|
||||
public long ID { get; set; }
|
||||
public bool Online { get; set; }
|
||||
public string ConnectProtocolUrl { get; set; }
|
||||
public string IPAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
public class SharedInfo
|
||||
{
|
||||
public bool Sensitive { get; set; }
|
||||
public virtual bool Sensitive { get; set; }
|
||||
public bool Show { get; set; } = true;
|
||||
public int Id {get;set;}
|
||||
}
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
@ -85,6 +85,14 @@ namespace SharedLibraryCore
|
||||
/// a client's information was updated
|
||||
/// </summary>
|
||||
Update,
|
||||
/// <summary>
|
||||
/// connection was lost to a server (the server has not responded after a number of attempts)
|
||||
/// </summary>
|
||||
ConnectionLost,
|
||||
/// <summary>
|
||||
/// connection was restored to a server (the server began responding again)
|
||||
/// </summary>
|
||||
ConnectionRestored,
|
||||
|
||||
// events "generated" by clients
|
||||
/// <summary>
|
||||
@ -167,6 +175,10 @@ namespace SharedLibraryCore
|
||||
/// team info printed out by game script
|
||||
/// </summary>
|
||||
JoinTeam = 304,
|
||||
/// <summary>
|
||||
/// used for community generated plugin events
|
||||
/// </summary>
|
||||
Other
|
||||
}
|
||||
|
||||
static long NextEventId;
|
||||
|
@ -1,47 +1,45 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using Newtonsoft.Json;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Configuration
|
||||
{
|
||||
public class BaseConfigurationHandler<T> : IConfigurationHandler<T> where T : IBaseConfiguration
|
||||
{
|
||||
readonly string Filename;
|
||||
IConfigurationRoot ConfigurationRoot { get; set; }
|
||||
readonly string _configurationPath;
|
||||
T _configuration;
|
||||
|
||||
public BaseConfigurationHandler(string fn)
|
||||
{
|
||||
Filename = fn;
|
||||
_configurationPath = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fn}.json");
|
||||
Build();
|
||||
}
|
||||
|
||||
public void Build()
|
||||
{
|
||||
ConfigurationRoot = new ConfigurationBuilder()
|
||||
.AddJsonFile(Path.Join(Utilities.OperatingDirectory, "Configuration", $"{Filename}.json"), true)
|
||||
.Build();
|
||||
|
||||
_configuration = ConfigurationRoot.Get<T>();
|
||||
|
||||
if (_configuration == null)
|
||||
try
|
||||
{
|
||||
var configContent = File.ReadAllText(_configurationPath);
|
||||
_configuration = JsonConvert.DeserializeObject<T>(configContent);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_configuration = default(T);
|
||||
}
|
||||
}
|
||||
|
||||
public Task Save()
|
||||
{
|
||||
var appConfigJSON = JsonConvert.SerializeObject(_configuration, Formatting.Indented);
|
||||
return File.WriteAllTextAsync(Path.Join(Utilities.OperatingDirectory, "Configuration", $"{Filename}.json"), appConfigJSON);
|
||||
return File.WriteAllTextAsync(_configurationPath, appConfigJSON);
|
||||
}
|
||||
|
||||
public T Configuration() => _configuration;
|
||||
public T Configuration()
|
||||
{
|
||||
return _configuration;
|
||||
}
|
||||
|
||||
public void Set(T config)
|
||||
{
|
||||
|
@ -1,20 +1,25 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Helpers
|
||||
{
|
||||
public class MessageToken
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
Func<Server, string> Value;
|
||||
public MessageToken(string Name, Func<Server, string> Value)
|
||||
private readonly Func<Server, Task<string>> _asyncValue;
|
||||
|
||||
|
||||
public MessageToken(string Name, Func<Server, Task<string>> Value)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Value = Value;
|
||||
_asyncValue = Value;
|
||||
}
|
||||
|
||||
public string Process(Server server)
|
||||
|
||||
public async Task<string> ProcessAsync(Server server)
|
||||
{
|
||||
return this.Value(server);
|
||||
string result = await _asyncValue(server);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,19 +11,10 @@ namespace SharedLibraryCore.Helpers
|
||||
{
|
||||
DateTime t = DateTime.UtcNow;
|
||||
When = new DateTime(t.Year, t.Month, t.Day, t.Hour, Math.Min(59, UpdateInterval * (int)Math.Round(t.Minute / (float)UpdateInterval)), 0);
|
||||
PlayerCount = cNum;
|
||||
y = cNum;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public PlayerHistory(DateTime t, int cNum)
|
||||
{
|
||||
When = new DateTime(t.Year, t.Month, t.Day, t.Hour, Math.Min(59, UpdateInterval * (int)Math.Round(t.Minute / (float)UpdateInterval)), 0);
|
||||
PlayerCount = cNum;
|
||||
}
|
||||
#endif
|
||||
|
||||
private DateTime When;
|
||||
private int PlayerCount;
|
||||
|
||||
/// <summary>
|
||||
/// Used by CanvasJS as a point on the x axis
|
||||
@ -36,16 +27,9 @@ namespace SharedLibraryCore.Helpers
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Used by CanvasJS as a point on the y axis
|
||||
/// </summary>
|
||||
public int y
|
||||
{
|
||||
get
|
||||
{
|
||||
return PlayerCount;
|
||||
}
|
||||
}
|
||||
public int y { get; }
|
||||
}
|
||||
}
|
||||
|
@ -27,5 +27,10 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...)
|
||||
/// </summary>
|
||||
Game GameName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// specifies the connect URI used to join game servers via web browser
|
||||
/// </summary>
|
||||
string URLProtocolFormat { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -46,5 +46,6 @@ namespace SharedLibraryCore.Interfaces
|
||||
IEventParser GenerateDynamicEventParser();
|
||||
string Version { get;}
|
||||
ITokenAuthentication TokenAuthenticator { get; }
|
||||
string ExternalIPAddress { get; }
|
||||
}
|
||||
}
|
||||
|
696
SharedLibraryCore/Migrations/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs
generated
Normal file
696
SharedLibraryCore/Migrations/20190222234742_AddIndexToEFMeta-KeyAndClientId.Designer.cs
generated
Normal file
@ -0,0 +1,696 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20190222234742_AddIndexToEFMeta-KeyAndClientId")]
|
||||
partial class AddIndexToEFMetaKeyAndClientId
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("HitDestinationId");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int>("HitOriginId");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int>("LastStrainAngleId");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<long?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("Performance");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("EndPoint");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int?>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue");
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<string>("PreviousValue");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("Key");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<bool>("IsEvadedOffense");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddIndexToEFMetaKeyAndClientId : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.ActiveProvider == "Pomelo.EntityFrameworkCore.MySql")
|
||||
{
|
||||
migrationBuilder.Sql("CREATE FULLTEXT INDEX IX_EFMeta_Key ON EFMeta ( `Key` );");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_EFMeta_Key",
|
||||
table: "EFMeta",
|
||||
column: "Key");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_EFMeta_Key",
|
||||
table: "EFMeta");
|
||||
}
|
||||
}
|
||||
}
|
699
SharedLibraryCore/Migrations/20190423142128_AddGameNameToEFServer.Designer.cs
generated
Normal file
699
SharedLibraryCore/Migrations/20190423142128_AddGameNameToEFServer.Designer.cs
generated
Normal file
@ -0,0 +1,699 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20190423142128_AddGameNameToEFServer")]
|
||||
partial class AddGameNameToEFServer
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<int>("CurrentSessionLength");
|
||||
|
||||
b.Property<double>("CurrentStrain");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("Distance");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("HitDestinationId");
|
||||
|
||||
b.Property<int>("HitLocation");
|
||||
|
||||
b.Property<int>("HitOriginId");
|
||||
|
||||
b.Property<int>("HitType");
|
||||
|
||||
b.Property<int>("Hits");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<int>("LastStrainAngleId");
|
||||
|
||||
b.Property<double>("SessionAngleOffset");
|
||||
|
||||
b.Property<double>("SessionSPM");
|
||||
|
||||
b.Property<int>("SessionScore");
|
||||
|
||||
b.Property<double>("StrainAngleBetween");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent");
|
||||
|
||||
b.Property<int>("WeaponId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AttackerId");
|
||||
|
||||
b.Property<int>("Damage");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id");
|
||||
|
||||
b.Property<int>("DeathType");
|
||||
|
||||
b.Property<double>("Fraction");
|
||||
|
||||
b.Property<int>("HitLoc");
|
||||
|
||||
b.Property<bool>("IsKill");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id");
|
||||
|
||||
b.Property<int>("Map");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<int>("VictimId");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id");
|
||||
|
||||
b.Property<double>("VisibilityPercentage");
|
||||
|
||||
b.Property<int>("Weapon");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<string>("Message");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<DateTime>("TimeSent");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("Deaths");
|
||||
|
||||
b.Property<double>("EloRating");
|
||||
|
||||
b.Property<int>("Kills");
|
||||
|
||||
b.Property<double>("MaxStrain");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR");
|
||||
|
||||
b.Property<double>("SPM");
|
||||
|
||||
b.Property<double>("Skill");
|
||||
|
||||
b.Property<int>("TimePlayed");
|
||||
|
||||
b.Property<double>("VisionAverage");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnName("EFClientStatistics_ClientId");
|
||||
|
||||
b.Property<int>("HitCount");
|
||||
|
||||
b.Property<float>("HitOffsetAverage");
|
||||
|
||||
b.Property<int>("Location");
|
||||
|
||||
b.Property<float>("MaxAngleDistance");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnName("EFClientStatistics_ServerId");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("ClientId", "ServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ActivityAmount");
|
||||
|
||||
b.Property<bool>("Newest");
|
||||
|
||||
b.Property<double>("Performance");
|
||||
|
||||
b.Property<int>("Ranking");
|
||||
|
||||
b.Property<int>("RatingHistoryId");
|
||||
|
||||
b.Property<long?>("ServerId");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("Performance");
|
||||
|
||||
b.HasIndex("Ranking");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("EndPoint");
|
||||
|
||||
b.Property<int?>("GameName");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<long>("ServerId");
|
||||
|
||||
b.Property<long>("TotalKills");
|
||||
|
||||
b.Property<long>("TotalPlayTime");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<DateTime>("DateAdded");
|
||||
|
||||
b.Property<int?>("IPAddress");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue");
|
||||
|
||||
b.Property<int>("OriginEntityId");
|
||||
|
||||
b.Property<string>("PreviousValue");
|
||||
|
||||
b.Property<int>("TargetEntityId");
|
||||
|
||||
b.Property<DateTime>("TimeChanged");
|
||||
|
||||
b.Property<int>("TypeOfChange");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("AliasLinkId");
|
||||
|
||||
b.Property<int>("Connections");
|
||||
|
||||
b.Property<int>("CurrentAliasId");
|
||||
|
||||
b.Property<DateTime>("FirstConnection");
|
||||
|
||||
b.Property<DateTime>("LastConnection");
|
||||
|
||||
b.Property<int>("Level");
|
||||
|
||||
b.Property<bool>("Masked");
|
||||
|
||||
b.Property<long>("NetworkId");
|
||||
|
||||
b.Property<string>("Password");
|
||||
|
||||
b.Property<string>("PasswordSalt");
|
||||
|
||||
b.Property<int>("TotalConnectionTime");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<int>("ClientId");
|
||||
|
||||
b.Property<DateTime>("Created");
|
||||
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(32);
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired();
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("Key");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<bool>("Active");
|
||||
|
||||
b.Property<string>("AutomatedOffense");
|
||||
|
||||
b.Property<DateTime?>("Expires");
|
||||
|
||||
b.Property<bool>("IsEvadedOffense");
|
||||
|
||||
b.Property<int>("LinkId");
|
||||
|
||||
b.Property<int>("OffenderId");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired();
|
||||
|
||||
b.Property<int>("PunisherId");
|
||||
|
||||
b.Property<int>("Type");
|
||||
|
||||
b.Property<DateTime>("When");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd();
|
||||
|
||||
b.Property<int?>("EFACSnapshotSnapshotId");
|
||||
|
||||
b.Property<float>("X");
|
||||
|
||||
b.Property<float>("Y");
|
||||
|
||||
b.Property<float>("Z");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.HasIndex("EFACSnapshotSnapshotId");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics")
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("ClientId", "ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("EFACSnapshotSnapshotId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddGameNameToEFServer : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "GameName",
|
||||
table: "EFServers",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "GameName",
|
||||
table: "EFServers");
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
|
||||
.HasAnnotation("ProductVersion", "2.2.2-servicing-10034");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
@ -283,6 +283,8 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.Property<string>("EndPoint");
|
||||
|
||||
b.Property<int?>("GameName");
|
||||
|
||||
b.Property<int>("Port");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
@ -432,7 +434,8 @@ namespace SharedLibraryCore.Migrations
|
||||
b.Property<string>("Extra");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired();
|
||||
.IsRequired()
|
||||
.HasMaxLength(32);
|
||||
|
||||
b.Property<DateTime>("Updated");
|
||||
|
||||
@ -443,6 +446,8 @@ namespace SharedLibraryCore.Migrations
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("Key");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
|
@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Objects
|
||||
{
|
||||
public class Alias : Database.Models.EFAlias
|
||||
{
|
||||
|
||||
}
|
||||
}
|
7
SharedLibraryCore/Objects/EFAlias.cs
Normal file
7
SharedLibraryCore/Objects/EFAlias.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
public partial class EFAlias
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
@ -79,12 +80,12 @@ namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
{ "_reportCount", 0 }
|
||||
};
|
||||
CurrentAlias = new EFAlias();
|
||||
ReceivedPenalties = new List<EFPenalty>();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}::{NetworkId}";
|
||||
return $"{CurrentAlias?.Name ?? "--"}::{NetworkId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -241,11 +242,6 @@ namespace SharedLibraryCore.Database.Models
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
this.Level = Permission.Flagged;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
@ -273,16 +269,11 @@ namespace SharedLibraryCore.Database.Models
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
}
|
||||
|
||||
else if (this.Level != EFClient.Permission.Flagged)
|
||||
else if (this.Level != Permission.Flagged)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
this.Level = Permission.User;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
@ -377,7 +368,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
/// <param name="unbanReason">reason for the unban</param>
|
||||
/// <param name="sender">client performing the unban</param>
|
||||
/// <returns></returns>
|
||||
public GameEvent Unban(String unbanReason, EFClient sender)
|
||||
public GameEvent Unban(string unbanReason, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
@ -399,6 +390,42 @@ namespace SharedLibraryCore.Database.Models
|
||||
return e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// sets the level of the client
|
||||
/// </summary>
|
||||
/// <param name="newPermission">new permission to set client to</param>
|
||||
/// <param name="sender">user performing the set level</param>
|
||||
/// <returns></returns>
|
||||
public GameEvent SetLevel(Permission newPermission, EFClient sender)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ChangePermission,
|
||||
Extra = newPermission,
|
||||
Origin = sender,
|
||||
Target = this,
|
||||
Owner = sender.CurrentServer
|
||||
};
|
||||
|
||||
if (this.Level > sender.Level)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Permission;
|
||||
}
|
||||
|
||||
else if (Level == newPermission)
|
||||
{
|
||||
e.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Level = newPermission;
|
||||
}
|
||||
|
||||
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles any client related logic on connection
|
||||
/// </summary>
|
||||
@ -416,9 +443,9 @@ namespace SharedLibraryCore.Database.Models
|
||||
return;
|
||||
}
|
||||
|
||||
if (Name == "Unknown Soldier" ||
|
||||
Name == "UnknownSoldier" ||
|
||||
Name == "CHEATER")
|
||||
if (CurrentServer.Manager.GetApplicationSettings().Configuration()
|
||||
.DisallowedClientNames
|
||||
?.Any(_name => Regex.IsMatch(Name, _name)) ?? false)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their name is generic");
|
||||
Kick(loc["SERVER_KICK_GENERICNAME"], Utilities.IW4MAdminClient(CurrentServer));
|
||||
@ -450,95 +477,141 @@ namespace SharedLibraryCore.Database.Models
|
||||
State = ClientState.Disconnecting;
|
||||
TotalConnectionTime += ConnectionLength;
|
||||
LastConnection = DateTime.UtcNow;
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
|
||||
try
|
||||
{
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
CurrentServer.Logger.WriteWarning($"Could not update disconnected player {this}");
|
||||
CurrentServer.Logger.WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> OnJoin(int? ipAddress)
|
||||
public async Task OnJoin(int? ipAddress)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Start join for {this}::{ipAddress}::{Level.ToString()}");
|
||||
|
||||
IPAddress = ipAddress;
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
|
||||
|
||||
if (ipAddress != null)
|
||||
{
|
||||
// todo: remove this in a few weeks because it's just temporary for server forwarding
|
||||
if (IPAddressString == "66.150.121.184" || IPAddressString == "62.210.178.177")
|
||||
{
|
||||
Kick($"Your favorite servers are outdated. Please remove and re-add this server. ({CurrentServer.Hostname})", autoKickClient);
|
||||
return false;
|
||||
}
|
||||
IPAddress = ipAddress;
|
||||
await CurrentServer.Manager.GetClientService().UpdateAlias(this);
|
||||
}
|
||||
|
||||
// we want to run any non GUID based logic here
|
||||
OnConnect();
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
|
||||
CurrentServer.Logger.WriteDebug($"OnConnect finished for {this}");
|
||||
if (await CanConnect(ipAddress))
|
||||
{
|
||||
if (IPAddress != null)
|
||||
{
|
||||
await CurrentServer.Manager.GetClientService().Update(this);
|
||||
|
||||
#region CLIENT_BAN
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = this,
|
||||
Target = this,
|
||||
Owner = CurrentServer
|
||||
};
|
||||
|
||||
CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Client {this} is not allowed to join the server");
|
||||
}
|
||||
|
||||
CurrentServer.Logger.WriteDebug($"OnJoin finished for {this}");
|
||||
}
|
||||
|
||||
private async Task<bool> CanConnect(int? ipAddress)
|
||||
{
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
var autoKickClient = Utilities.IW4MAdminClient(CurrentServer);
|
||||
|
||||
#region CLIENT_GUID_BAN
|
||||
// kick them as their level is banned
|
||||
if (Level == Permission.Banned)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because they are banned");
|
||||
var ban = ReceivedPenalties.FirstOrDefault(_penalty => _penalty.Expires == null && _penalty.Active);
|
||||
var profileBan = ReceivedPenalties.FirstOrDefault(_penalty => _penalty.Expires == null && _penalty.Active);
|
||||
|
||||
if (ban == null)
|
||||
if (profileBan == null)
|
||||
{
|
||||
// this is from the old system before bans were applied to all accounts
|
||||
ban = (await CurrentServer.Manager
|
||||
profileBan = (await CurrentServer.Manager
|
||||
.GetPenaltyService()
|
||||
.GetActivePenaltiesAsync(AliasLinkId))
|
||||
.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.Ban);
|
||||
|
||||
CurrentServer.Logger.WriteError($"Client {this} is banned, but no penalty exists for their ban");
|
||||
CurrentServer.Logger.WriteWarning($"Client {this} is GUID banned, but no previous penalty exists for their ban");
|
||||
|
||||
if (profileBan == null)
|
||||
{
|
||||
profileBan = new EFPenalty() { Offense = loc["SERVER_BAN_UNKNOWN"] };
|
||||
CurrentServer.Logger.WriteWarning($"Client {this} is GUID banned, but we could not find the penalty on any linked accounts");
|
||||
}
|
||||
|
||||
// hack: re apply the automated offense to the reban
|
||||
if (ban.AutomatedOffense != null)
|
||||
if (profileBan.AutomatedOffense != null)
|
||||
{
|
||||
autoKickClient.AdministeredPenalties?.Add(new EFPenalty()
|
||||
{
|
||||
AutomatedOffense = ban.AutomatedOffense
|
||||
AutomatedOffense = profileBan.AutomatedOffense
|
||||
});
|
||||
}
|
||||
|
||||
// this is a reban of the new GUID and IP
|
||||
Ban($"{ban.Offense}", autoKickClient, false);
|
||||
Ban($"{profileBan.Offense}", autoKickClient, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
Kick($"{loc["SERVER_BAN_PREV"]} {ban?.Offense}", autoKickClient);
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because they are banned");
|
||||
Kick(loc["SERVER_BAN_PREV"].FormatExt(profileBan?.Offense), autoKickClient);
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
var tempBan = ReceivedPenalties.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.TempBan && _penalty.Expires > DateTime.UtcNow && _penalty.Active);
|
||||
// they have an active tempban tied to their GUID
|
||||
if (tempBan != null)
|
||||
#region CLIENT_GUID_TEMPBAN
|
||||
else
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because they are temporarily banned");
|
||||
Kick($"{loc["SERVER_TB_REMAIN"]} ({(tempBan.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
|
||||
return false;
|
||||
var profileTempBan = ReceivedPenalties.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.TempBan &&
|
||||
_penalty.Active &&
|
||||
_penalty.Expires > DateTime.UtcNow);
|
||||
|
||||
// they have an active tempban tied to their GUID
|
||||
if (profileTempBan != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Kicking {this} because their GUID is temporarily banned");
|
||||
Kick($"{loc["SERVER_TB_REMAIN"]} ({(profileTempBan.Expires.Value - DateTime.UtcNow).TimeSpanText()} {loc["WEBFRONT_PENALTY_TEMPLATE_REMAINING"]})", autoKickClient);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// we want to get any penalties that are tied to their IP or AliasLink (but not necessarily their GUID)
|
||||
var activePenalties = await CurrentServer.Manager.GetPenaltyService().GetActivePenaltiesAsync(AliasLinkId, ipAddress);
|
||||
|
||||
#region CLIENT_LINKED_TEMPBAN
|
||||
var tempBan = activePenalties.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.TempBan);
|
||||
|
||||
// they have an active tempban tied to their AliasLink
|
||||
if (tempBan != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Tempbanning {this} because their AliasLink is temporarily banned, but they are not");
|
||||
TempBan(tempBan.Offense, DateTime.UtcNow - (tempBan.Expires ?? DateTime.UtcNow), autoKickClient);
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CLIENT_LINKED_BAN
|
||||
var currentBan = activePenalties.FirstOrDefault(p => p.Type == Penalty.PenaltyType.Ban);
|
||||
|
||||
var currentAutoFlag = activePenalties.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1)
|
||||
.Where(p => p.Active)
|
||||
.OrderByDescending(p => p.When)
|
||||
.FirstOrDefault();
|
||||
|
||||
// remove their auto flag status after a week
|
||||
if (Level == Permission.Flagged &&
|
||||
currentAutoFlag != null &&
|
||||
(DateTime.UtcNow - currentAutoFlag.When).TotalDays > 7)
|
||||
{
|
||||
Level = Permission.User;
|
||||
}
|
||||
|
||||
// they have a perm ban tied to their AliasLink/profile
|
||||
if (currentBan != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteInfo($"Banned client {this} trying to evade...");
|
||||
@ -567,20 +640,38 @@ namespace SharedLibraryCore.Database.Models
|
||||
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
else
|
||||
#region CLIENT_LINKED_FLAG
|
||||
if (Level != Permission.Flagged)
|
||||
{
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Join,
|
||||
Origin = this,
|
||||
Target = this,
|
||||
Owner = CurrentServer
|
||||
};
|
||||
var currentFlag = activePenalties.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.Flag);
|
||||
|
||||
CurrentServer.Manager.GetEventHandler().AddEvent(e);
|
||||
return true;
|
||||
if (currentFlag != null)
|
||||
{
|
||||
CurrentServer.Logger.WriteDebug($"Flagging {this} because their AliasLink is flagged, but they are not");
|
||||
Flag(currentFlag.Offense, autoKickClient);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (Level == Permission.Flagged)
|
||||
{
|
||||
var currentAutoFlag = activePenalties
|
||||
.Where(p => p.Type == Penalty.PenaltyType.Flag && p.PunisherId == 1)
|
||||
.OrderByDescending(p => p.When)
|
||||
.FirstOrDefault();
|
||||
|
||||
// remove their auto flag status after a week
|
||||
if (currentAutoFlag != null &&
|
||||
(DateTime.UtcNow - currentAutoFlag.When).TotalDays > 7)
|
||||
{
|
||||
CurrentServer.Logger.WriteInfo($"Unflagging {this} because the auto flag time has expired");
|
||||
Unflag(Utilities.CurrentLocalization.LocalizationIndex["SERVER_AUTOFLAG_UNFLAG"], autoKickClient);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[NotMapped]
|
||||
|
@ -15,11 +15,7 @@ namespace SharedLibraryCore.Objects
|
||||
Ban,
|
||||
Unban,
|
||||
Any,
|
||||
}
|
||||
|
||||
public String GetWhenFormatted()
|
||||
{
|
||||
return When.ToString("MM/dd/yy HH:mm:ss"); ;
|
||||
Unflag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -88,9 +88,10 @@ namespace SharedLibraryCore.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception E)
|
||||
catch (Exception e)
|
||||
{
|
||||
Manager.GetLogger(0).WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Plugin.Location} - {E.Message}");
|
||||
Manager.GetLogger(0).WriteWarning(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(Plugin.Location));
|
||||
Manager.GetLogger(0).WriteDebug(e.GetExceptionInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ namespace SharedLibraryCore.RCon
|
||||
retrySend:
|
||||
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
||||
{
|
||||
//DontFragment = true,
|
||||
DontFragment = true,
|
||||
Ttl = 100,
|
||||
ExclusiveAddressUse = true,
|
||||
})
|
||||
@ -138,7 +138,7 @@ namespace SharedLibraryCore.RCon
|
||||
|
||||
if (response.Length == 0 && waitForResponse)
|
||||
{
|
||||
throw new Exception();
|
||||
throw new NetworkException("Expected response but got 0 bytes back");
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
@ -149,18 +149,16 @@ namespace SharedLibraryCore.RCon
|
||||
{
|
||||
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
||||
{
|
||||
// Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
||||
await Task.Delay(StaticHelpers.FloodProtectionInterval);
|
||||
goto retrySend;
|
||||
}
|
||||
|
||||
connectionState.OnComplete.Release(1);
|
||||
//Log.WriteDebug(ex.GetExceptionInfo());
|
||||
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
|
||||
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"].FormatExt(Endpoint));
|
||||
}
|
||||
}
|
||||
|
||||
string responseString = Encoding.GetEncoding("windows-1252").GetString(response, 0, response.Length) + '\n';
|
||||
string responseString = defaultEncoding.GetString(response, 0, response.Length) + '\n';
|
||||
|
||||
if (responseString.Contains("Invalid password"))
|
||||
{
|
||||
@ -229,6 +227,8 @@ namespace SharedLibraryCore.RCon
|
||||
}
|
||||
}
|
||||
|
||||
rconSocket.Close();
|
||||
|
||||
byte[] response = connectionState.ReceiveBuffer
|
||||
.Take(connectionState.ReceiveEventArgs.BytesTransferred)
|
||||
.ToArray();
|
||||
|
@ -87,6 +87,11 @@ namespace SharedLibraryCore
|
||||
/// <returns>Matching player if found</returns>
|
||||
public List<EFClient> GetClientByName(String pName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pName))
|
||||
{
|
||||
return new List<EFClient>();
|
||||
}
|
||||
|
||||
string[] QuoteSplit = pName.Split('"');
|
||||
bool literal = false;
|
||||
if (QuoteSplit.Length > 1)
|
||||
@ -95,9 +100,11 @@ namespace SharedLibraryCore
|
||||
literal = true;
|
||||
}
|
||||
if (literal)
|
||||
return Clients.Where(p => p != null && p.Name.ToLower().Equals(pName.ToLower())).ToList();
|
||||
{
|
||||
return GetClientsAsList().Where(p => p.Name?.ToLower() == pName.ToLower()).ToList();
|
||||
}
|
||||
|
||||
return Clients.Where(p => p != null && p.Name.ToLower().Contains(pName.ToLower())).ToList();
|
||||
return GetClientsAsList().Where(p => (p.Name?.ToLower() ?? "").Contains(pName.ToLower())).ToList();
|
||||
}
|
||||
|
||||
virtual public Task<bool> ProcessUpdatesAsync(CancellationToken cts) => (Task<bool>)Task.CompletedTask;
|
||||
@ -260,7 +267,7 @@ namespace SharedLibraryCore
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{IP}-{Port}";
|
||||
return $"{IP}:{Port}";
|
||||
}
|
||||
|
||||
protected async Task<bool> ScriptLoaded()
|
||||
@ -313,6 +320,7 @@ namespace SharedLibraryCore
|
||||
// Internal
|
||||
public string IP { get; protected set; }
|
||||
public string Version { get; protected set; }
|
||||
public bool IsInitialized { get; set; }
|
||||
|
||||
protected int Port;
|
||||
protected string FSGame;
|
||||
|
@ -17,27 +17,6 @@ namespace SharedLibraryCore.Services
|
||||
public async Task<EFAlias> Create(EFAlias entity)
|
||||
{
|
||||
throw await Task.FromResult(new Exception());
|
||||
/*using (var context = new DatabaseContext())
|
||||
{
|
||||
var alias = new EFAlias()
|
||||
{
|
||||
Active = true,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.IPAddress,
|
||||
Name = entity.Name
|
||||
};
|
||||
|
||||
entity.Link = await context.AliasLinks
|
||||
.FirstAsync(a => a.AliasLinkId == entity.Link.AliasLinkId);
|
||||
context.Aliases.Add(entity);
|
||||
await context.SaveChangesAsync();
|
||||
return entity;
|
||||
}*/
|
||||
}
|
||||
|
||||
public Task<EFAlias> CreateProxy()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<EFAlias> Delete(EFAlias entity)
|
||||
|
@ -1,10 +1,9 @@
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Events;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
@ -16,7 +15,7 @@ namespace SharedLibraryCore.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<EFChangeHistory> Add(GameEvent e)
|
||||
public async Task<EFChangeHistory> Add(GameEvent e, DatabaseContext ctx = null)
|
||||
{
|
||||
EFChangeHistory change = null;
|
||||
|
||||
@ -32,12 +31,19 @@ namespace SharedLibraryCore.Services
|
||||
};
|
||||
break;
|
||||
case GameEvent.EventType.Command:
|
||||
// this prevents passwords/tokens being logged into the database in plain text
|
||||
if (e.Extra is Command cmd)
|
||||
{
|
||||
if (cmd.Name == "login" || cmd.Name == "setpassword")
|
||||
{
|
||||
e.Message = string.Join(' ', e.Message.Split(" ").Select((arg, index) => index > 0 ? "*****" : arg));
|
||||
}
|
||||
}
|
||||
change = new EFChangeHistory()
|
||||
{
|
||||
OriginEntityId = e.Origin.ClientId,
|
||||
TargetEntityId = e.Target?.ClientId ?? 0,
|
||||
Comment = "Executed command",
|
||||
PreviousValue = "",
|
||||
CurrentValue = e.Message,
|
||||
TypeOfChange = EFChangeHistory.ChangeType.Command
|
||||
};
|
||||
@ -47,9 +53,9 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
OriginEntityId = e.Origin.ClientId,
|
||||
TargetEntityId = e.Target.ClientId,
|
||||
Comment = "Changed permission level",
|
||||
TypeOfChange = EFChangeHistory.ChangeType.Permission,
|
||||
PreviousValue = ((Change)e.Extra).PreviousValue,
|
||||
CurrentValue = ((Change)e.Extra).NewValue
|
||||
CurrentValue = ((EFClient.Permission)e.Extra).ToString()
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@ -58,18 +64,27 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
if (change != null)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
ctx.EFChangeHistory.Add(change);
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
bool existingCtx = ctx != null;
|
||||
ctx = ctx ?? new DatabaseContext(true);
|
||||
|
||||
catch (Exception ex)
|
||||
ctx.EFChangeHistory.Add(change);
|
||||
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
e.Owner.Logger.WriteWarning(ex.Message);
|
||||
e.Owner.Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
if (!existingCtx)
|
||||
{
|
||||
e.Owner.Logger.WriteWarning(ex.Message);
|
||||
e.Owner.Logger.WriteDebug(ex.GetExceptionInfo());
|
||||
ctx.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,33 +17,79 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
int? linkId = null;
|
||||
int? aliasId = null;
|
||||
|
||||
if (entity.IPAddress != null)
|
||||
{
|
||||
var existingAlias = await context.Aliases
|
||||
.Select(_alias => new { _alias.AliasId, _alias.LinkId, _alias.IPAddress, _alias.Name })
|
||||
.FirstOrDefaultAsync(_alias => _alias.IPAddress == entity.IPAddress);
|
||||
|
||||
if (existingAlias != null)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing link {existingAlias.LinkId}");
|
||||
|
||||
linkId = existingAlias.LinkId;
|
||||
if (existingAlias.Name == entity.Name)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] client with new GUID {entity} has existing alias {existingAlias.AliasId}");
|
||||
aliasId = existingAlias.AliasId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var client = new EFClient()
|
||||
{
|
||||
Level = Permission.User,
|
||||
FirstConnection = DateTime.UtcNow,
|
||||
Connections = 1,
|
||||
LastConnection = DateTime.UtcNow,
|
||||
Masked = false,
|
||||
NetworkId = entity.NetworkId,
|
||||
AliasLink = new EFAliasLink()
|
||||
{
|
||||
Active = false
|
||||
},
|
||||
ReceivedPenalties = new List<EFPenalty>()
|
||||
};
|
||||
|
||||
client.CurrentAlias = new Alias()
|
||||
{
|
||||
Name = entity.Name,
|
||||
Link = client.AliasLink,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.IPAddress,
|
||||
// the first time a client is created, we may not have their ip,
|
||||
// so we create a temporary alias
|
||||
Active = false
|
||||
NetworkId = entity.NetworkId
|
||||
};
|
||||
|
||||
context.Clients.Add(client);
|
||||
|
||||
// they're just using a new GUID
|
||||
if (aliasId.HasValue)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s alias id and linkid to ({aliasId.Value}, {linkId.Value})");
|
||||
client.CurrentAliasId = aliasId.Value;
|
||||
client.AliasLinkId = linkId.Value;
|
||||
}
|
||||
|
||||
// link was found but they don't have an exact alias
|
||||
else if (!aliasId.HasValue && linkId.HasValue)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] setting {entity}'s linkid to {linkId.Value}, but creating new alias");
|
||||
client.AliasLinkId = linkId.Value;
|
||||
client.CurrentAlias = new EFAlias()
|
||||
{
|
||||
Name = entity.Name,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.IPAddress,
|
||||
LinkId = linkId.Value
|
||||
};
|
||||
}
|
||||
|
||||
// brand new players (supposedly)
|
||||
else
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"[create] creating new Link and Alias for {entity}");
|
||||
var link = new EFAliasLink();
|
||||
var alias = new EFAlias()
|
||||
{
|
||||
Name = entity.Name,
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = entity.IPAddress,
|
||||
Link = link
|
||||
};
|
||||
|
||||
link.Children.Add(alias);
|
||||
|
||||
client.AliasLink = link;
|
||||
client.CurrentAlias = alias;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return client;
|
||||
@ -56,137 +102,161 @@ namespace SharedLibraryCore.Services
|
||||
// get all aliases by IP address and LinkId
|
||||
var iqAliases = context.Aliases
|
||||
.Include(a => a.Link)
|
||||
.Where(a => (a.IPAddress == ip) ||
|
||||
a.LinkId == entity.AliasLinkId);
|
||||
// we only want alias that have the same IP address or share a link
|
||||
.Where(_alias => _alias.IPAddress == ip || (_alias.LinkId == entity.AliasLinkId));
|
||||
|
||||
#if DEBUG == true
|
||||
var aliasSql = iqAliases.ToSql();
|
||||
#endif
|
||||
var aliases = await iqAliases.ToListAsync();
|
||||
|
||||
// update each of the aliases where this is no IP but the name is identical
|
||||
foreach (var alias in aliases.Where(_alias => (_alias.IPAddress == null || _alias.IPAddress == 0)))
|
||||
{
|
||||
alias.IPAddress = ip;
|
||||
}
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// see if they have a matching IP + Name but new NetworkId
|
||||
var existingExactAlias = aliases.FirstOrDefault(a => a.Name == name && a.IPAddress == ip);
|
||||
bool exactAliasMatch = existingExactAlias != null;
|
||||
bool hasExactAliasMatch = existingExactAlias != null;
|
||||
|
||||
// if existing alias matches link them
|
||||
EFAliasLink aliasLink = existingExactAlias?.Link;
|
||||
// if no exact matches find the first IP that matches
|
||||
aliasLink = aliasLink ?? aliases.FirstOrDefault()?.Link;
|
||||
// if no matches are found, use our current one
|
||||
aliasLink = aliasLink ?? entity.AliasLink;
|
||||
var newAliasLink = existingExactAlias?.Link;
|
||||
// if no exact matches find the first IP or LinkId that matches
|
||||
newAliasLink = newAliasLink ?? aliases.FirstOrDefault()?.Link;
|
||||
// if no matches are found, use our current one ( it will become permanent )
|
||||
newAliasLink = newAliasLink ?? entity.AliasLink;
|
||||
|
||||
bool hasExistingAlias = aliases.Count > 0;
|
||||
bool isAliasLinkUpdated = newAliasLink.AliasLinkId != entity.AliasLink.AliasLinkId;
|
||||
|
||||
// this happens when an alias exists but the current link is a temporary one
|
||||
if ((exactAliasMatch || hasExistingAlias) &&
|
||||
(!entity.AliasLink.Active && entity.AliasLinkId != aliasLink.AliasLinkId))
|
||||
// this happens when the link we found is different than the one we create before adding an IP
|
||||
if (isAliasLinkUpdated)
|
||||
{
|
||||
entity.AliasLinkId = aliasLink.AliasLinkId;
|
||||
entity.AliasLink = aliasLink;
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] found a link for {entity} so we are updating link from {entity.AliasLink.AliasLinkId} to {newAliasLink.AliasLinkId}");
|
||||
|
||||
var oldAliasLink = entity.AliasLink;
|
||||
|
||||
// update all the clients that have the old alias link
|
||||
await context.Clients
|
||||
.Where(_client => _client.AliasLinkId == oldAliasLink.AliasLinkId)
|
||||
.ForEachAsync(_client => _client.AliasLinkId = newAliasLink.AliasLinkId);
|
||||
|
||||
entity.AliasLink = newAliasLink;
|
||||
entity.AliasLinkId = newAliasLink.AliasLinkId;
|
||||
|
||||
// update all previous aliases
|
||||
await context.Aliases
|
||||
.Where(_alias => _alias.LinkId == oldAliasLink.AliasLinkId)
|
||||
.ForEachAsync(_alias => _alias.LinkId = newAliasLink.AliasLinkId);
|
||||
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Updating alias link for {entity}");
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
foreach (var alias in aliases.Union(new List<EFAlias>() { entity.CurrentAlias })
|
||||
.Where(_alias => !_alias.Active ||
|
||||
_alias.LinkId != aliasLink.AliasLinkId))
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"{entity} updating alias-link id is {alias.LinkId}");
|
||||
alias.Active = true;
|
||||
alias.LinkId = aliasLink.AliasLinkId;
|
||||
}
|
||||
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Saving updated aliases for {entity}");
|
||||
// we want to delete the now inactive alias
|
||||
context.AliasLinks.Remove(oldAliasLink);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// todo: fix this
|
||||
/*context.AliasLinks.Remove(entity.AliasLink);
|
||||
entity.AliasLink = null;
|
||||
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Removing temporary link for {entity}");
|
||||
|
||||
try
|
||||
{
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// entity.CurrentServer.Logger.WriteDebug($"Failed to remove link for {entity}");
|
||||
}*/
|
||||
}
|
||||
|
||||
// the existing alias matches ip and name, so we can just ignore the temporary one
|
||||
if (exactAliasMatch)
|
||||
if (hasExactAliasMatch)
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"{entity} has exact alias match");
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match");
|
||||
|
||||
var oldAlias = entity.CurrentAlias;
|
||||
entity.CurrentAliasId = existingExactAlias.AliasId;
|
||||
entity.CurrentAlias = existingExactAlias;
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// the alias is the same so we can just remove it
|
||||
if (oldAlias.AliasId != existingExactAlias.AliasId && oldAlias.AliasId > 0)
|
||||
{
|
||||
await context.Clients
|
||||
.Where(_client => _client.CurrentAliasId == oldAlias.AliasId)
|
||||
.ForEachAsync(_client => _client.CurrentAliasId = existingExactAlias.AliasId);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}");
|
||||
context.Aliases.Remove(oldAlias);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// theres no exact match, but they've played before with the GUID or IP
|
||||
else if (hasExistingAlias)
|
||||
{
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Connecting player is using a new alias {entity}");
|
||||
|
||||
// this happens when a temporary alias gets updated
|
||||
if (entity.CurrentAlias.Name == name && entity.CurrentAlias.IPAddress == null)
|
||||
{
|
||||
entity.CurrentAlias.IPAddress = ip;
|
||||
entity.CurrentAlias.Active = true;
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Updating temporary alias for {entity}");
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var newAlias = new EFAlias()
|
||||
{
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = ip,
|
||||
LinkId = aliasLink.AliasLinkId,
|
||||
Name = name,
|
||||
Active = true,
|
||||
};
|
||||
|
||||
entity.CurrentAlias = newAlias;
|
||||
//entity.CurrentServer.Logger.WriteDebug($"Saving new alias for {entity}");
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// no record of them playing
|
||||
else
|
||||
{
|
||||
//entity.CurrentServer.Logger.WriteDebug($"{entity} has not be seen before");
|
||||
entity.CurrentServer.Logger.WriteDebug($"[updatealias] {entity} is using a new alias");
|
||||
|
||||
entity.AliasLink.Active = true;
|
||||
entity.CurrentAlias.Active = true;
|
||||
entity.CurrentAlias.IPAddress = ip;
|
||||
entity.CurrentAlias.Name = name;
|
||||
var newAlias = new EFAlias()
|
||||
{
|
||||
DateAdded = DateTime.UtcNow,
|
||||
IPAddress = ip,
|
||||
LinkId = newAliasLink.AliasLinkId,
|
||||
Name = name,
|
||||
Active = true,
|
||||
};
|
||||
|
||||
//entity.CurrentServer.Logger.WriteDebug($"updating new alias for {entity}");
|
||||
entity.CurrentAlias = newAlias;
|
||||
entity.CurrentAliasId = 0;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
var linkIds = aliases.Select(a => a.LinkId);
|
||||
|
||||
if (linkIds.Count() > 0 &&
|
||||
aliases.Count(_alias => _alias.Name == name && _alias.IPAddress == ip) > 0)
|
||||
/// <summary>
|
||||
/// updates the permission level of the given target to the given permission level
|
||||
/// </summary>
|
||||
/// <param name="newPermission"></param>
|
||||
/// <param name="temporalClient"></param>
|
||||
/// <param name="origin"></param>
|
||||
/// <param name="ctx"></param>
|
||||
/// <returns></returns>
|
||||
public async Task UpdateLevel(Permission newPermission, EFClient temporalClient, EFClient origin)
|
||||
{
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
var highestLevel = await context.Clients
|
||||
.Where(c => linkIds.Contains(c.AliasLinkId))
|
||||
.MaxAsync(c => c.Level);
|
||||
var entity = await ctx.Clients
|
||||
.Where(_client => _client.AliasLinkId == temporalClient.AliasLinkId)
|
||||
.FirstAsync();
|
||||
|
||||
if (entity.Level != highestLevel)
|
||||
var oldPermission = entity.Level;
|
||||
|
||||
entity.Level = newPermission;
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
#if DEBUG == true
|
||||
temporalClient.CurrentServer.Logger.WriteDebug($"Updated {temporalClient.ClientId} to {newPermission}");
|
||||
#endif
|
||||
|
||||
// if their permission level has been changed to level that needs to be updated on all accounts
|
||||
if ((oldPermission != newPermission) &&
|
||||
(newPermission == Permission.Banned ||
|
||||
newPermission == Permission.Flagged ||
|
||||
newPermission == Permission.User))
|
||||
{
|
||||
entity.CurrentServer.Logger.WriteDebug($"{entity} updating user level");
|
||||
// todo: log level changes here
|
||||
context.Update(entity);
|
||||
entity.Level = highestLevel;
|
||||
await context.SaveChangesAsync();
|
||||
var changeSvc = new ChangeHistoryService();
|
||||
|
||||
//get all clients that have the same linkId
|
||||
var iqMatchingClients = ctx.Clients
|
||||
.Where(_client => _client.AliasLinkId == entity.AliasLinkId);
|
||||
// make sure we don't select ourselves twice
|
||||
//.Where(_client => _client.ClientId != temporalClient.ClientId);
|
||||
|
||||
// this updates the level for all the clients with the same LinkId
|
||||
// only if their new level is flagged or banned
|
||||
await iqMatchingClients.ForEachAsync(_client =>
|
||||
{
|
||||
_client.Level = newPermission;
|
||||
#if DEBUG == true
|
||||
temporalClient.CurrentServer.Logger.WriteDebug($"Updated linked {_client.ClientId} to {newPermission}");
|
||||
#endif
|
||||
});
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
temporalClient.Level = newPermission;
|
||||
}
|
||||
|
||||
public async Task<EFClient> Delete(EFClient entity)
|
||||
@ -275,73 +345,45 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UpdateAlias(EFClient entity)
|
||||
public async Task UpdateAlias(EFClient temporalClient)
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
var client = context.Clients
|
||||
var entity = context.Clients
|
||||
.Include(c => c.AliasLink)
|
||||
.Include(c => c.CurrentAlias)
|
||||
.First(e => e.ClientId == entity.ClientId);
|
||||
.First(e => e.ClientId == temporalClient.ClientId);
|
||||
|
||||
client.CurrentServer = entity.CurrentServer;
|
||||
entity.CurrentServer = temporalClient.CurrentServer;
|
||||
|
||||
await UpdateAlias(entity.Name, entity.IPAddress, client, context);
|
||||
await UpdateAlias(temporalClient.Name, temporalClient.IPAddress, entity, context);
|
||||
|
||||
entity.CurrentAlias = client.CurrentAlias;
|
||||
entity.CurrentAliasId = client.CurrentAliasId;
|
||||
entity.AliasLink = client.AliasLink;
|
||||
entity.AliasLinkId = client.AliasLinkId;
|
||||
temporalClient.CurrentAlias = entity.CurrentAlias;
|
||||
temporalClient.CurrentAliasId = entity.CurrentAliasId;
|
||||
temporalClient.AliasLink = entity.AliasLink;
|
||||
temporalClient.AliasLinkId = entity.AliasLinkId;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<EFClient> Update(EFClient entity)
|
||||
public async Task<EFClient> Update(EFClient temporalClient)
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
// grab the context version of the entity
|
||||
var client = context.Clients
|
||||
.First(e => e.ClientId == entity.ClientId);
|
||||
var entity = context.Clients
|
||||
.First(client => client.ClientId == temporalClient.ClientId);
|
||||
|
||||
client.CurrentServer = entity.CurrentServer;
|
||||
|
||||
// if their level has been changed
|
||||
if (entity.Level != client.Level)
|
||||
{
|
||||
// get all clients that use the same aliasId
|
||||
var matchingClients = context.Clients
|
||||
.Where(c => c.CurrentAliasId == client.CurrentAliasId)
|
||||
// make sure we don't select ourselves twice
|
||||
.Where(c => c.ClientId != entity.ClientId);
|
||||
|
||||
// update all related clients level
|
||||
await matchingClients.ForEachAsync(c =>
|
||||
{
|
||||
// todo: log that it has changed here
|
||||
c.Level = entity.Level;
|
||||
});
|
||||
}
|
||||
|
||||
// set remaining non-navigation properties that may have been updated
|
||||
client.Level = entity.Level;
|
||||
client.LastConnection = entity.LastConnection;
|
||||
client.Connections = entity.Connections;
|
||||
client.FirstConnection = entity.FirstConnection;
|
||||
client.Masked = entity.Masked;
|
||||
client.TotalConnectionTime = entity.TotalConnectionTime;
|
||||
client.Password = entity.Password;
|
||||
client.PasswordSalt = entity.PasswordSalt;
|
||||
entity.LastConnection = temporalClient.LastConnection;
|
||||
entity.Connections = temporalClient.Connections;
|
||||
entity.FirstConnection = temporalClient.FirstConnection;
|
||||
entity.Masked = temporalClient.Masked;
|
||||
entity.TotalConnectionTime = temporalClient.TotalConnectionTime;
|
||||
entity.Password = temporalClient.Password;
|
||||
entity.PasswordSalt = temporalClient.PasswordSalt;
|
||||
|
||||
// update in database
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
// this is set so future updates don't trigger a new alias add
|
||||
if (entity.CurrentAlias.AliasId == 0)
|
||||
{
|
||||
entity.CurrentAlias.AliasId = client.CurrentAlias.AliasId;
|
||||
}
|
||||
|
||||
return client;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,11 +398,25 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves the number of owners
|
||||
/// (client level is owner)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<int> GetOwnerCount()
|
||||
{
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
return await ctx.Clients.AsNoTracking()
|
||||
.CountAsync(_client => _client.Level == Permission.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<EFClient>> GetPrivilegedClients()
|
||||
{
|
||||
using (var context = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
var iqClients = from client in context.Clients
|
||||
var iqClients = from client in context.Clients.AsNoTracking()
|
||||
where client.Level >= Permission.Trusted
|
||||
where client.Active
|
||||
select new EFClient()
|
||||
@ -371,7 +427,8 @@ namespace SharedLibraryCore.Services
|
||||
Level = client.Level,
|
||||
Password = client.Password,
|
||||
PasswordSalt = client.PasswordSalt,
|
||||
NetworkId = client.NetworkId
|
||||
NetworkId = client.NetworkId,
|
||||
LastConnection = client.LastConnection
|
||||
};
|
||||
|
||||
#if DEBUG == true
|
||||
@ -384,7 +441,7 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
public async Task<IList<EFClient>> FindClientsByIdentifier(string identifier)
|
||||
{
|
||||
if (identifier.Length < 3)
|
||||
if (identifier?.Length < 3)
|
||||
{
|
||||
return new List<EFClient>();
|
||||
}
|
||||
@ -401,7 +458,7 @@ namespace SharedLibraryCore.Services
|
||||
alias.Name.ToLower().Contains(identifier)
|
||||
select alias.LinkId).Distinct();
|
||||
|
||||
var linkIds = iqLinkIds.ToList();
|
||||
var linkIds = await iqLinkIds.ToListAsync();
|
||||
|
||||
var iqClients = context.Clients
|
||||
.Where(c => linkIds.Contains(c.AliasLinkId) ||
|
||||
@ -425,11 +482,6 @@ namespace SharedLibraryCore.Services
|
||||
.CountAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<EFClient> CreateProxy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,157 @@
|
||||
using SharedLibraryCore.Dtos;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Database;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
public class MetaService
|
||||
{
|
||||
private static List<Func<int, Task<List<ProfileMeta>>>> MetaActions = new List<Func<int, Task<List<ProfileMeta>>>>();
|
||||
private static List<Func<int, int, int, DateTime?, Task<List<ProfileMeta>>>> _metaActions = new List<Func<int, int, int, DateTime?, Task<List<ProfileMeta>>>>();
|
||||
|
||||
public static void AddMeta(Func<int, Task<List<ProfileMeta>>> metaAction)
|
||||
/// <summary>
|
||||
/// adds or updates meta key and value to the database
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key of meta data</param>
|
||||
/// <param name="metaValue">value of the meta data</param>
|
||||
/// <param name="client">client to save the meta for</param>
|
||||
/// <returns></returns>
|
||||
public async Task AddPersistentMeta(string metaKey, string metaValue, EFClient client)
|
||||
{
|
||||
MetaActions.Add(metaAction);
|
||||
// this seems to happen if the client disconnects before they've had time to authenticate and be added
|
||||
if (client.ClientId < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var ctx = new DatabaseContext())
|
||||
{
|
||||
var existingMeta = await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (existingMeta != null)
|
||||
{
|
||||
existingMeta.Value = metaValue;
|
||||
existingMeta.Updated = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ctx.EFMeta.Add(new EFMeta()
|
||||
{
|
||||
ClientId = client.ClientId,
|
||||
Created = DateTime.UtcNow,
|
||||
Key = metaKey,
|
||||
Value = metaValue
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<List<ProfileMeta>> GetMeta(int clientId)
|
||||
/// <summary>
|
||||
/// retrieves meta data for given client and key
|
||||
/// </summary>
|
||||
/// <param name="metaKey">key to retrieve value for</param>
|
||||
/// <param name="client">client to retrieve meta for</param>
|
||||
/// <returns></returns>
|
||||
public async Task<EFMeta> GetPersistentMeta(string metaKey, EFClient client)
|
||||
{
|
||||
using (var ctx = new DatabaseContext(disableTracking: true))
|
||||
{
|
||||
return await ctx.EFMeta
|
||||
.Where(_meta => _meta.Key == metaKey)
|
||||
.Where(_meta => _meta.ClientId == client.ClientId)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// aads a meta task to the runtime meta list
|
||||
/// </summary>
|
||||
/// <param name="metaAction"></param>
|
||||
public static void AddRuntimeMeta(Func<int, int, int, DateTime?, Task<List<ProfileMeta>>> metaAction)
|
||||
{
|
||||
_metaActions.Add(metaAction);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves all the runtime meta information for given client idea
|
||||
/// </summary>
|
||||
/// <param name="clientId">id of the client</param>
|
||||
/// <param name="count">number of meta items to retrieve</param>
|
||||
/// <param name="offset">offset from the first item</param>
|
||||
/// <returns></returns>
|
||||
public static async Task<List<ProfileMeta>> GetRuntimeMeta(int clientId, int offset = 0, int count = int.MaxValue, DateTime? startAt = null)
|
||||
{
|
||||
var meta = new List<ProfileMeta>();
|
||||
foreach (var action in MetaActions)
|
||||
meta.AddRange(await action(clientId));
|
||||
return meta;
|
||||
|
||||
foreach (var action in _metaActions)
|
||||
{
|
||||
var metaItems = await action(clientId, offset, count, startAt);
|
||||
meta.AddRange(metaItems);
|
||||
}
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
var table = new List<List<ProfileMeta>>();
|
||||
var metaWithColumn = meta
|
||||
.Where(_meta => _meta.Column != null);
|
||||
|
||||
var columnGrouping = metaWithColumn
|
||||
.GroupBy(_meta => _meta.Column);
|
||||
|
||||
var metaToSort = meta.Except(metaWithColumn).ToList();
|
||||
|
||||
foreach (var metaItem in columnGrouping)
|
||||
{
|
||||
table.Add(new List<ProfileMeta>(metaItem));
|
||||
}
|
||||
|
||||
while (metaToSort.Count > 0)
|
||||
{
|
||||
var sortingMeta = metaToSort.First();
|
||||
|
||||
int indexOfSmallestColumn()
|
||||
{
|
||||
int index = 0;
|
||||
int smallestColumnSize = int.MaxValue;
|
||||
for (int i = 0; i < table.Count; i++)
|
||||
{
|
||||
if (table[i].Count < smallestColumnSize)
|
||||
{
|
||||
smallestColumnSize = table[i].Count;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
int columnIndex = indexOfSmallestColumn();
|
||||
|
||||
sortingMeta.Column = columnIndex;
|
||||
sortingMeta.Order = columnGrouping
|
||||
.First(_group => _group.Key == columnIndex)
|
||||
.Count();
|
||||
|
||||
table[columnIndex].Add(sortingMeta);
|
||||
|
||||
metaToSort.Remove(sortingMeta);
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
return meta.OrderByDescending(_meta => _meta.When)
|
||||
.Take(count)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Database.Models.EFClient;
|
||||
|
||||
namespace SharedLibraryCore.Services
|
||||
{
|
||||
@ -18,91 +17,56 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
// make bans propogate to all aliases
|
||||
if (newEntity.Type == Penalty.PenaltyType.Ban)
|
||||
var penalty = new EFPenalty()
|
||||
{
|
||||
await context.Clients
|
||||
.Include(c => c.ReceivedPenalties)
|
||||
.Where(c => c.AliasLinkId == newEntity.Link.AliasLinkId)
|
||||
.ForEachAsync(c =>
|
||||
{
|
||||
if (c.Level != Permission.Banned)
|
||||
{
|
||||
c.Level = Permission.Banned;
|
||||
c.ReceivedPenalties.Add(new EFPenalty()
|
||||
{
|
||||
Active = true,
|
||||
OffenderId = c.ClientId,
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = c.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = DateTime.UtcNow,
|
||||
AutomatedOffense = newEntity.AutomatedOffense,
|
||||
IsEvadedOffense = newEntity.IsEvadedOffense
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// make flags propogate to all aliases
|
||||
else if (newEntity.Type == Penalty.PenaltyType.Flag)
|
||||
{
|
||||
await context.Clients
|
||||
.Include(c => c.ReceivedPenalties)
|
||||
.Where(c => c.AliasLinkId == newEntity.Link.AliasLinkId)
|
||||
.ForEachAsync(c =>
|
||||
{
|
||||
if (c.Level != Permission.Flagged)
|
||||
{
|
||||
c.Level = Permission.Flagged;
|
||||
c.ReceivedPenalties.Add(new EFPenalty()
|
||||
{
|
||||
Active = true,
|
||||
OffenderId = c.ClientId,
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = c.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = DateTime.UtcNow,
|
||||
AutomatedOffense = newEntity.AutomatedOffense,
|
||||
IsEvadedOffense = newEntity.IsEvadedOffense
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// we just want to add it to the database
|
||||
else
|
||||
{
|
||||
var penalty = new EFPenalty()
|
||||
{
|
||||
Active = true,
|
||||
OffenderId = newEntity.Offender.ClientId,
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = newEntity.Link.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = DateTime.UtcNow,
|
||||
AutomatedOffense = newEntity.AutomatedOffense,
|
||||
IsEvadedOffense = newEntity.IsEvadedOffense
|
||||
};
|
||||
|
||||
newEntity.Offender.ReceivedPenalties?.Add(penalty);
|
||||
context.Penalties.Add(penalty);
|
||||
}
|
||||
Active = true,
|
||||
OffenderId = newEntity.Offender.ClientId,
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = newEntity.Link.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = DateTime.UtcNow,
|
||||
AutomatedOffense = newEntity.AutomatedOffense,
|
||||
IsEvadedOffense = newEntity.IsEvadedOffense
|
||||
};
|
||||
|
||||
context.Penalties.Add(penalty);
|
||||
await context.SaveChangesAsync();
|
||||
return newEntity;
|
||||
}
|
||||
}
|
||||
|
||||
public Task<EFPenalty> CreateProxy()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
// certain penalties we want to save across all profiles
|
||||
if (penalty.Type.ShouldPenaltyApplyToAllProfiles())
|
||||
{
|
||||
var iqLinkedProfiles = context.Clients
|
||||
.Where(_client => _client.AliasLinkId == newEntity.Link.AliasLinkId)
|
||||
.Where(_client => _client.Level != EFClient.Permission.Banned)
|
||||
// prevent adding the penalty twice to the same profile
|
||||
.Where(_client => _client.ClientId != penalty.OffenderId);
|
||||
|
||||
await iqLinkedProfiles.ForEachAsync(_client =>
|
||||
{
|
||||
|
||||
var linkedPenalty = new EFPenalty()
|
||||
{
|
||||
OffenderId = _client.ClientId,
|
||||
PunisherId = newEntity.Punisher.ClientId,
|
||||
LinkId = newEntity.Link.AliasLinkId,
|
||||
Type = newEntity.Type,
|
||||
Expires = newEntity.Expires,
|
||||
Offense = newEntity.Offense,
|
||||
When = DateTime.UtcNow,
|
||||
AutomatedOffense = newEntity.AutomatedOffense,
|
||||
IsEvadedOffense = newEntity.IsEvadedOffense
|
||||
};
|
||||
|
||||
context.Penalties.Add(linkedPenalty);
|
||||
});
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
return newEntity;
|
||||
}
|
||||
|
||||
public Task<EFPenalty> Delete(EFPenalty entity)
|
||||
@ -130,145 +94,82 @@ namespace SharedLibraryCore.Services
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<IList<EFPenalty>> GetRecentPenalties(int count, int offset, Penalty.PenaltyType showOnly = Penalty.PenaltyType.Any)
|
||||
public async Task<IList<PenaltyInfo>> GetRecentPenalties(int count, int offset, Penalty.PenaltyType showOnly = Penalty.PenaltyType.Any)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
return await context.Penalties
|
||||
.Include(p => p.Offender.CurrentAlias)
|
||||
.Include(p => p.Punisher.CurrentAlias)
|
||||
.Where(p => showOnly == Penalty.PenaltyType.Any ? p.Type != Penalty.PenaltyType.Any : p.Type == showOnly)
|
||||
.Where(p => p.Active)
|
||||
.OrderByDescending(p => p.When)
|
||||
.Skip(offset)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
var iqPenalties = context.Penalties
|
||||
.Where(p => showOnly == Penalty.PenaltyType.Any ? p.Type != Penalty.PenaltyType.Any : p.Type == showOnly)
|
||||
.OrderByDescending(p => p.When)
|
||||
.Skip(offset)
|
||||
.Take(count)
|
||||
.Select(_penalty => new PenaltyInfo()
|
||||
{
|
||||
Id = _penalty.PenaltyId,
|
||||
Offense = _penalty.Offense,
|
||||
AutomatedOffense = _penalty.AutomatedOffense,
|
||||
OffenderId = _penalty.OffenderId,
|
||||
OffenderName = _penalty.Offender.CurrentAlias.Name,
|
||||
PunisherId = _penalty.PunisherId,
|
||||
PunisherName = _penalty.Punisher.CurrentAlias.Name,
|
||||
PunisherLevel = _penalty.Punisher.Level,
|
||||
PenaltyType = _penalty.Type,
|
||||
Expires = _penalty.Expires,
|
||||
TimePunished = _penalty.When,
|
||||
IsEvade = _penalty.IsEvadedOffense
|
||||
});
|
||||
|
||||
public async Task<IList<EFPenalty>> GetClientPenaltiesAsync(int clientId)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
{
|
||||
return await context.Penalties
|
||||
.Where(p => p.OffenderId == clientId)
|
||||
.Where(p => p.Active)
|
||||
.Include(p => p.Offender.CurrentAlias)
|
||||
.Include(p => p.Punisher.CurrentAlias)
|
||||
.ToListAsync();
|
||||
#if DEBUG == true
|
||||
var querySql = iqPenalties.ToSql();
|
||||
#endif
|
||||
return await iqPenalties.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a read-only copy of client penalties
|
||||
/// retrieves penalty information for meta service
|
||||
/// </summary>
|
||||
/// <param name="clientId"></param>
|
||||
/// <param name="victim">Retreive penalties for clients receiving penalties, other wise given</param>
|
||||
/// <param name="clientId">database id of the client</param>
|
||||
/// <param name="count">how many items to retrieve</param>
|
||||
/// <param name="offset">not used</param>
|
||||
/// <param name="startAt">retreive penalties older than this</param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<ProfileMeta>> ReadGetClientPenaltiesAsync(int clientId, bool victim = true)
|
||||
public async Task<IList<PenaltyInfo>> GetClientPenaltyForMetaAsync(int clientId, int count, int offset, DateTime? startAt)
|
||||
{
|
||||
using (var context = new DatabaseContext(true))
|
||||
using (var ctx = new DatabaseContext(true))
|
||||
{
|
||||
// todo: clean this up
|
||||
if (victim)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var iqPenalties = from penalty in context.Penalties.AsNoTracking()
|
||||
where penalty.OffenderId == clientId
|
||||
join victimClient in context.Clients.AsNoTracking()
|
||||
on penalty.OffenderId equals victimClient.ClientId
|
||||
join victimAlias in context.Aliases.AsNoTracking()
|
||||
on victimClient.CurrentAliasId equals victimAlias.AliasId
|
||||
join punisherClient in context.Clients.AsNoTracking()
|
||||
on penalty.PunisherId equals punisherClient.ClientId
|
||||
join punisherAlias in context.Aliases.AsNoTracking()
|
||||
on punisherClient.CurrentAliasId equals punisherAlias.AliasId
|
||||
//orderby penalty.When descending
|
||||
select new ProfileMeta()
|
||||
{
|
||||
Key = "Event.Penalty",
|
||||
Value = new PenaltyInfo
|
||||
{
|
||||
Id = penalty.PenaltyId,
|
||||
OffenderName = victimAlias.Name,
|
||||
OffenderId = victimClient.ClientId,
|
||||
PunisherName = punisherAlias.Name,
|
||||
PunisherId = penalty.PunisherId,
|
||||
Offense = penalty.Offense,
|
||||
Type = penalty.Type.ToString(),
|
||||
TimeRemaining = penalty.Expires.HasValue ? (now > penalty.Expires ? "" : penalty.Expires.ToString()) : DateTime.MaxValue.ToString(),
|
||||
AutomatedOffense = penalty.AutomatedOffense
|
||||
},
|
||||
When = penalty.When,
|
||||
Sensitive = penalty.Type == Penalty.PenaltyType.Flag
|
||||
};
|
||||
// fixme: is this good and fast?
|
||||
var list = await iqPenalties.ToListAsync();
|
||||
list.ForEach(p =>
|
||||
var iqPenalties = ctx.Penalties.AsNoTracking()
|
||||
//.Where(_penalty => _penalty.Active)
|
||||
.Where(_penalty => _penalty.OffenderId == clientId || _penalty.PunisherId == clientId)
|
||||
.Where(_penalty => _penalty.When < startAt)
|
||||
.OrderByDescending(_penalty => _penalty.When)
|
||||
.Skip(offset)
|
||||
.Take(count)
|
||||
.Select(_penalty => new PenaltyInfo()
|
||||
{
|
||||
// todo: why does this have to be done?
|
||||
if (((PenaltyInfo)p.Value).Type.Length < 2)
|
||||
{
|
||||
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
|
||||
}
|
||||
|
||||
var pi = ((PenaltyInfo)p.Value);
|
||||
if (pi.TimeRemaining?.Length > 0)
|
||||
{
|
||||
pi.TimeRemaining = (DateTime.Parse(((PenaltyInfo)p.Value).TimeRemaining) - now).TimeSpanText();
|
||||
}
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var iqPenalties = from penalty in context.Penalties.AsNoTracking()
|
||||
where penalty.PunisherId == clientId
|
||||
join victimClient in context.Clients.AsNoTracking()
|
||||
on penalty.OffenderId equals victimClient.ClientId
|
||||
join victimAlias in context.Aliases
|
||||
on victimClient.CurrentAliasId equals victimAlias.AliasId
|
||||
join punisherClient in context.Clients
|
||||
on penalty.PunisherId equals punisherClient.ClientId
|
||||
join punisherAlias in context.Aliases
|
||||
on punisherClient.CurrentAliasId equals punisherAlias.AliasId
|
||||
//orderby penalty.When descending
|
||||
select new ProfileMeta()
|
||||
{
|
||||
Key = "Event.Penalty",
|
||||
Value = new PenaltyInfo
|
||||
{
|
||||
Id = penalty.PenaltyId,
|
||||
OffenderName = victimAlias.Name,
|
||||
OffenderId = victimClient.ClientId,
|
||||
PunisherName = punisherAlias.Name,
|
||||
PunisherId = penalty.PunisherId,
|
||||
Offense = penalty.Offense,
|
||||
Type = penalty.Type.ToString(),
|
||||
AutomatedOffense = penalty.AutomatedOffense
|
||||
},
|
||||
When = penalty.When,
|
||||
Sensitive = penalty.Type == Penalty.PenaltyType.Flag
|
||||
};
|
||||
// fixme: is this good and fast?
|
||||
var list = await iqPenalties.ToListAsync();
|
||||
|
||||
list.ForEach(p =>
|
||||
{
|
||||
// todo: why does this have to be done?
|
||||
if (((PenaltyInfo)p.Value).Type.Length < 2)
|
||||
{
|
||||
((PenaltyInfo)p.Value).Type = ((Penalty.PenaltyType)Convert.ToInt32(((PenaltyInfo)p.Value).Type)).ToString();
|
||||
}
|
||||
Id = _penalty.PenaltyId,
|
||||
Offense = _penalty.Offense,
|
||||
AutomatedOffense = _penalty.AutomatedOffense,
|
||||
OffenderId = _penalty.OffenderId,
|
||||
OffenderName = _penalty.Offender.CurrentAlias.Name,
|
||||
PunisherId = _penalty.PunisherId,
|
||||
PunisherName = _penalty.Punisher.CurrentAlias.Name,
|
||||
PunisherLevel = _penalty.Punisher.Level,
|
||||
PenaltyType = _penalty.Type,
|
||||
Expires = _penalty.Expires,
|
||||
TimePunished = _penalty.When,
|
||||
IsEvade = _penalty.IsEvadedOffense
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
#if DEBUG == true
|
||||
var querySql = iqPenalties.ToSql();
|
||||
#endif
|
||||
|
||||
return await iqPenalties.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int? ip = null)
|
||||
public async Task<List<EFPenalty>> GetActivePenaltiesAsync(int linkId, int? ip = null, bool includePunisherName = false)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
@ -306,31 +207,20 @@ namespace SharedLibraryCore.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RemoveActivePenalties(int aliasLinkId)
|
||||
public async Task RemoveActivePenalties(int aliasLinkId, EFClient origin)
|
||||
{
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var penalties = await context.Penalties
|
||||
var penalties = context.Penalties
|
||||
.Include(p => p.Link.Children)
|
||||
.Where(p => p.LinkId == aliasLinkId)
|
||||
.Where(p => p.Expires > now || p.Expires == null)
|
||||
.ToListAsync();
|
||||
.Where(p => p.Expires > now || p.Expires == null);
|
||||
|
||||
penalties.ForEach(async p =>
|
||||
await penalties.ForEachAsync(p =>
|
||||
{
|
||||
p.Active = false;
|
||||
// reset the player levels
|
||||
if (p.Type == Penalty.PenaltyType.Ban)
|
||||
{
|
||||
using (var internalContext = new DatabaseContext())
|
||||
{
|
||||
await internalContext.Clients
|
||||
.Where(c => c.AliasLinkId == p.LinkId)
|
||||
.ForEachAsync(c => c.Level = EFClient.Permission.User);
|
||||
await internalContext.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
|
@ -17,6 +17,8 @@
|
||||
<Compile Remove="Migrations\20181126232438_AddEndpointToEFServer.cs" />
|
||||
<Compile Remove="Migrations\20181126233300_AddEndpointToEFServer.cs" />
|
||||
<Compile Remove="Migrations\20181127143920_AddEndpointToEFServerUpdateServerIdType.cs" />
|
||||
<Compile Remove="Migrations\20190222234606_AddIndexToEFMeta-KeyAndClientId.cs" />
|
||||
<Compile Remove="Migrations\20190223012312_SetMaxLengthForMetaKey.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -10,10 +10,12 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using static SharedLibraryCore.Objects.Penalty;
|
||||
using static SharedLibraryCore.Server;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
@ -21,7 +23,8 @@ namespace SharedLibraryCore
|
||||
public static class Utilities
|
||||
{
|
||||
#if DEBUG == true
|
||||
public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}";
|
||||
public static string OperatingDirectory => @"X:\IW4MAdmin\Application\bin\Debug\netcoreapp2.2\";
|
||||
//public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}";
|
||||
#else
|
||||
public static string OperatingDirectory => $"{Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)}{Path.DirectorySeparatorChar}..{Path.DirectorySeparatorChar}";
|
||||
#endif
|
||||
@ -183,7 +186,7 @@ namespace SharedLibraryCore
|
||||
return CurrentLocalization.LocalizationIndex[$"GLOBAL_PERMISSION_{perm.ToString().ToUpper()}"];
|
||||
}
|
||||
|
||||
public static String ProcessMessageToken(this Server server, IList<Helpers.MessageToken> tokens, String str)
|
||||
public async static Task<string> ProcessMessageToken(this Server server, IList<Helpers.MessageToken> tokens, String str)
|
||||
{
|
||||
MatchCollection RegexMatches = Regex.Matches(str, @"\{\{[A-Z]+\}\}", RegexOptions.IgnoreCase);
|
||||
foreach (Match M in RegexMatches)
|
||||
@ -195,7 +198,7 @@ namespace SharedLibraryCore
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
str = str.Replace(Match, found.Process(server));
|
||||
str = str.Replace(Match, await found.ProcessAsync(server));
|
||||
}
|
||||
}
|
||||
|
||||
@ -266,6 +269,7 @@ namespace SharedLibraryCore
|
||||
public static long ConvertLong(this string str)
|
||||
{
|
||||
str = str.Substring(0, Math.Min(str.Length, 16));
|
||||
int maxBots = 18;
|
||||
long id;
|
||||
|
||||
if (str.Length <= 11) // 10 numeric characters + signed character
|
||||
@ -285,7 +289,7 @@ namespace SharedLibraryCore
|
||||
if (!string.IsNullOrEmpty(bot))
|
||||
{
|
||||
// should set their GUID to the negation of their 1 based index (-1 - -18)
|
||||
return -(Convert.ToInt64(bot.Substring(3)) + 1);
|
||||
return -(Convert.ToInt64(bot.Substring(3)) + 1) % maxBots;
|
||||
}
|
||||
|
||||
return long.MinValue;
|
||||
@ -478,9 +482,23 @@ namespace SharedLibraryCore
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
public static bool ShouldPenaltyApplyToAllProfiles(this PenaltyType penaltyType)
|
||||
{
|
||||
return penaltyType == PenaltyType.Ban ||
|
||||
penaltyType == PenaltyType.Unban ||
|
||||
penaltyType == PenaltyType.Flag ||
|
||||
penaltyType == PenaltyType.Unflag ||
|
||||
penaltyType == PenaltyType.TempBan;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper extension that determines if a user is a privileged client
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPrivileged(this EFClient p)
|
||||
{
|
||||
return p.Level > EFClient.Permission.User;
|
||||
return p.Level > EFClient.Permission.Flagged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -596,19 +614,6 @@ namespace SharedLibraryCore
|
||||
return response;
|
||||
}
|
||||
|
||||
public static int ClientIdFromString(String[] lineSplit, int cIDPos)
|
||||
{
|
||||
int pID = -2; // apparently falling = -1 cID so i can't use it now
|
||||
int.TryParse(lineSplit[cIDPos].Trim(), out pID);
|
||||
|
||||
if (pID == -1) // special case similar to mod_suicide
|
||||
{
|
||||
int.TryParse(lineSplit[2], out pID);
|
||||
}
|
||||
|
||||
return pID;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> DictionaryFromKeyValue(this string eventLine)
|
||||
{
|
||||
string[] values = eventLine.Substring(1).Split('\\');
|
||||
@ -713,6 +718,76 @@ namespace SharedLibraryCore
|
||||
return Assembly.GetCallingAssembly().GetName().Version.ToString();
|
||||
}
|
||||
|
||||
public static string FormatExt(this string input, params object[] values)
|
||||
{
|
||||
var matches = Regex.Matches(Regex.Unescape(input), @"{{\w+}}");
|
||||
string output = input;
|
||||
int index = 0;
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
output = output.Replace(match.Value.ToString(), $"{{{index.ToString()}}}");
|
||||
index++;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return string.Format(output, values);
|
||||
}
|
||||
catch { return input; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// https://stackoverflow.com/questions/8113546/how-to-determine-whether-an-ip-address-in-private/39120248
|
||||
/// An extension method to determine if an IP address is internal, as specified in RFC1918
|
||||
/// </summary>
|
||||
/// <param name="toTest">The IP address that will be tested</param>
|
||||
/// <returns>Returns true if the IP is internal, false if it is external</returns>
|
||||
public static bool IsInternal(this IPAddress toTest)
|
||||
{
|
||||
if (toTest.ToString().StartsWith("127.0.0"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] bytes = toTest.GetAddressBytes();
|
||||
switch (bytes[0])
|
||||
{
|
||||
case 10:
|
||||
return true;
|
||||
case 172:
|
||||
return bytes[1] < 32 && bytes[1] >= 16;
|
||||
case 192:
|
||||
return bytes[1] == 168;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieves the external IP address of the current running machine
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> GetExternalIP()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var wc = new WebClient())
|
||||
{
|
||||
return await wc.DownloadStringTaskAsync("https://api.ipify.org");
|
||||
}
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsQuickMessage(this string message)
|
||||
{
|
||||
return Regex.IsMatch(message, @"^\u0014(?:[A-Z]|_)+$");
|
||||
}
|
||||
|
||||
#if DEBUG == true
|
||||
|
||||
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
@ -10,6 +10,11 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class AccountController : BaseController
|
||||
{
|
||||
/// <summary>
|
||||
/// life span in months
|
||||
/// </summary>
|
||||
private const int COOKIE_LIFESPAN = 3;
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> LoginAsync(int clientId, string password)
|
||||
{
|
||||
@ -20,19 +25,23 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
try
|
||||
{
|
||||
var client = Manager.GetPrivilegedClients()[clientId];
|
||||
//#if DEBUG == true
|
||||
// var client = Utilities.IW4MAdminClient();
|
||||
// bool loginSuccess = true;
|
||||
//#else
|
||||
var privilegedClient = Manager.GetPrivilegedClients()[clientId];
|
||||
bool loginSuccess = Manager.TokenAuthenticator.AuthorizeToken(privilegedClient.NetworkId, password) ||
|
||||
(await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(password, privilegedClient.PasswordSalt)))[0] == privilegedClient.Password;
|
||||
//#endif
|
||||
|
||||
// string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(password, client.PasswordSalt));
|
||||
//if (hashedPassword[0] == client.Password)
|
||||
|
||||
if (Manager.TokenAuthenticator.AuthorizeToken(client.NetworkId, password))
|
||||
if (loginSuccess)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, client.Name),
|
||||
new Claim(ClaimTypes.Role, client.Level.ToString()),
|
||||
new Claim(ClaimTypes.Sid, client.ClientId.ToString()),
|
||||
new Claim(ClaimTypes.PrimarySid, client.NetworkId.ToString())
|
||||
new Claim(ClaimTypes.NameIdentifier, privilegedClient.Name),
|
||||
new Claim(ClaimTypes.Role, privilegedClient.Level.ToString()),
|
||||
new Claim(ClaimTypes.Sid, privilegedClient.ClientId.ToString()),
|
||||
new Claim(ClaimTypes.PrimarySid, privilegedClient.NetworkId.ToString("X"))
|
||||
};
|
||||
|
||||
var claimsIdentity = new ClaimsIdentity(claims, "login");
|
||||
@ -40,7 +49,7 @@ namespace WebfrontCore.Controllers
|
||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrinciple, new AuthenticationProperties()
|
||||
{
|
||||
AllowRefresh = true,
|
||||
ExpiresUtc = DateTime.UtcNow.AddDays(30),
|
||||
ExpiresUtc = DateTime.UtcNow.AddMonths(COOKIE_LIFESPAN),
|
||||
IsPersistent = true,
|
||||
IssuedUtc = DateTime.UtcNow
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ namespace WebfrontCore.Controllers
|
||||
}));
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GenerateLoginTokenForm()
|
||||
public IActionResult GenerateLoginTokenForm()
|
||||
{
|
||||
var info = new ActionInfo()
|
||||
{
|
||||
|
@ -59,17 +59,23 @@ namespace WebfrontCore.Controllers
|
||||
{
|
||||
ClientId = -1,
|
||||
Level = EFClient.Permission.User,
|
||||
CurrentAlias = new EFAlias() { Name = "Web Console Guest" }
|
||||
CurrentAlias = new EFAlias() { Name = "Webfront Guest" }
|
||||
};
|
||||
|
||||
if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
|
||||
{
|
||||
try
|
||||
{
|
||||
Client.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
|
||||
Client.NetworkId = User.Claims.First(_claim => _claim.Type == ClaimTypes.PrimarySid).Value.ConvertLong();
|
||||
Client.Level = (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
|
||||
Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
||||
int clientId = Convert.ToInt32(User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Sid)?.Value ?? "-1");
|
||||
|
||||
if (clientId > 0)
|
||||
{
|
||||
Client.ClientId = clientId;
|
||||
Client.NetworkId = User.Claims.First(_claim => _claim.Type == ClaimTypes.PrimarySid).Value.ConvertLong();
|
||||
Client.Level = (EFClient.Permission)Enum.Parse(typeof(EFClient.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
|
||||
Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
||||
Authorized = Client.ClientId >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
catch (InvalidOperationException)
|
||||
@ -77,7 +83,7 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
}
|
||||
|
||||
catch (System.Collections.Generic.KeyNotFoundException)
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
// force the "banned" client to be signed out
|
||||
HttpContext.SignOutAsync().Wait(5000);
|
||||
@ -93,7 +99,6 @@ namespace WebfrontCore.Controllers
|
||||
Authorized = true;
|
||||
}
|
||||
|
||||
Authorized = Client.ClientId >= 0;
|
||||
ViewBag.Authorized = Authorized;
|
||||
ViewBag.Url = Manager.GetApplicationSettings().Configuration().WebfrontUrl;
|
||||
ViewBag.User = Client;
|
||||
|
@ -15,6 +15,7 @@ namespace WebfrontCore.Controllers
|
||||
public async Task<IActionResult> ProfileAsync(int id)
|
||||
{
|
||||
var client = await Manager.GetClientService().Get(id);
|
||||
|
||||
if (client == null)
|
||||
{
|
||||
return NotFound();
|
||||
@ -22,10 +23,6 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId, client.IPAddress);
|
||||
|
||||
#if DEBUG
|
||||
Authorized = true;
|
||||
#endif
|
||||
|
||||
var clientDto = new PlayerInfo()
|
||||
{
|
||||
Name = client.Name,
|
||||
@ -34,10 +31,6 @@ namespace WebfrontCore.Controllers
|
||||
ClientId = client.ClientId,
|
||||
IPAddress = client.IPAddressString,
|
||||
NetworkId = client.NetworkId,
|
||||
ConnectionCount = client.Connections,
|
||||
FirstSeen = Utilities.GetTimePassed(client.FirstConnection, false),
|
||||
LastSeen = Utilities.GetTimePassed(client.LastConnection, false),
|
||||
TimePlayed = Math.Round(client.TotalConnectionTime / 3600.0, 1).ToString("#,##0"),
|
||||
Meta = new List<ProfileMeta>(),
|
||||
Aliases = client.AliasLink.Children
|
||||
.Where(a => a.Name != client.Name)
|
||||
@ -53,61 +46,37 @@ namespace WebfrontCore.Controllers
|
||||
.OrderBy(i => i)
|
||||
.ToList(),
|
||||
HasActivePenalty = activePenalties.Count > 0,
|
||||
ActivePenaltyType = activePenalties.Count > 0 ? activePenalties.First().Type.ToString() : null,
|
||||
Online = Manager.GetActiveClients().FirstOrDefault(c => c.ClientId == client.ClientId) != null,
|
||||
TimeOnline = (DateTime.UtcNow - client.LastConnection).TimeSpanText(),
|
||||
LinkedAccounts = client.LinkedAccounts
|
||||
};
|
||||
|
||||
var meta = await MetaService.GetMeta(client.ClientId);
|
||||
var penaltyMeta = await Manager.GetPenaltyService()
|
||||
.ReadGetClientPenaltiesAsync(client.ClientId);
|
||||
var administeredPenaltiesMeta = await Manager.GetPenaltyService()
|
||||
.ReadGetClientPenaltiesAsync(client.ClientId, false);
|
||||
|
||||
if (Authorized && client.Level > EFClient.Permission.Trusted)
|
||||
var meta = await MetaService.GetRuntimeMeta(client.ClientId, 0, 1, DateTime.UtcNow);
|
||||
var gravatar = await new MetaService().GetPersistentMeta("GravatarEmail", client);
|
||||
if (gravatar != null)
|
||||
{
|
||||
clientDto.Meta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Localization["WEBFRONT_CLIENT_META_MASKED"],
|
||||
Value = client.Masked ? Localization["WEBFRONT_CLIENT_META_TRUE"] : Localization["WEBFRONT_CLIENT_META_FALSE"],
|
||||
Sensitive = true,
|
||||
When = DateTime.MinValue
|
||||
Key = "GravatarEmail",
|
||||
Type = ProfileMeta.MetaType.Other,
|
||||
Value = gravatar.Value
|
||||
});
|
||||
}
|
||||
|
||||
if (Authorized)
|
||||
{
|
||||
clientDto.Meta.AddRange(client.AliasLink.Children
|
||||
.GroupBy(a => a.Name)
|
||||
.Select(a => a.First())
|
||||
.Select(a => new ProfileMeta()
|
||||
{
|
||||
Key = "AliasEvent",
|
||||
Value = $"{Localization["WEBFRONT_CLIENT_META_JOINED"]} {a.Name}",
|
||||
Sensitive = true,
|
||||
When = a.DateAdded
|
||||
}));
|
||||
}
|
||||
var currentPenalty = activePenalties.FirstOrDefault();
|
||||
|
||||
if (Authorized)
|
||||
if (currentPenalty != null && currentPenalty.Type == SharedLibraryCore.Objects.Penalty.PenaltyType.TempBan)
|
||||
{
|
||||
penaltyMeta.ForEach(p => p.Value.Offense = p.Value.AutomatedOffense ?? p.Value.Offense);
|
||||
administeredPenaltiesMeta.ForEach(p => p.Value.Offense = p.Value.AutomatedOffense ?? p.Value.Offense);
|
||||
clientDto.Meta.Add(new ProfileMeta()
|
||||
{
|
||||
Key = Localization["WEBFRONT_CLIENT_META_REMAINING_BAN"],
|
||||
Value = ((currentPenalty.Expires - DateTime.UtcNow) ?? new TimeSpan()).TimeSpanText(),
|
||||
When = currentPenalty.When
|
||||
});
|
||||
}
|
||||
|
||||
clientDto.Meta.AddRange(Authorized ? meta : meta.Where(m => !m.Sensitive));
|
||||
clientDto.Meta.AddRange(Authorized ? penaltyMeta : penaltyMeta.Where(m => !m.Sensitive));
|
||||
clientDto.Meta.AddRange(Authorized ? administeredPenaltiesMeta : administeredPenaltiesMeta.Where(m => !m.Sensitive));
|
||||
clientDto.Meta.AddRange(client.Meta.Select(m => new ProfileMeta()
|
||||
{
|
||||
When = m.Created,
|
||||
Key = m.Key,
|
||||
Value = m.Value,
|
||||
Show = false,
|
||||
}));
|
||||
clientDto.Meta = clientDto.Meta
|
||||
.OrderByDescending(m => m.When)
|
||||
.ToList();
|
||||
|
||||
ViewBag.Title = clientDto.Name.Substring(clientDto.Name.Length - 1).ToLower()[0] == 's' ?
|
||||
clientDto.Name + "'" :
|
||||
@ -122,8 +91,8 @@ namespace WebfrontCore.Controllers
|
||||
public async Task<IActionResult> PrivilegedAsync()
|
||||
{
|
||||
var admins = (await Manager.GetClientService().GetPrivilegedClients())
|
||||
.GroupBy(a => a.AliasLinkId).Where(_clients => _clients.FirstOrDefault(_client => _client.LastConnection == _clients.Max(c => c.LastConnection)) != null)
|
||||
.Select(_client => _client.First())
|
||||
.GroupBy(a => a.AliasLinkId)
|
||||
.Select(_client => _client.OrderByDescending(_c => _c.LastConnection).First())
|
||||
.OrderByDescending(_client => _client.Level);
|
||||
|
||||
var adminsDict = new Dictionary<EFClient.Permission, IList<ClientInfo>>();
|
||||
@ -160,13 +129,30 @@ namespace WebfrontCore.Controllers
|
||||
Name = c.Name,
|
||||
Level = c.Level.ToLocalizedLevelName(),
|
||||
LevelInt = (int)c.Level,
|
||||
ClientId = c.ClientId,
|
||||
LastSeen = Utilities.GetTimePassed(c.LastConnection, false)
|
||||
LastConnection = c.LastConnection,
|
||||
ClientId = c.ClientId
|
||||
})
|
||||
.ToList();
|
||||
|
||||
ViewBag.Title = $"{clientsDto.Count} {Localization["WEBFRONT_CLIENT_SEARCH_MATCHING"]} \"{clientName}\"";
|
||||
return View("Find/Index", clientsDto);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Meta(int id, int count, int offset, DateTime? startAt)
|
||||
{
|
||||
IEnumerable<ProfileMeta> meta = await MetaService.GetRuntimeMeta(id, startAt == null ? offset : 0, count, startAt ?? DateTime.UtcNow);
|
||||
|
||||
if (!Authorized)
|
||||
{
|
||||
meta = meta.Where(_meta => !_meta.Sensitive);
|
||||
}
|
||||
|
||||
if (meta.Count() == 0)
|
||||
{
|
||||
return Ok();
|
||||
}
|
||||
|
||||
return View("Components/ProfileMetaList/_List", meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user