fix aggregate issue with KDR on global top stats

refactor some of the main application code to have a cleaner code flow
add enviroment flag to opt out of .net core telemetry in start script
fixed "a moment" missing the "ago"
fixed case sensitive client searches on postgresql
clean up command code flow
Add missing map "mp_cairo" to default settings
This commit is contained in:
RaidMax 2019-05-08 20:34:17 -05:00
parent 43c4d4af38
commit 1dc0f5a240
22 changed files with 492 additions and 409 deletions

View File

@ -6,7 +6,7 @@
<RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion> <RuntimeFrameworkVersion>2.2.2</RuntimeFrameworkVersion>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish> <MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<PackageId>RaidMax.IW4MAdmin.Application</PackageId> <PackageId>RaidMax.IW4MAdmin.Application</PackageId>
<Version>2.2.7.1</Version> <Version>2.2.7.2</Version>
<Authors>RaidMax</Authors> <Authors>RaidMax</Authors>
<Company>Forever None</Company> <Company>Forever None</Company>
<Product>IW4MAdmin</Product> <Product>IW4MAdmin</Product>
@ -32,8 +32,8 @@
<PropertyGroup> <PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection> <ServerGarbageCollection>true</ServerGarbageCollection>
<TieredCompilation>true</TieredCompilation> <TieredCompilation>true</TieredCompilation>
<AssemblyVersion>2.2.7.1</AssemblyVersion> <AssemblyVersion>2.2.7.2</AssemblyVersion>
<FileVersion>2.2.7.1</FileVersion> <FileVersion>2.2.7.2</FileVersion>
<LangVersion>7.1</LangVersion> <LangVersion>7.1</LangVersion>
</PropertyGroup> </PropertyGroup>
@ -47,12 +47,6 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="DefaultSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" /> <PackageReference Update="Microsoft.NETCore.App" Version="2.2.2" />
</ItemGroup> </ItemGroup>

View File

@ -15,6 +15,7 @@ using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects; using SharedLibraryCore.Objects;
using SharedLibraryCore.Services; using SharedLibraryCore.Services;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -26,7 +27,7 @@ namespace IW4MAdmin.Application
{ {
public class ApplicationManager : IManager public class ApplicationManager : IManager
{ {
private List<Server> _servers; private ConcurrentBag<Server> _servers;
public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList(); public List<Server> Servers => _servers.OrderByDescending(s => s.ClientNum).ToList();
public ILogger Logger => GetLogger(0); public ILogger Logger => GetLogger(0);
public bool Running { get; private set; } public bool Running { get; private set; }
@ -41,8 +42,9 @@ namespace IW4MAdmin.Application
public IList<IRConParser> AdditionalRConParsers { get; } public IList<IRConParser> AdditionalRConParsers { get; }
public IList<IEventParser> AdditionalEventParsers { get; } public IList<IEventParser> AdditionalEventParsers { get; }
public ITokenAuthentication TokenAuthenticator { get; } public ITokenAuthentication TokenAuthenticator { get; }
public CancellationToken CancellationToken => _tokenSource.Token;
public string ExternalIPAddress { get; private set; } public string ExternalIPAddress { get; private set; }
public bool IsRestartRequested { get; private set; }
static ApplicationManager Instance; static ApplicationManager Instance;
readonly List<AsyncStatus> TaskStatuses; readonly List<AsyncStatus> TaskStatuses;
List<Command> Commands; List<Command> Commands;
@ -52,16 +54,16 @@ namespace IW4MAdmin.Application
readonly PenaltyService PenaltySvc; readonly PenaltyService PenaltySvc;
public BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler; public BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
GameEventHandler Handler; GameEventHandler Handler;
ManualResetEventSlim OnQuit;
readonly IPageList PageList; readonly IPageList PageList;
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1); readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim(1, 1);
readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>(); readonly Dictionary<long, ILogger> Loggers = new Dictionary<long, ILogger>();
private readonly MetaService _metaService; private readonly MetaService _metaService;
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0); private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
private readonly CancellationTokenSource _tokenSource;
private ApplicationManager() private ApplicationManager()
{ {
_servers = new List<Server>(); _servers = new ConcurrentBag<Server>();
Commands = new List<Command>(); Commands = new List<Command>();
TaskStatuses = new List<AsyncStatus>(); TaskStatuses = new List<AsyncStatus>();
MessageTokens = new List<MessageToken>(); MessageTokens = new List<MessageToken>();
@ -70,7 +72,6 @@ namespace IW4MAdmin.Application
PenaltySvc = new PenaltyService(); PenaltySvc = new PenaltyService();
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings"); ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
StartTime = DateTime.UtcNow; StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
PageList = new PageList(); PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>(); AdditionalEventParsers = new List<IEventParser>();
AdditionalRConParsers = new List<IRConParser>(); AdditionalRConParsers = new List<IRConParser>();
@ -78,6 +79,7 @@ namespace IW4MAdmin.Application
OnServerEvent += EventApi.OnGameEvent; OnServerEvent += EventApi.OnGameEvent;
TokenAuthenticator = new TokenAuthentication(); TokenAuthenticator = new TokenAuthentication();
_metaService = new MetaService(); _metaService = new MetaService();
_tokenSource = new CancellationTokenSource();
} }
private async void OnGameEvent(object sender, GameEventArgs args) private async void OnGameEvent(object sender, GameEventArgs args)
@ -155,12 +157,12 @@ namespace IW4MAdmin.Application
return Instance ?? (Instance = new ApplicationManager()); return Instance ?? (Instance = new ApplicationManager());
} }
public async Task UpdateServerStates(CancellationToken token) public async Task UpdateServerStates()
{ {
// store the server hash code and task for it // store the server hash code and task for it
var runningUpdateTasks = new Dictionary<long, Task>(); var runningUpdateTasks = new Dictionary<long, Task>();
while (Running) while (!_tokenSource.IsCancellationRequested)
{ {
// select the server ids that have completed the update task // select the server ids that have completed the update task
var serverTasksToRemove = runningUpdateTasks var serverTasksToRemove = runningUpdateTasks
@ -191,10 +193,11 @@ namespace IW4MAdmin.Application
{ {
try try
{ {
await server.ProcessUpdatesAsync(token); await server.ProcessUpdatesAsync(_tokenSource.Token);
if (server.Throttled) if (server.Throttled)
{ {
await Task.Delay((int)_throttleTimeout.TotalMilliseconds); await Task.Delay((int)_throttleTimeout.TotalMilliseconds, _tokenSource.Token);
} }
} }
@ -218,7 +221,7 @@ namespace IW4MAdmin.Application
#endif #endif
try try
{ {
await Task.Delay(ConfigHandler.Configuration().RConPollRate, token); await Task.Delay(ConfigHandler.Configuration().RConPollRate, _tokenSource.Token);
} }
// if a cancellation is received, we want to return immediately // if a cancellation is received, we want to return immediately
catch { break; } catch { break; }
@ -341,7 +344,7 @@ namespace IW4MAdmin.Application
GetApplicationSettings().Configuration()?.DatabaseProvider)) GetApplicationSettings().Configuration()?.DatabaseProvider))
{ {
await new ContextSeed(db).Seed(); await new ContextSeed(db).Seed();
} }
#endregion #endregion
#region COMMANDS #region COMMANDS
@ -351,6 +354,7 @@ namespace IW4MAdmin.Application
} }
Commands.Add(new CQuit()); Commands.Add(new CQuit());
Commands.Add(new CRestart());
Commands.Add(new CKick()); Commands.Add(new CKick());
Commands.Add(new CSay()); Commands.Add(new CSay());
Commands.Add(new CTempBan()); Commands.Add(new CTempBan());
@ -517,7 +521,12 @@ namespace IW4MAdmin.Application
MetaService.AddRuntimeMeta(getPenaltyMeta); MetaService.AddRuntimeMeta(getPenaltyMeta);
#endregion #endregion
#region INIT await InitializeServers();
}
private async Task InitializeServers()
{
var config = ConfigHandler.Configuration();
int successServers = 0; int successServers = 0;
Exception lastException = null; Exception lastException = null;
@ -531,10 +540,7 @@ namespace IW4MAdmin.Application
var ServerInstance = new IW4MServer(this, Conf); var ServerInstance = new IW4MServer(this, Conf);
await ServerInstance.Initialize(); await ServerInstance.Initialize();
lock (_servers) _servers.Add(ServerInstance);
{
_servers.Add(ServerInstance);
}
Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname)); Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"].FormatExt(ServerInstance.Hostname));
// add the start event for this server // add the start event for this server
@ -577,25 +583,25 @@ namespace IW4MAdmin.Application
throw lastException; throw lastException;
} }
} }
#endregion
} }
private async Task SendHeartbeat(object state)
{
var heartbeatState = (HeartbeatState)state;
while (Running) private async Task SendHeartbeat()
{
bool connected = false;
while (!_tokenSource.IsCancellationRequested)
{ {
if (!heartbeatState.Connected) if (!connected)
{ {
try try
{ {
await Heartbeat.Send(this, true); await Heartbeat.Send(this, true);
heartbeatState.Connected = true; connected = true;
} }
catch (Exception e) catch (Exception e)
{ {
heartbeatState.Connected = false; connected = false;
Logger.WriteWarning($"Could not connect to heartbeat server - {e.Message}"); Logger.WriteWarning($"Could not connect to heartbeat server - {e.Message}");
} }
} }
@ -621,7 +627,7 @@ namespace IW4MAdmin.Application
{ {
if (((RestEase.ApiException)ex).StatusCode == System.Net.HttpStatusCode.Unauthorized) if (((RestEase.ApiException)ex).StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
heartbeatState.Connected = false; connected = false;
} }
} }
} }
@ -631,9 +637,10 @@ namespace IW4MAdmin.Application
Logger.WriteWarning($"Could not send heartbeat - {e.Message}"); Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized) if (e.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{ {
heartbeatState.Connected = false; connected = false;
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.WriteWarning($"Could not send heartbeat - {e.Message}"); Logger.WriteWarning($"Could not send heartbeat - {e.Message}");
@ -643,31 +650,32 @@ namespace IW4MAdmin.Application
try try
{ {
await Task.Delay(30000, heartbeatState.Token); await Task.Delay(30000, _tokenSource.Token);
} }
catch { break; } catch { break; }
} }
} }
public void Start() public Task Start()
{ {
var tokenSource = new CancellationTokenSource(); return Task.WhenAll(new[]
// this needs to be run seperately from the main thread
_ = Task.Run(() => SendHeartbeat(new HeartbeatState() { Token = tokenSource.Token }));
_ = Task.Run(() => UpdateServerStates(tokenSource.Token));
while (Running)
{ {
OnQuit.Wait(); SendHeartbeat(),
tokenSource.Cancel(); UpdateServerStates()
OnQuit.Reset(); });
}
} }
public void Stop() public void Stop()
{ {
_tokenSource.Cancel();
Running = false; Running = false;
OnQuit.Set(); Instance = null;
}
public void Restart()
{
IsRestartRequested = true;
Stop();
} }
public ILogger GetLogger(long serverId) public ILogger GetLogger(long serverId)
@ -725,21 +733,11 @@ namespace IW4MAdmin.Application
return ConfigHandler; return ConfigHandler;
} }
public bool ShutdownRequested()
{
return !Running;
}
public IEventHandler GetEventHandler() public IEventHandler GetEventHandler()
{ {
return Handler; return Handler;
} }
public void SetHasEvent()
{
}
public IList<Assembly> GetPluginAssemblies() public IList<Assembly> GetPluginAssemblies()
{ {
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList(); return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList();

View File

@ -108,8 +108,8 @@ if "%CurrentConfiguration%" == "Release" (
) )
echo making start scripts echo making start scripts
@(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 set DOTNET_CLI_TELEMETRY_OPTOUT=1 && 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 @echo off && echo @title IW4MAdmin && echo set DOTNET_CLI_TELEMETRY_OPTOUT=1 && 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 export DOTNET_CLI_TELEMETRY_OPTOUT=1 && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\WindowsPrerelease\StartIW4MAdmin.sh"
@(echo #!/bin/bash && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh" @(echo #!/bin/bash && echo export DOTNET_CLI_TELEMETRY_OPTOUT=1 && echo dotnet Lib/IW4MAdmin.dll) > "%SolutionDir%Publish\Windows\StartIW4MAdmin.sh"

View File

@ -16,7 +16,7 @@
"Keep grenade launcher use to a minimum", "Keep grenade launcher use to a minimum",
"Balance teams at ALL times" "Balance teams at ALL times"
], ],
"DisallowedClientNames": ["Unknown Soldier", "VickNet", "UnknownSoldier", "CHEATER"], "DisallowedClientNames": [ "Unknown Soldier", "VickNet", "UnknownSoldier", "CHEATER", "Play77" ],
"QuickMessages": [ "QuickMessages": [
{ {
"Game": "IW4", "Game": "IW4",
@ -517,6 +517,10 @@
"Alias": "Hanoi", "Alias": "Hanoi",
"Name": "mp_hanoi" "Name": "mp_hanoi"
}, },
{
"Alias": "Havana",
"Name": "mp_cairo"
},
{ {
"Alias": "Hazard", "Alias": "Hazard",
"Name": "mp_golfcourse" "Name": "mp_golfcourse"

View File

@ -8,10 +8,10 @@ namespace IW4MAdmin.Application.IO
{ {
class GameLogEventDetection class GameLogEventDetection
{ {
Server Server; private long previousFileSize;
long PreviousFileSize; private readonly Server _server;
IGameLogReader Reader; private readonly IGameLogReader _reader;
readonly string GameLogFile; private readonly string _gameLogFile;
class EventState class EventState
{ {
@ -21,16 +21,16 @@ namespace IW4MAdmin.Application.IO
public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri) public GameLogEventDetection(Server server, string gameLogPath, Uri gameLogServerUri)
{ {
GameLogFile = gameLogPath; _gameLogFile = gameLogPath;
Reader = gameLogServerUri != null ? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser) : Reader = new GameLogReader(gameLogPath, server.EventParser); _reader = gameLogServerUri != null ? new GameLogReaderHttp(gameLogServerUri, gameLogPath, server.EventParser) : _reader = new GameLogReader(gameLogPath, server.EventParser);
Server = server; _server = server;
} }
public async Task PollForChanges() public async Task PollForChanges()
{ {
while (!Server.Manager.ShutdownRequested()) while (!_server.Manager.CancellationToken.IsCancellationRequested)
{ {
if (Server.IsInitialized) if (_server.IsInitialized)
{ {
try try
{ {
@ -39,39 +39,40 @@ namespace IW4MAdmin.Application.IO
catch (Exception e) catch (Exception e)
{ {
Server.Logger.WriteWarning($"Failed to update log event for {Server.EndPoint}"); _server.Logger.WriteWarning($"Failed to update log event for {_server.EndPoint}");
Server.Logger.WriteDebug($"Exception: {e.Message}"); _server.Logger.WriteDebug($"Exception: {e.Message}");
Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}"); _server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
} }
} }
Thread.Sleep(Reader.UpdateInterval);
await Task.Delay(_reader.UpdateInterval, _server.Manager.CancellationToken);
} }
} }
private async Task UpdateLogEvents() private async Task UpdateLogEvents()
{ {
long fileSize = Reader.Length; long fileSize = _reader.Length;
if (PreviousFileSize == 0) if (previousFileSize == 0)
PreviousFileSize = fileSize; previousFileSize = fileSize;
long fileDiff = fileSize - PreviousFileSize; long fileDiff = fileSize - previousFileSize;
// this makes the http log get pulled // this makes the http log get pulled
if (fileDiff < 1 && fileSize != -1) if (fileDiff < 1 && fileSize != -1)
return; return;
PreviousFileSize = fileSize; previousFileSize = fileSize;
var events = await Reader.ReadEventsFromLog(Server, fileDiff, 0); var events = await _reader.ReadEventsFromLog(_server, fileDiff, 0);
foreach (var ev in events) foreach (var ev in events)
{ {
Server.Manager.GetEventHandler().AddEvent(ev); _server.Manager.GetEventHandler().AddEvent(ev);
await ev.WaitAsync(); await ev.WaitAsync(Utilities.DefaultCommandTimeout, _server.Manager.CancellationToken);
} }
PreviousFileSize = fileSize; previousFileSize = fileSize;
} }
} }
} }

View File

@ -296,6 +296,20 @@ namespace IW4MAdmin
Target = E.Target, Target = E.Target,
Reason = E.Data Reason = E.Data
}); });
Penalty newReport = new Penalty()
{
Type = Penalty.PenaltyType.Report,
Expires = DateTime.UtcNow,
Offender = E.Target,
Offense = E.Message,
Punisher = E.Origin,
Active = true,
When = DateTime.UtcNow,
Link = E.Target.AliasLink
};
await Manager.GetPenaltyService().Create(newReport);
} }
else if (E.Type == GameEvent.EventType.TempBan) else if (E.Type == GameEvent.EventType.TempBan)
@ -548,7 +562,7 @@ namespace IW4MAdmin
try try
{ {
#region SHUTDOWN #region SHUTDOWN
if (Manager.ShutdownRequested()) if (Manager.CancellationToken.IsCancellationRequested)
{ {
foreach (var client in GetClientsAsList()) foreach (var client in GetClientsAsList())
{ {
@ -560,7 +574,7 @@ namespace IW4MAdmin
}; };
Manager.GetEventHandler().AddEvent(e); Manager.GetEventHandler().AddEvent(e);
await e.WaitAsync(); await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
} }
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins) foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
@ -597,7 +611,7 @@ namespace IW4MAdmin
waiterList.Add(e); waiterList.Add(e);
} }
// wait for all the disconnect tasks to finish // wait for all the disconnect tasks to finish
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000))); await Task.WhenAll(waiterList.Select(e => e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken)));
waiterList.Clear(); waiterList.Clear();
// this are our new connecting clients // this are our new connecting clients
@ -621,7 +635,7 @@ namespace IW4MAdmin
} }
// wait for all the connect tasks to finish // wait for all the connect tasks to finish
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000))); await Task.WhenAll(waiterList.Select(e => e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken)));
waiterList.Clear(); waiterList.Clear();
// these are the clients that have updated // these are the clients that have updated
@ -638,7 +652,7 @@ namespace IW4MAdmin
waiterList.Add(e); waiterList.Add(e);
} }
await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000))); await Task.WhenAll(waiterList.Select(e => e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken)));
if (ConnectionErrors > 0) if (ConnectionErrors > 0)
{ {

View File

@ -1,204 +1,221 @@
using IW4MAdmin.Application.Migration; using IW4MAdmin.Application.Migration;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Localization;
using System; using System;
using System.IO;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
public class Program public class Program
{ {
static public double Version { get; private set; } public static double Version { get; private set; } = Utilities.GetVersionAsDouble();
static public ApplicationManager ServerManager; public static ApplicationManager ServerManager;
private static ManualResetEventSlim OnShutdownComplete = new ManualResetEventSlim(); private static Task ApplicationTask;
public static void Main(string[] args) /// <summary>
/// entrypoint of the application
/// </summary>
/// <returns></returns>
public static async Task Main()
{ {
AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory); AppDomain.CurrentDomain.SetData("DataDirectory", Utilities.OperatingDirectory);
Console.OutputEncoding = Encoding.UTF8; Console.OutputEncoding = Encoding.UTF8;
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
Version = Utilities.GetVersionAsDouble(); Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
Console.WriteLine("====================================================="); Console.WriteLine("=====================================================");
Console.WriteLine(" IW4M ADMIN"); Console.WriteLine(" IW4MAdmin");
Console.WriteLine(" by RaidMax "); Console.WriteLine(" by RaidMax ");
Console.WriteLine($" Version {Utilities.GetVersionAsString()}"); Console.WriteLine($" Version {Utilities.GetVersionAsString()}");
Console.WriteLine("====================================================="); Console.WriteLine("=====================================================");
Index loc = null; await LaunchAsync();
}
/// <summary>
/// event callback executed when the control + c combination is detected
/// gracefully stops the server manager and waits for all tasks to finish
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static async void OnCancelKey(object sender, ConsoleCancelEventArgs e)
{
ServerManager?.Stop();
await ApplicationTask;
}
/// <summary>
/// task that initializes application and starts the application monitoring and runtime tasks
/// </summary>
/// <returns></returns>
private static async Task LaunchAsync()
{
restart:
try try
{ {
ServerManager = ApplicationManager.GetInstance(); ServerManager = ApplicationManager.GetInstance();
var configuration = ServerManager.GetApplicationSettings().Configuration(); var configuration = ServerManager.GetApplicationSettings().Configuration();
Localization.Configure.Initialize(configuration?.EnableCustomLocale ?? false ? (configuration.CustomLocale ?? "windows-1252") : "windows-1252");
if (configuration != null) // do any needed housekeeping file/folder migrations
{
Localization.Configure.Initialize(configuration.EnableCustomLocale ? (configuration.CustomLocale ?? "windows-1252") : "windows-1252");
}
else
{
Localization.Configure.Initialize();
}
loc = Utilities.CurrentLocalization.LocalizationIndex;
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
CheckDirectories();
// do any needed migrations
// todo: move out
ConfigurationMigration.MoveConfigFolder10518(null); ConfigurationMigration.MoveConfigFolder10518(null);
ConfigurationMigration.CheckDirectories();
ServerManager.Logger.WriteInfo($"Version is {Version}"); ServerManager.Logger.WriteInfo(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_VERSION"].FormatExt(Version));
var api = API.Master.Endpoint.Get(); await CheckVersion();
await ServerManager.Init();
var version = new API.Master.VersionInfo()
{
CurrentVersionStable = 99.99f
};
try
{
version = api.GetVersion().Result;
}
catch (Exception e)
{
ServerManager.Logger.WriteWarning(loc["MANAGER_VERSION_FAIL"]);
while (e.InnerException != null)
{
e = e.InnerException;
}
ServerManager.Logger.WriteDebug(e.Message);
}
if (version.CurrentVersionStable == 99.99f)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
Console.ForegroundColor = ConsoleColor.Gray;
}
#if !PRERELEASE
else if (version.CurrentVersionStable > Version)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}]"));
Console.ForegroundColor = ConsoleColor.Gray;
}
#else
else if (version.CurrentVersionPrerelease > Version)
{
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"].FormatExt($"[v{Version.ToString("0.0")}-pr]"));
Console.ForegroundColor = ConsoleColor.Gray;
}
#endif
else
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
Console.ForegroundColor = ConsoleColor.Gray;
}
ServerManager.Init().Wait();
var consoleTask = Task.Run(async () =>
{
string userInput;
var Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
do
{
userInput = Console.ReadLine();
if (userInput?.ToLower() == "quit")
{
ServerManager.Stop();
}
if (ServerManager.Servers.Count == 0)
{
Console.WriteLine(loc["MANAGER_CONSOLE_NOSERV"]);
continue;
}
if (userInput?.Length > 0)
{
GameEvent E = new GameEvent()
{
Type = GameEvent.EventType.Command,
Data = userInput,
Origin = Origin,
Owner = ServerManager.Servers[0]
};
ServerManager.GetEventHandler().AddEvent(E);
await E.WaitAsync(30 * 1000);
}
Console.Write('>');
} while (ServerManager.Running);
});
} }
catch (Exception e) catch (Exception e)
{ {
var loc = Utilities.CurrentLocalization.LocalizationIndex;
string failMessage = loc == null ? "Failed to initalize IW4MAdmin" : loc["MANAGER_INIT_FAIL"]; string failMessage = loc == null ? "Failed to initalize IW4MAdmin" : loc["MANAGER_INIT_FAIL"];
string exitMessage = loc == null ? "Press any key to exit..." : loc["MANAGER_EXIT"]; string exitMessage = loc == null ? "Press any key to exit..." : loc["MANAGER_EXIT"];
Console.WriteLine(failMessage); Console.WriteLine(failMessage);
while (e.InnerException != null) while (e.InnerException != null)
{ {
e = e.InnerException; e = e.InnerException;
} }
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
Console.WriteLine(exitMessage); Console.WriteLine(exitMessage);
Console.ReadKey(); Console.ReadKey();
return;
} }
if (ServerManager.GetApplicationSettings().Configuration().EnableWebFront) try
{ {
Task.Run(() => WebfrontCore.Program.Init(ServerManager)); ApplicationTask = RunApplicationTasksAsync();
await ApplicationTask;
} }
OnShutdownComplete.Reset(); catch { }
ServerManager.Start();
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]); if (ServerManager.IsRestartRequested)
OnShutdownComplete.Set(); {
goto restart;
}
} }
private static void OnCancelKey(object sender, ConsoleCancelEventArgs e) /// <summary>
/// runs the core application tasks
/// </summary>
/// <returns></returns>
private static async Task RunApplicationTasksAsync()
{ {
ServerManager.Stop(); var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ?
OnShutdownComplete.Wait(); WebfrontCore.Program.Init(ServerManager, ServerManager.CancellationToken) :
Task.CompletedTask;
var tasks = new[]
{
webfrontTask,
ReadConsoleInput(),
ServerManager.Start(),
};
await Task.WhenAll(tasks);
ServerManager.Logger.WriteVerbose(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_SHUTDOWN_SUCCESS"]);
} }
static void CheckDirectories() /// <summary>
/// checks for latest version of the application
/// notifies user if an update is available
/// </summary>
/// <returns></returns>
private static async Task CheckVersion()
{ {
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Plugins"))) var api = API.Master.Endpoint.Get();
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var version = new API.Master.VersionInfo()
{ {
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Plugins")); CurrentVersionStable = 99.99f
};
try
{
version = await api.GetVersion();
} }
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Database"))) catch (Exception e)
{ {
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Database")); ServerManager.Logger.WriteWarning(loc["MANAGER_VERSION_FAIL"]);
while (e.InnerException != null)
{
e = e.InnerException;
}
ServerManager.Logger.WriteDebug(e.Message);
} }
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Log"))) if (version.CurrentVersionStable == 99.99f)
{ {
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Log")); Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
Console.ForegroundColor = ConsoleColor.Gray;
}
#if !PRERELEASE
else if (version.CurrentVersionStable > Version)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
Console.WriteLine(loc["MANAGER_VERSION_CURRENT"].FormatExt($"[v{Version.ToString("0.0")}]"));
Console.ForegroundColor = ConsoleColor.Gray;
}
#else
else if (version.CurrentVersionPrerelease > Version)
{
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"].FormatExt($"[v{Version.ToString("0.0")}-pr]"));
Console.ForegroundColor = ConsoleColor.Gray;
}
#endif
else
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
Console.ForegroundColor = ConsoleColor.Gray;
}
}
/// <summary>
/// reads input from the console and executes entered commands on the default server
/// </summary>
/// <returns></returns>
private static async Task ReadConsoleInput()
{
string lastCommand;
var Origin = Utilities.IW4MAdminClient(ServerManager.Servers[0]);
while (!ServerManager.CancellationToken.IsCancellationRequested)
{
lastCommand = Console.ReadLine();
if (lastCommand?.Length > 0)
{
if (lastCommand?.Length > 0)
{
GameEvent E = new GameEvent()
{
Type = GameEvent.EventType.Command,
Data = lastCommand,
Origin = Origin,
Owner = ServerManager.Servers[0]
};
ServerManager.GetEventHandler().AddEvent(E);
await E.WaitAsync(Utilities.DefaultCommandTimeout, ServerManager.CancellationToken);
Console.Write('>');
}
}
} }
} }
} }

View File

@ -15,6 +15,27 @@ namespace IW4MAdmin.Application.Migration
/// </summary> /// </summary>
class ConfigurationMigration class ConfigurationMigration
{ {
/// <summary>
/// ensures required directories are created
/// </summary>
public static void CheckDirectories()
{
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Plugins")))
{
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Plugins"));
}
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Database")))
{
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Database"));
}
if (!Directory.Exists(Path.Join(Utilities.OperatingDirectory, "Log")))
{
Directory.CreateDirectory(Path.Join(Utilities.OperatingDirectory, "Log"));
}
}
/// <summary> /// <summary>
/// moves existing configs from the root folder into a configs folder /// moves existing configs from the root folder into a configs folder
/// </summary> /// </summary>

View File

@ -55,6 +55,7 @@ namespace IW4MAdmin.Application
void Write(string msg, LogType type) void Write(string msg, LogType type)
{ {
return;
OnLogWriting.Wait(); OnLogWriting.Wait();
string stringType = type.ToString(); string stringType = type.ToString();

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio Version 16
VisualStudioVersion = 15.0.26730.16 VisualStudioVersion = 16.0.28803.352
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8B310-269E-46D4-A612-24601F16065F}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8B310-269E-46D4-A612-24601F16065F}"
EndProject EndProject

View File

@ -20,25 +20,22 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
public class StatManager public class StatManager
{ {
private ConcurrentDictionary<long, ServerStats> Servers; private readonly ConcurrentDictionary<long, ServerStats> _servers;
private ILogger Log; private readonly ILogger _log;
private readonly IManager Manager;
private readonly SemaphoreSlim OnProcessingPenalty; private readonly SemaphoreSlim OnProcessingPenalty;
private readonly SemaphoreSlim OnProcessingSensitive; private readonly SemaphoreSlim OnProcessingSensitive;
public StatManager(IManager mgr) public StatManager(IManager mgr)
{ {
Servers = new ConcurrentDictionary<long, ServerStats>(); _servers = new ConcurrentDictionary<long, ServerStats>();
Log = mgr.GetLogger(0); _log = mgr.GetLogger(0);
Manager = mgr;
OnProcessingPenalty = new SemaphoreSlim(1, 1); OnProcessingPenalty = new SemaphoreSlim(1, 1);
OnProcessingSensitive = new SemaphoreSlim(1, 1); OnProcessingSensitive = new SemaphoreSlim(1, 1);
} }
public EFClientStatistics GetClientStats(int clientId, long serverId) public EFClientStatistics GetClientStats(int clientId, long serverId)
{ {
return Servers[serverId].PlayerStats[clientId]; return _servers[serverId].PlayerStats[clientId];
} }
public static Expression<Func<EFRating, bool>> GetRankingFunc(long? serverId = null) public static Expression<Func<EFRating, bool>> GetRankingFunc(long? serverId = null)
@ -135,6 +132,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
var iqStatsInfo = (from stat in context.Set<EFClientStatistics>() var iqStatsInfo = (from stat in context.Set<EFClientStatistics>()
where clientIds.Contains(stat.ClientId) where clientIds.Contains(stat.ClientId)
where stat.Kills > 0 || stat.Deaths > 0
group stat by stat.ClientId into s group stat by stat.ClientId into s
select new select new
{ {
@ -242,7 +240,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// check to see if the stats have ever been initialized // check to see if the stats have ever been initialized
var serverStats = InitializeServerStats(server.ServerId); var serverStats = InitializeServerStats(server.ServerId);
Servers.TryAdd(serverId, new ServerStats(server, serverStats) _servers.TryAdd(serverId, new ServerStats(server, serverStats)
{ {
IsTeamBased = sv.Gametype != "dm" IsTeamBased = sv.Gametype != "dm"
}); });
@ -250,8 +248,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception e) catch (Exception e)
{ {
Log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}"); _log.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_ERROR_ADD"]} - {e.Message}");
Log.WriteDebug(e.GetExceptionInfo()); _log.WriteDebug(e.GetExceptionInfo());
} }
} }
@ -268,18 +266,18 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
long serverId = await GetIdForServer(pl.CurrentServer); long serverId = await GetIdForServer(pl.CurrentServer);
if (!Servers.ContainsKey(serverId)) if (!_servers.ContainsKey(serverId))
{ {
Log.WriteError($"[Stats::AddPlayer] Server with id {serverId} could not be found"); _log.WriteError($"[Stats::AddPlayer] Server with id {serverId} could not be found");
return null; return null;
} }
var playerStats = Servers[serverId].PlayerStats; var playerStats = _servers[serverId].PlayerStats;
var detectionStats = Servers[serverId].PlayerDetections; var detectionStats = _servers[serverId].PlayerDetections;
if (playerStats.ContainsKey(pl.ClientId)) if (playerStats.ContainsKey(pl.ClientId))
{ {
Log.WriteWarning($"Duplicate ClientId in stats {pl.ClientId}"); _log.WriteWarning($"Duplicate ClientId in stats {pl.ClientId}");
return playerStats[pl.ClientId]; return playerStats[pl.ClientId];
} }
@ -321,7 +319,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (!playerStats.TryAdd(clientStats.ClientId, clientStats)) if (!playerStats.TryAdd(clientStats.ClientId, clientStats))
{ {
Log.WriteWarning("Adding new client to stats failed"); _log.WriteWarning("Adding new client to stats failed");
} }
} }
@ -329,7 +327,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{ {
if (!playerStats.TryAdd(clientStats.ClientId, clientStats)) if (!playerStats.TryAdd(clientStats.ClientId, clientStats))
{ {
Log.WriteWarning("Adding pre-existing client to stats failed"); _log.WriteWarning("Adding pre-existing client to stats failed");
} }
} }
@ -366,9 +364,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
clientStats.SessionScore = pl.Score; clientStats.SessionScore = pl.Score;
clientStats.LastScore = pl.Score; clientStats.LastScore = pl.Score;
if (!detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(Log, clientStats))) if (!detectionStats.TryAdd(pl.ClientId, new Cheat.Detection(_log, clientStats)))
{ {
Log.WriteWarning("Could not add client to detection"); _log.WriteWarning("Could not add client to detection");
} }
pl.CurrentServer.Logger.WriteInfo($"Adding {pl} to stats"); pl.CurrentServer.Logger.WriteInfo($"Adding {pl} to stats");
@ -379,8 +377,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception ex) catch (Exception ex)
{ {
Log.WriteWarning("Could not add client to stats"); _log.WriteWarning("Could not add client to stats");
Log.WriteDebug(ex.GetExceptionInfo()); _log.WriteDebug(ex.GetExceptionInfo());
} }
finally finally
@ -401,9 +399,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
pl.CurrentServer.Logger.WriteInfo($"Removing {pl} from stats"); pl.CurrentServer.Logger.WriteInfo($"Removing {pl} from stats");
long serverId = await GetIdForServer(pl.CurrentServer); long serverId = await GetIdForServer(pl.CurrentServer);
var playerStats = Servers[serverId].PlayerStats; var playerStats = _servers[serverId].PlayerStats;
var detectionStats = Servers[serverId].PlayerDetections; var detectionStats = _servers[serverId].PlayerDetections;
var serverStats = Servers[serverId].ServerStatistics; var serverStats = _servers[serverId].ServerStatistics;
if (!playerStats.ContainsKey(pl.ClientId)) if (!playerStats.ContainsKey(pl.ClientId))
{ {
@ -473,8 +471,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (FormatException) catch (FormatException)
{ {
Log.WriteWarning("Could not parse kill or death origin or viewangle vectors"); _log.WriteWarning("Could not parse kill or death origin or viewangle vectors");
Log.WriteDebug($"Kill - {killOrigin} Death - {deathOrigin} ViewAngle - {viewAngles}"); _log.WriteDebug($"Kill - {killOrigin} Death - {deathOrigin} ViewAngle - {viewAngles}");
await AddStandardKill(attacker, victim); await AddStandardKill(attacker, victim);
return; return;
} }
@ -491,7 +489,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (FormatException) catch (FormatException)
{ {
Log.WriteWarning("Could not parse snapshot angles"); _log.WriteWarning("Could not parse snapshot angles");
return; return;
} }
@ -537,13 +535,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
// incase the add player event get delayed // incase the add player event get delayed
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId)) if (!_servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
{ {
await AddPlayer(attacker); await AddPlayer(attacker);
} }
var clientDetection = Servers[serverId].PlayerDetections[attacker.ClientId]; var clientDetection = _servers[serverId].PlayerDetections[attacker.ClientId];
var clientStats = Servers[serverId].PlayerStats[attacker.ClientId]; var clientStats = _servers[serverId].PlayerStats[attacker.ClientId];
using (var ctx = new DatabaseContext(disableTracking: true)) using (var ctx = new DatabaseContext(disableTracking: true))
{ {
@ -598,8 +596,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
catch (Exception ex) catch (Exception ex)
{ {
Log.WriteError("Could not save hit or AC info"); _log.WriteError("Could not save hit or AC info");
Log.WriteDebug(ex.GetExceptionInfo()); _log.WriteDebug(ex.GetExceptionInfo());
} }
OnProcessingPenalty.Release(1); OnProcessingPenalty.Release(1);
@ -627,7 +625,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
}; };
await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], penaltyClient, false).WaitAsync(); await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], penaltyClient, false).WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken);
if (clientDetection.Tracker.HasChanges) if (clientDetection.Tracker.HasChanges)
{ {
@ -644,7 +642,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
$"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" : $"{penalty.Type}-{(int)penalty.Location}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}" :
$"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}"; $"{penalty.Type}-{Math.Round(penalty.Value, 2)}@{penalty.HitCount}";
await attacker.Flag(flagReason, penaltyClient).WaitAsync(); await attacker.Flag(flagReason, penaltyClient).WaitAsync(Utilities.DefaultCommandTimeout, attacker.CurrentServer.Manager.CancellationToken);
if (clientDetection.Tracker.HasChanges) if (clientDetection.Tracker.HasChanges)
{ {
@ -714,33 +712,33 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
long serverId = await GetIdForServer(attacker.CurrentServer); long serverId = await GetIdForServer(attacker.CurrentServer);
EFClientStatistics attackerStats = null; EFClientStatistics attackerStats = null;
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId)) if (!_servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
{ {
attackerStats = await AddPlayer(attacker); attackerStats = await AddPlayer(attacker);
} }
else else
{ {
attackerStats = Servers[serverId].PlayerStats[attacker.ClientId]; attackerStats = _servers[serverId].PlayerStats[attacker.ClientId];
} }
EFClientStatistics victimStats = null; EFClientStatistics victimStats = null;
if (!Servers[serverId].PlayerStats.ContainsKey(victim.ClientId)) if (!_servers[serverId].PlayerStats.ContainsKey(victim.ClientId))
{ {
victimStats = await AddPlayer(victim); victimStats = await AddPlayer(victim);
} }
else else
{ {
victimStats = Servers[serverId].PlayerStats[victim.ClientId]; victimStats = _servers[serverId].PlayerStats[victim.ClientId];
} }
#if DEBUG #if DEBUG
Log.WriteDebug("Calculating standard kill"); _log.WriteDebug("Calculating standard kill");
#endif #endif
// update the total stats // update the total stats
Servers[serverId].ServerStatistics.TotalKills += 1; _servers[serverId].ServerStatistics.TotalKills += 1;
await Sync(attacker.CurrentServer); await Sync(attacker.CurrentServer);
// this happens when the round has changed // this happens when the round has changed
@ -777,14 +775,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// fixme: why? // fixme: why?
if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill)) if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill))
{ {
Log.WriteDebug($"[StatManager::AddStandardKill] victim SPM/SKILL {victimStats.SPM} {victimStats.Skill}"); _log.WriteDebug($"[StatManager::AddStandardKill] victim SPM/SKILL {victimStats.SPM} {victimStats.Skill}");
victimStats.SPM = 0.0; victimStats.SPM = 0.0;
victimStats.Skill = 0.0; victimStats.Skill = 0.0;
} }
if (double.IsNaN(attackerStats.SPM) || double.IsNaN(attackerStats.Skill)) if (double.IsNaN(attackerStats.SPM) || double.IsNaN(attackerStats.Skill))
{ {
Log.WriteDebug($"[StatManager::AddStandardKill] attacker SPM/SKILL {victimStats.SPM} {victimStats.Skill}"); _log.WriteDebug($"[StatManager::AddStandardKill] attacker SPM/SKILL {victimStats.SPM} {victimStats.Skill}");
attackerStats.SPM = 0.0; attackerStats.SPM = 0.0;
attackerStats.Skill = 0.0; attackerStats.Skill = 0.0;
} }
@ -1014,7 +1012,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
attackerStats = UpdateStats(attackerStats); attackerStats = UpdateStats(attackerStats);
// calulate elo // calulate elo
if (Servers[attackerStats.ServerId].PlayerStats.Count > 1) if (_servers[attackerStats.ServerId].PlayerStats.Count > 1)
{ {
#region DEPRECATED #region DEPRECATED
/* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats /* var validAttackerLobbyRatings = Servers[attackerStats.ServerId].PlayerStats
@ -1095,7 +1093,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
} }
double killSPM = scoreDifference / timeSinceLastCalc; double killSPM = scoreDifference / timeSinceLastCalc;
double spmMultiplier = 2.934 * Math.Pow(Servers[clientStats.ServerId].TeamCount(clientStats.Team == IW4Info.Team.Allies ? IW4Info.Team.Axis : IW4Info.Team.Allies), -0.454); double spmMultiplier = 2.934 * Math.Pow(_servers[clientStats.ServerId].TeamCount(clientStats.Team == IW4Info.Team.Allies ? IW4Info.Team.Axis : IW4Info.Team.Allies), -0.454);
killSPM *= Math.Max(1, spmMultiplier); killSPM *= Math.Max(1, spmMultiplier);
// update this for ac tracking // update this for ac tracking
@ -1120,8 +1118,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (clientStats.SPM < 0) if (clientStats.SPM < 0)
{ {
Log.WriteWarning("[StatManager:UpdateStats] clientStats SPM < 0"); _log.WriteWarning("[StatManager:UpdateStats] clientStats SPM < 0");
Log.WriteDebug($"{scoreDifference}-{clientStats.RoundScore} - {clientStats.LastScore} - {clientStats.SessionScore}"); _log.WriteDebug($"{scoreDifference}-{clientStats.RoundScore} - {clientStats.LastScore} - {clientStats.SessionScore}");
clientStats.SPM = 0; clientStats.SPM = 0;
} }
@ -1131,8 +1129,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// fixme: how does this happen? // fixme: how does this happen?
if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill)) if (double.IsNaN(clientStats.SPM) || double.IsNaN(clientStats.Skill))
{ {
Log.WriteWarning("[StatManager::UpdateStats] clientStats SPM/Skill NaN"); _log.WriteWarning("[StatManager::UpdateStats] clientStats SPM/Skill NaN");
Log.WriteDebug($"{killSPM}-{KDRWeight}-{totalPlayTime}-{SPMAgainstPlayWeight}-{clientStats.SPM}-{clientStats.Skill}-{scoreDifference}"); _log.WriteDebug($"{killSPM}-{KDRWeight}-{totalPlayTime}-{SPMAgainstPlayWeight}-{clientStats.SPM}-{clientStats.Skill}-{scoreDifference}");
clientStats.SPM = 0; clientStats.SPM = 0;
clientStats.Skill = 0; clientStats.Skill = 0;
} }
@ -1154,7 +1152,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (serverStats == null) if (serverStats == null)
{ {
Log.WriteDebug($"Initializing server stats for {serverId}"); _log.WriteDebug($"Initializing server stats for {serverId}");
// server stats have never been generated before // server stats have never been generated before
serverStats = new EFServerStatistics() serverStats = new EFServerStatistics()
{ {
@ -1173,7 +1171,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public void ResetKillstreaks(long serverId) public void ResetKillstreaks(long serverId)
{ {
var serverStats = Servers[serverId]; var serverStats = _servers[serverId];
foreach (var stat in serverStats.PlayerStats.Values) foreach (var stat in serverStats.PlayerStats.Values)
{ {
@ -1183,7 +1181,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public void ResetStats(int clientId, long serverId) public void ResetStats(int clientId, long serverId)
{ {
var stats = Servers[serverId].PlayerStats[clientId]; var stats = _servers[serverId].PlayerStats[clientId];
stats.Kills = 0; stats.Kills = 0;
stats.Deaths = 0; stats.Deaths = 0;
stats.SPM = 0; stats.SPM = 0;
@ -1221,10 +1219,10 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
using (var ctx = new DatabaseContext(disableTracking: true)) using (var ctx = new DatabaseContext(disableTracking: true))
{ {
var serverSet = ctx.Set<EFServer>(); var serverSet = ctx.Set<EFServer>();
serverSet.Update(Servers[serverId].Server); serverSet.Update(_servers[serverId].Server);
var serverStatsSet = ctx.Set<EFServerStatistics>(); var serverStatsSet = ctx.Set<EFServerStatistics>();
serverStatsSet.Update(Servers[serverId].ServerStatistics); serverStatsSet.Update(_servers[serverId].ServerStatistics);
await ctx.SaveChangesAsync(); await ctx.SaveChangesAsync();
} }
@ -1232,7 +1230,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public void SetTeamBased(long serverId, bool isTeamBased) public void SetTeamBased(long serverId, bool isTeamBased)
{ {
Servers[serverId].IsTeamBased = isTeamBased; _servers[serverId].IsTeamBased = isTeamBased;
} }
public static async Task<long> GetIdForServer(Server server) public static async Task<long> GetIdForServer(Server server)

View File

@ -23,7 +23,22 @@ namespace SharedLibraryCore.Commands
public override Task ExecuteAsync(GameEvent E) public override Task ExecuteAsync(GameEvent E)
{ {
return Task.Run(() => { E.Owner.Manager.Stop(); }); E.Owner.Manager.Stop();
return Task.CompletedTask;
}
}
public class CRestart : Command
{
public CRestart() :
base("restart", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RESTART_DESC"], "rs", EFClient.Permission.Owner, false)
{ }
public override Task ExecuteAsync(GameEvent E)
{
E.Owner.Manager.Restart();
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RESTART_SUCCESS"]);
return Task.CompletedTask;
} }
} }
@ -120,9 +135,18 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
{ {
var _ = !(await E.Target.Kick(E.Data, E.Origin).WaitAsync()).Failed ? switch ((await E.Target.Kick(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
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)); case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"].FormatExt(E.Target.Name));
break;
case GameEvent.EventFailReason.Exception:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"].FormatExt(E.Target.Name));
break;
}
} }
} }
@ -186,9 +210,18 @@ namespace SharedLibraryCore.Commands
else else
{ {
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ? switch ((await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
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)); case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"].FormatExt(E.Target, length.TimeSpanText()));
break;
case GameEvent.EventFailReason.Exception:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"].FormatExt(E.Target.Name));
break;
}
} }
} }
} }
@ -214,9 +247,18 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
{ {
var _ = !(await E.Target.Ban(E.Data, E.Origin, false).WaitAsync()).Failed ? switch ((await E.Target.Ban(E.Data, E.Origin, false).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"].FormatExt(E.Target.Name)) : {
case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"].FormatExt(E.Target.Name));
break;
case GameEvent.EventFailReason.Exception:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"].FormatExt(E.Target.Name)); E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"].FormatExt(E.Target.Name));
break;
}
} }
} }
@ -240,12 +282,21 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
{ {
// todo: don't do the lookup here
var penalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId); var penalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId);
if (penalties.Where(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan).FirstOrDefault() != null) 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(); switch ((await E.Target.Unban(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"].FormatExt(E.Target)); {
case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"].FormatExt(E.Target));
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
}
} }
else else
{ {
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"].FormatExt(E.Target)); E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"].FormatExt(E.Target));
@ -587,12 +638,13 @@ namespace SharedLibraryCore.Commands
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap) if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
{ {
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"].FormatExt(m.Alias)); E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"].FormatExt(m.Alias));
await Task.Delay(5000); await Task.Delay((int)(Utilities.DefaultCommandTimeout.TotalMilliseconds / 2.0));
await E.Owner.LoadMap(m.Name); await E.Owner.LoadMap(m.Name);
return; return;
} }
} }
// todo: this can be moved into a single statement
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"].FormatExt(newMap)); E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"].FormatExt(newMap));
await Task.Delay(5000); await Task.Delay(5000);
await E.Owner.LoadMap(newMap); await E.Owner.LoadMap(newMap);
@ -720,27 +772,23 @@ namespace SharedLibraryCore.Commands
}) })
{ } { }
public override Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
{ {
var flagEvent = E.Target.Flag(E.Data, E.Origin); switch ((await E.Target.Flag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
if (E.FailReason == GameEvent.EventFailReason.Permission)
{ {
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"].FormatExt(E.Target.Name)); case GameEvent.EventFailReason.Permission:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"].FormatExt(E.Target.Name));
break;
case GameEvent.EventFailReason.Invalid:
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}");
break;
case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"].FormatExt(E.Target.Name));
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
} }
else if (E.FailReason == GameEvent.EventFailReason.Invalid)
{
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}");
}
else
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"].FormatExt(E.Target.Name));
}
return Task.CompletedTask;
} }
} }
@ -757,27 +805,23 @@ namespace SharedLibraryCore.Commands
}) })
{ } { }
public override Task ExecuteAsync(GameEvent E) public override async Task ExecuteAsync(GameEvent E)
{ {
var unflagEvent = E.Target.Unflag(E.Data, E.Origin); switch ((await E.Target.Unflag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
if (unflagEvent.FailReason == GameEvent.EventFailReason.Permission)
{ {
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"].FormatExt(E.Target.Name)); case GameEvent.EventFailReason.None:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"].FormatExt(E.Target.Name));
break;
case GameEvent.EventFailReason.Permission:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"].FormatExt(E.Target.Name));
break;
case GameEvent.EventFailReason.Invalid:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
break;
default:
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMAND_INGAME"]);
break;
} }
else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid)
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
}
else
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"].FormatExt(E.Target.Name));
}
return Task.CompletedTask;
// todo: update immediately?
} }
} }
@ -808,46 +852,30 @@ namespace SharedLibraryCore.Commands
return; return;
} }
var reportEvent = commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin); bool success = false;
if (reportEvent.FailReason == GameEvent.EventFailReason.Permission) switch ((await commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin).WaitAsync(Utilities.DefaultCommandTimeout, commandEvent.Owner.Manager.CancellationToken)).FailReason)
{ {
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"].FormatExt(commandEvent.Target.Name)); case GameEvent.EventFailReason.None:
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
success = true;
break;
case GameEvent.EventFailReason.Exception:
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
break;
case GameEvent.EventFailReason.Permission:
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"].FormatExt(commandEvent.Target.Name));
break;
case GameEvent.EventFailReason.Invalid:
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
break;
case GameEvent.EventFailReason.Throttle:
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_TOOMANY"]);
break;
} }
else if (reportEvent.FailReason == GameEvent.EventFailReason.Invalid) if (success)
{ {
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
}
else if (reportEvent.FailReason == GameEvent.EventFailReason.Throttle)
{
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_TOOMANY"]);
}
else if (reportEvent.Failed)
{
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
}
else
{
// todo: move into server
Penalty newReport = new Penalty()
{
Type = Penalty.PenaltyType.Report,
Expires = DateTime.UtcNow,
Offender = commandEvent.Target,
Offense = commandEvent.Data,
Punisher = commandEvent.Origin,
Active = true,
When = DateTime.UtcNow,
Link = commandEvent.Target.AliasLink
};
await commandEvent.Owner.Manager.GetPenaltyService().Create(newReport);
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
commandEvent.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", commandEvent.Origin.Name, commandEvent.Target.Name, commandEvent.Data)); commandEvent.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", commandEvent.Origin.Name, commandEvent.Target.Name, commandEvent.Data));
} }
} }
@ -1118,10 +1146,6 @@ namespace SharedLibraryCore.Commands
E.Origin.Password = hashedPassword[0]; E.Origin.Password = hashedPassword[0];
E.Origin.PasswordSalt = hashedPassword[1]; E.Origin.PasswordSalt = hashedPassword[1];
// update the password for the client in privileged
//E.Owner.Manager.PrivilegedClients[E.Origin.ClientId].Password = hashedPassword[0];
//E.Owner.Manager.PrivilegedClients[E.Origin.ClientId].PasswordSalt = hashedPassword[1];
await E.Owner.Manager.GetClientService().Update(E.Origin); await E.Owner.Manager.GetClientService().Update(E.Origin);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]); E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]);
} }

View File

@ -29,7 +29,7 @@ namespace SharedLibraryCore.Database
{ {
#if DEBUG == true #if DEBUG == true
activeContextCount++; activeContextCount++;
Console.WriteLine($"Initialized DB Context #{activeContextCount}"); //Console.WriteLine($"Initialized DB Context #{activeContextCount}");
#endif #endif
} }
@ -37,7 +37,7 @@ namespace SharedLibraryCore.Database
{ {
#if DEBUG == true #if DEBUG == true
activeContextCount++; activeContextCount++;
Console.WriteLine($"Initialized DB Context #{activeContextCount}"); //Console.WriteLine($"Initialized DB Context #{activeContextCount}");
#endif #endif
} }
@ -45,7 +45,7 @@ namespace SharedLibraryCore.Database
{ {
#if DEBUG == true #if DEBUG == true
Console.WriteLine($"Disposed DB Context #{activeContextCount}"); //Console.WriteLine($"Disposed DB Context #{activeContextCount}");
activeContextCount--; activeContextCount--;
#endif #endif
} }

View File

@ -212,11 +212,11 @@ namespace SharedLibraryCore
/// asynchronously wait for GameEvent to be processed /// asynchronously wait for GameEvent to be processed
/// </summary> /// </summary>
/// <returns>waitable task </returns> /// <returns>waitable task </returns>
public Task<GameEvent> WaitAsync(int timeOut = int.MaxValue) public Task<GameEvent> WaitAsync(TimeSpan timeSpan, CancellationToken token)
{ {
return Task.Run(() => return Task.Run(() =>
{ {
OnProcessed.Wait(timeOut); OnProcessed.Wait(timeSpan, token);
return this; return this;
}); });
} }

View File

@ -7,14 +7,16 @@ using SharedLibraryCore.Configuration;
using System.Reflection; using System.Reflection;
using SharedLibraryCore.Database.Models; using SharedLibraryCore.Database.Models;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading;
namespace SharedLibraryCore.Interfaces namespace SharedLibraryCore.Interfaces
{ {
public interface IManager public interface IManager
{ {
Task Init(); Task Init();
void Start(); Task Start();
void Stop(); void Stop();
void Restart();
ILogger GetLogger(long serverId); ILogger GetLogger(long serverId);
IList<Server> GetServers(); IList<Server> GetServers();
IList<Command> GetCommands(); IList<Command> GetCommands();
@ -29,11 +31,6 @@ namespace SharedLibraryCore.Interfaces
/// </summary> /// </summary>
/// <returns>EventHandler for the manager</returns> /// <returns>EventHandler for the manager</returns>
IEventHandler GetEventHandler(); IEventHandler GetEventHandler();
/// <summary>
/// Signal to the manager that event(s) needs to be processed
/// </summary>
void SetHasEvent();
bool ShutdownRequested();
IList<Assembly> GetPluginAssemblies(); IList<Assembly> GetPluginAssemblies();
/// <summary> /// <summary>
/// provides a page list to add and remove from /// provides a page list to add and remove from
@ -47,5 +44,7 @@ namespace SharedLibraryCore.Interfaces
string Version { get;} string Version { get;}
ITokenAuthentication TokenAuthenticator { get; } ITokenAuthentication TokenAuthenticator { get; }
string ExternalIPAddress { get; } string ExternalIPAddress { get; }
CancellationToken CancellationToken { get; }
bool IsRestartRequested { get; }
} }
} }

View File

@ -546,6 +546,7 @@ namespace SharedLibraryCore.Database.Models
profileBan = (await CurrentServer.Manager profileBan = (await CurrentServer.Manager
.GetPenaltyService() .GetPenaltyService()
.GetActivePenaltiesAsync(AliasLinkId)) .GetActivePenaltiesAsync(AliasLinkId))
.OrderByDescending(_penalty => _penalty.When)
.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.Ban); .FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.Ban);
CurrentServer.Logger.WriteWarning($"Client {this} is GUID banned, but no previous penalty exists for their ban"); CurrentServer.Logger.WriteWarning($"Client {this} is GUID banned, but no previous penalty exists for their ban");
@ -596,18 +597,6 @@ namespace SharedLibraryCore.Database.Models
// we want to get any penalties that are tied to their IP or AliasLink (but not necessarily their GUID) // 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); 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 #region CLIENT_LINKED_BAN
var currentBan = activePenalties.FirstOrDefault(p => p.Type == Penalty.PenaltyType.Ban); var currentBan = activePenalties.FirstOrDefault(p => p.Type == Penalty.PenaltyType.Ban);
@ -642,6 +631,20 @@ namespace SharedLibraryCore.Database.Models
} }
#endregion #endregion
#region CLIENT_LINKED_TEMPBAN
var tempBan = activePenalties
.OrderByDescending(_penalty => _penalty.When)
.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_FLAG #region CLIENT_LINKED_FLAG
if (Level != Permission.Flagged) if (Level != Permission.Flagged)
{ {

View File

@ -42,7 +42,7 @@ namespace SharedLibraryCore
} }
catch (Exception ex) catch (Exception ex)
{ {
Manager.GetLogger(0).WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"]} {Name}"); Manager.GetLogger(0).WriteError(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(Name));
Manager.GetLogger(0).WriteDebug(ex.Message); Manager.GetLogger(0).WriteDebug(ex.Message);
} }
} }

View File

@ -23,10 +23,10 @@ namespace SharedLibraryCore
IW4 = 2, IW4 = 2,
IW5 = 3, IW5 = 3,
IW6 = 4, IW6 = 4,
T4 = 5, T4 = 5,
T5 = 6, T5 = 6,
T6 = 7, T6 = 7,
T7 = 8 T7 = 8
} }
public Server(IManager mgr, ServerConfiguration config) public Server(IManager mgr, ServerConfiguration config)
@ -159,14 +159,16 @@ namespace SharedLibraryCore
if (Target.Level == EFClient.Permission.Console) if (Target.Level == EFClient.Permission.Console)
{ {
Console.ForegroundColor = ConsoleColor.Cyan; Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine(Message.StripColors()); Console.WriteLine(Message.StripColors());
Console.ForegroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Gray;
} }
// prevent this from queueing up too many command responses // prevent this from queueing up too many command responses
if (CommandResult.Count > 15) if (CommandResult.Count > 15)
{
CommandResult.RemoveAt(0); CommandResult.RemoveAt(0);
}
// it was a remote command so we need to add it to the command result queue // it was a remote command so we need to add it to the command result queue
if (Target.ClientNumber < 0) if (Target.ClientNumber < 0)

View File

@ -447,8 +447,6 @@ namespace SharedLibraryCore.Services
return new List<PlayerInfo>(); return new List<PlayerInfo>();
} }
identifier = identifier.ToLower();
using (var context = new DatabaseContext(disableTracking: true)) using (var context = new DatabaseContext(disableTracking: true))
{ {
long? networkId = null; long? networkId = null;
@ -472,7 +470,7 @@ namespace SharedLibraryCore.Services
// todo maybe not make it start with wildcard? // todo maybe not make it start with wildcard?
else else
{ {
iqLinkIds = iqLinkIds.Where(_alias => EF.Functions.Like(_alias.Name, $"%{identifier}%")); iqLinkIds = iqLinkIds.Where(_alias => EF.Functions.Like(_alias.Name.ToLower(), $"%{identifier.ToLower()}%"));
} }
var linkIds = await iqLinkIds var linkIds = await iqLinkIds

View File

@ -30,6 +30,8 @@ namespace SharedLibraryCore
#endif #endif
public static Encoding EncodingType; public static Encoding EncodingType;
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>()); public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
public static TimeSpan DefaultCommandTimeout = new TimeSpan(0, 0, 10);
public static EFClient IW4MAdminClient(Server server = null) public static EFClient IW4MAdminClient(Server server = null)
{ {
return new EFClient() return new EFClient()
@ -320,7 +322,7 @@ namespace SharedLibraryCore
if (Elapsed.TotalSeconds < 30) if (Elapsed.TotalSeconds < 30)
{ {
return CurrentLocalization.LocalizationIndex["GLOBAL_TIME_JUSTNOW"]; return CurrentLocalization.LocalizationIndex["GLOBAL_TIME_JUSTNOW"] + ago;
} }
if (Elapsed.TotalMinutes < 120) if (Elapsed.TotalMinutes < 120)
{ {
@ -783,6 +785,11 @@ namespace SharedLibraryCore
} }
} }
/// <summary>
/// Determines if the given message is a quick message
/// </summary>
/// <param name="message"></param>
/// <returns>true if the </returns>
public static bool IsQuickMessage(this string message) public static bool IsQuickMessage(this string message)
{ {
return Regex.IsMatch(message, @"^\u0014(?:[A-Z]|_)+$"); return Regex.IsMatch(message, @"^\u0014(?:[A-Z]|_)+$");

View File

@ -53,7 +53,7 @@ namespace WebfrontCore.Controllers
Manager.GetEventHandler().AddEvent(remoteEvent); Manager.GetEventHandler().AddEvent(remoteEvent);
List<CommandResponseInfo> response; List<CommandResponseInfo> response;
// wait for the event to process // wait for the event to process
if (!(await remoteEvent.WaitAsync(60 * 1000)).Failed) if (!(await remoteEvent.WaitAsync(Utilities.DefaultCommandTimeout, server.Manager.CancellationToken)).Failed)
{ {
response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList(); response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();

View File

@ -1,4 +1,6 @@
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using SharedLibraryCore.Interfaces; using SharedLibraryCore.Interfaces;
@ -9,18 +11,18 @@ namespace WebfrontCore
{ {
public static IManager Manager; public static IManager Manager;
static void Main(string[] args) static void Main()
{ {
throw new System.Exception("Webfront core cannot be run as a standalone application"); throw new System.Exception("Webfront core cannot be run as a standalone application");
} }
public static void Init(IManager mgr) public static Task Init(IManager mgr, CancellationToken cancellationToken)
{ {
Manager = mgr; Manager = mgr;
BuildWebHost().Run(); return BuildWebHost().RunAsync(cancellationToken);
} }
public static IWebHost BuildWebHost() private static IWebHost BuildWebHost()
{ {
var config = new ConfigurationBuilder() var config = new ConfigurationBuilder()
.AddEnvironmentVariables() .AddEnvironmentVariables()