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

View File

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

View File

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

View File

@ -296,6 +296,20 @@ namespace IW4MAdmin
Target = E.Target,
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)
@ -548,7 +562,7 @@ namespace IW4MAdmin
try
{
#region SHUTDOWN
if (Manager.ShutdownRequested())
if (Manager.CancellationToken.IsCancellationRequested)
{
foreach (var client in GetClientsAsList())
{
@ -560,7 +574,7 @@ namespace IW4MAdmin
};
Manager.GetEventHandler().AddEvent(e);
await e.WaitAsync();
await e.WaitAsync(Utilities.DefaultCommandTimeout, Manager.CancellationToken);
}
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
@ -597,7 +611,7 @@ namespace IW4MAdmin
waiterList.Add(e);
}
// 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();
// this are our new connecting clients
@ -621,7 +635,7 @@ namespace IW4MAdmin
}
// 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();
// these are the clients that have updated
@ -638,7 +652,7 @@ namespace IW4MAdmin
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)
{

View File

@ -1,204 +1,221 @@
using IW4MAdmin.Application.Migration;
using SharedLibraryCore;
using SharedLibraryCore.Localization;
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IW4MAdmin.Application
{
public class Program
{
static public double Version { get; private set; }
static public ApplicationManager ServerManager;
private static ManualResetEventSlim OnShutdownComplete = new ManualResetEventSlim();
public static double Version { get; private set; } = Utilities.GetVersionAsDouble();
public static ApplicationManager ServerManager;
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);
Console.OutputEncoding = Encoding.UTF8;
Console.ForegroundColor = ConsoleColor.Gray;
Version = Utilities.GetVersionAsDouble();
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
Console.WriteLine("=====================================================");
Console.WriteLine(" IW4M ADMIN");
Console.WriteLine(" IW4MAdmin");
Console.WriteLine(" by RaidMax ");
Console.WriteLine($" Version {Utilities.GetVersionAsString()}");
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
{
ServerManager = ApplicationManager.GetInstance();
var configuration = ServerManager.GetApplicationSettings().Configuration();
Localization.Configure.Initialize(configuration?.EnableCustomLocale ?? false ? (configuration.CustomLocale ?? "windows-1252") : "windows-1252");
if (configuration != null)
{
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
// do any needed housekeeping file/folder migrations
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();
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);
});
await CheckVersion();
await ServerManager.Init();
}
catch (Exception e)
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
string failMessage = loc == null ? "Failed to initalize IW4MAdmin" : loc["MANAGER_INIT_FAIL"];
string exitMessage = loc == null ? "Press any key to exit..." : loc["MANAGER_EXIT"];
Console.WriteLine(failMessage);
while (e.InnerException != null)
{
e = e.InnerException;
}
Console.WriteLine(e.Message);
Console.WriteLine(exitMessage);
Console.ReadKey();
return;
}
if (ServerManager.GetApplicationSettings().Configuration().EnableWebFront)
try
{
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
ApplicationTask = RunApplicationTasksAsync();
await ApplicationTask;
}
OnShutdownComplete.Reset();
ServerManager.Start();
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
OnShutdownComplete.Set();
catch { }
if (ServerManager.IsRestartRequested)
{
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();
OnShutdownComplete.Wait();
var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ?
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>
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>
/// moves existing configs from the root folder into a configs folder
/// </summary>

View File

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

View File

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

View File

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

View File

@ -23,7 +23,22 @@ namespace SharedLibraryCore.Commands
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)
{
var _ = !(await E.Target.Kick(E.Data, E.Origin).WaitAsync()).Failed ?
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));
switch ((await E.Target.Kick(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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
{
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ?
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));
switch ((await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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)
{
var _ = !(await E.Target.Ban(E.Data, E.Origin, false).WaitAsync()).Failed ?
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"].FormatExt(E.Target.Name)) :
switch ((await E.Target.Ban(E.Data, E.Origin, false).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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));
break;
}
}
}
@ -240,12 +282,21 @@ namespace SharedLibraryCore.Commands
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);
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"].FormatExt(E.Target));
switch ((await E.Target.Unban(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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
{
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)
{
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);
return;
}
}
// todo: this can be moved into a single statement
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"].FormatExt(newMap));
await Task.Delay(5000);
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);
if (E.FailReason == GameEvent.EventFailReason.Permission)
switch ((await E.Target.Flag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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);
if (unflagEvent.FailReason == GameEvent.EventFailReason.Permission)
switch ((await E.Target.Unflag(E.Data, E.Origin).WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken)).FailReason)
{
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;
}
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));
}
}
@ -1118,10 +1146,6 @@ namespace SharedLibraryCore.Commands
E.Origin.Password = hashedPassword[0];
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);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]);
}

View File

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

View File

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

View File

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

View File

@ -546,6 +546,7 @@ namespace SharedLibraryCore.Database.Models
profileBan = (await CurrentServer.Manager
.GetPenaltyService()
.GetActivePenaltiesAsync(AliasLinkId))
.OrderByDescending(_penalty => _penalty.When)
.FirstOrDefault(_penalty => _penalty.Type == Penalty.PenaltyType.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)
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);
@ -642,6 +631,20 @@ namespace SharedLibraryCore.Database.Models
}
#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
if (Level != Permission.Flagged)
{

View File

@ -42,7 +42,7 @@ namespace SharedLibraryCore
}
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);
}
}

View File

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

View File

@ -447,8 +447,6 @@ namespace SharedLibraryCore.Services
return new List<PlayerInfo>();
}
identifier = identifier.ToLower();
using (var context = new DatabaseContext(disableTracking: true))
{
long? networkId = null;
@ -472,7 +470,7 @@ namespace SharedLibraryCore.Services
// todo maybe not make it start with wildcard?
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

View File

@ -30,6 +30,8 @@ namespace SharedLibraryCore
#endif
public static Encoding EncodingType;
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)
{
return new EFClient()
@ -320,7 +322,7 @@ namespace SharedLibraryCore
if (Elapsed.TotalSeconds < 30)
{
return CurrentLocalization.LocalizationIndex["GLOBAL_TIME_JUSTNOW"];
return CurrentLocalization.LocalizationIndex["GLOBAL_TIME_JUSTNOW"] + ago;
}
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)
{
return Regex.IsMatch(message, @"^\u0014(?:[A-Z]|_)+$");

View File

@ -53,7 +53,7 @@ namespace WebfrontCore.Controllers
Manager.GetEventHandler().AddEvent(remoteEvent);
List<CommandResponseInfo> response;
// 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();

View File

@ -1,4 +1,6 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using SharedLibraryCore.Interfaces;
@ -9,18 +11,18 @@ namespace WebfrontCore
{
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");
}
public static void Init(IManager mgr)
public static Task Init(IManager mgr, CancellationToken cancellationToken)
{
Manager = mgr;
BuildWebHost().Run();
return BuildWebHost().RunAsync(cancellationToken);
}
public static IWebHost BuildWebHost()
private static IWebHost BuildWebHost()
{
var config = new ConfigurationBuilder()
.AddEnvironmentVariables()