2018-11-25 21:00:36 -05:00
using IW4MAdmin.Application.API.Master ;
2019-01-26 21:33:37 -05:00
using IW4MAdmin.Application.EventParsers ;
2019-02-15 23:19:59 -05:00
using IW4MAdmin.Application.Misc ;
2019-01-26 21:33:37 -05:00
using IW4MAdmin.Application.RconParsers ;
2018-04-08 02:44:42 -04:00
using SharedLibraryCore ;
using SharedLibraryCore.Commands ;
using SharedLibraryCore.Configuration ;
2018-08-28 17:32:59 -04:00
using SharedLibraryCore.Database ;
2018-11-05 22:01:29 -05:00
using SharedLibraryCore.Database.Models ;
2019-02-22 20:06:51 -05:00
using SharedLibraryCore.Dtos ;
2018-11-25 21:00:36 -05:00
using SharedLibraryCore.Events ;
using SharedLibraryCore.Exceptions ;
using SharedLibraryCore.Helpers ;
using SharedLibraryCore.Interfaces ;
2019-03-29 22:56:56 -04:00
using SharedLibraryCore.Objects ;
2018-11-25 21:00:36 -05:00
using SharedLibraryCore.Services ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using System.Text ;
using System.Threading ;
using System.Threading.Tasks ;
2018-09-29 15:52:22 -04:00
2018-04-08 14:48:40 -04:00
namespace IW4MAdmin.Application
2015-07-03 00:10:01 -04:00
{
2018-02-21 20:29:23 -05:00
public class ApplicationManager : IManager
2015-07-03 00:10:01 -04:00
{
2017-10-04 19:01:04 -04:00
private List < Server > _servers ;
public List < Server > Servers = > _servers . OrderByDescending ( s = > s . ClientNum ) . ToList ( ) ;
2018-11-05 22:01:29 -05:00
public Dictionary < int , EFClient > PrivilegedClients { get ; set ; }
2018-10-06 12:47:14 -04:00
public ILogger Logger = > GetLogger ( 0 ) ;
2017-06-19 13:58:01 -04:00
public bool Running { get ; private set ; }
2018-07-04 22:09:42 -04:00
public bool IsInitialized { get ; private set ; }
2018-08-27 18:07:54 -04:00
// define what the delagate function looks like
public delegate void OnServerEventEventHandler ( object sender , GameEventArgs e ) ;
// expose the event handler so we can execute the events
2018-09-02 17:59:27 -04:00
public OnServerEventEventHandler OnServerEvent { get ; set ; }
2018-04-18 16:46:53 -04:00
public DateTime StartTime { get ; private set ; }
2018-09-16 16:34:16 -04:00
public string Version = > Assembly . GetEntryAssembly ( ) . GetName ( ) . Version . ToString ( ) ;
2017-06-19 13:58:01 -04:00
2019-02-01 20:49:25 -05:00
public IList < IRConParser > AdditionalRConParsers { get ; }
public IList < IEventParser > AdditionalEventParsers { get ; }
2019-02-15 23:19:59 -05:00
public ITokenAuthentication TokenAuthenticator = > Authenticator ;
public ITokenAuthentication Authenticator = > _authenticator ;
2019-04-08 13:29:48 -04:00
public string ExternalIPAddress { get ; private set ; }
2019-02-15 23:19:59 -05:00
2017-06-19 13:58:01 -04:00
static ApplicationManager Instance ;
2018-08-03 22:11:58 -04:00
readonly List < AsyncStatus > TaskStatuses ;
2017-05-26 18:49:27 -04:00
List < Command > Commands ;
2018-08-03 22:11:58 -04:00
readonly List < MessageToken > MessageTokens ;
2017-11-25 20:29:58 -05:00
ClientService ClientSvc ;
2018-08-03 22:11:58 -04:00
readonly AliasService AliasSvc ;
readonly PenaltyService PenaltySvc ;
2018-08-28 17:32:59 -04:00
public BaseConfigurationHandler < ApplicationConfiguration > ConfigHandler ;
2018-04-26 02:13:04 -04:00
GameEventHandler Handler ;
2018-08-28 17:32:59 -04:00
ManualResetEventSlim OnQuit ;
2018-08-03 18:10:20 -04:00
readonly IPageList PageList ;
2018-09-23 20:45:54 -04:00
readonly SemaphoreSlim ProcessingEvent = new SemaphoreSlim ( 1 , 1 ) ;
2018-11-27 19:31:48 -05:00
readonly Dictionary < long , ILogger > Loggers = new Dictionary < long , ILogger > ( ) ;
2019-02-15 23:19:59 -05:00
readonly ITokenAuthentication _authenticator ;
2019-02-22 20:06:51 -05:00
private readonly MetaService _metaService ;
2019-04-23 18:27:20 -04:00
private readonly TimeSpan _throttleTimeout = new TimeSpan ( 0 , 1 , 0 ) ;
2017-05-26 18:49:27 -04:00
2017-06-19 13:58:01 -04:00
private ApplicationManager ( )
2015-07-03 00:10:01 -04:00
{
2017-10-04 19:01:04 -04:00
_servers = new List < Server > ( ) ;
2017-05-26 18:49:27 -04:00
Commands = new List < Command > ( ) ;
2017-06-19 13:58:01 -04:00
TaskStatuses = new List < AsyncStatus > ( ) ;
MessageTokens = new List < MessageToken > ( ) ;
2017-11-25 20:29:58 -05:00
ClientSvc = new ClientService ( ) ;
AliasSvc = new AliasService ( ) ;
PenaltySvc = new PenaltyService ( ) ;
2018-03-18 22:25:11 -04:00
ConfigHandler = new BaseConfigurationHandler < ApplicationConfiguration > ( "IW4MAdminSettings" ) ;
2018-04-18 16:46:53 -04:00
StartTime = DateTime . UtcNow ;
2018-08-28 17:32:59 -04:00
OnQuit = new ManualResetEventSlim ( ) ;
2018-08-03 18:10:20 -04:00
PageList = new PageList ( ) ;
2019-02-01 20:49:25 -05:00
AdditionalEventParsers = new List < IEventParser > ( ) ;
AdditionalRConParsers = new List < IRConParser > ( ) ;
2018-09-29 15:52:22 -04:00
OnServerEvent + = OnGameEvent ;
2018-08-30 21:53:00 -04:00
OnServerEvent + = EventApi . OnGameEvent ;
2019-02-15 23:19:59 -05:00
_authenticator = new TokenAuthentication ( ) ;
2019-02-22 20:06:51 -05:00
_metaService = new MetaService ( ) ;
2018-08-27 18:07:54 -04:00
}
2018-09-29 15:52:22 -04:00
private async void OnGameEvent ( object sender , GameEventArgs args )
2018-08-27 18:07:54 -04:00
{
2018-09-29 15:52:22 -04:00
#if DEBUG = = true
Logger . WriteDebug ( $"Entering event process for {args.Event.Id}" ) ;
#endif
2018-09-23 20:45:54 -04:00
2018-08-27 18:07:54 -04:00
var newEvent = args . Event ;
2018-10-02 13:39:08 -04:00
// the event has failed already
if ( newEvent . Failed )
{
goto skip ;
}
2018-08-27 18:07:54 -04:00
try
2018-11-25 21:00:36 -05:00
{
await newEvent . Owner . ExecuteEvent ( newEvent ) ;
2018-09-29 15:52:22 -04:00
2018-11-25 21:00:36 -05:00
// save the event info to the database
var changeHistorySvc = new ChangeHistoryService ( ) ;
await changeHistorySvc . Add ( args . Event ) ;
2018-08-28 17:32:59 -04:00
2018-08-27 18:07:54 -04:00
#if DEBUG
2018-09-02 17:59:27 -04:00
Logger . WriteDebug ( $"Processed event with id {newEvent.Id}" ) ;
2018-08-27 18:07:54 -04:00
#endif
}
// this happens if a plugin requires login
catch ( AuthorizationException ex )
{
2018-10-02 13:39:08 -04:00
newEvent . FailReason = GameEvent . EventFailReason . Permission ;
2018-09-29 15:52:22 -04:00
newEvent . Origin . Tell ( $"{Utilities.CurrentLocalization.LocalizationIndex[" COMMAND_NOTAUTHORIZED "]} - {ex.Message}" ) ;
2018-08-27 18:07:54 -04:00
}
catch ( NetworkException ex )
{
2018-10-02 13:39:08 -04:00
newEvent . FailReason = GameEvent . EventFailReason . Exception ;
2018-08-27 18:07:54 -04:00
Logger . WriteError ( ex . Message ) ;
2018-09-23 20:45:54 -04:00
Logger . WriteDebug ( ex . GetExceptionInfo ( ) ) ;
2018-08-27 18:07:54 -04:00
}
catch ( ServerException ex )
{
2018-10-02 13:39:08 -04:00
newEvent . FailReason = GameEvent . EventFailReason . Exception ;
2018-08-27 18:07:54 -04:00
Logger . WriteWarning ( ex . Message ) ;
}
catch ( Exception ex )
{
2018-10-02 13:39:08 -04:00
newEvent . FailReason = GameEvent . EventFailReason . Exception ;
2019-04-25 14:00:54 -04:00
Logger . WriteError ( Utilities . CurrentLocalization . LocalizationIndex [ "SERVER_ERROR_EXCEPTION" ] . FormatExt ( newEvent . Owner ) ) ;
2018-09-23 20:45:54 -04:00
Logger . WriteDebug ( ex . GetExceptionInfo ( ) ) ;
2018-08-27 18:07:54 -04:00
}
2018-09-23 20:45:54 -04:00
2018-11-25 21:00:36 -05:00
skip :
2018-10-02 13:39:08 -04:00
2018-08-27 18:07:54 -04:00
// tell anyone waiting for the output that we're done
2018-09-06 14:25:58 -04:00
newEvent . OnProcessed . Set ( ) ;
2018-03-14 01:36:25 -04:00
}
2017-09-29 22:42:24 -04:00
public IList < Server > GetServers ( )
2015-07-03 00:10:01 -04:00
{
2017-05-26 18:49:27 -04:00
return Servers ;
2015-07-03 00:10:01 -04:00
}
2017-09-29 22:42:24 -04:00
public IList < Command > GetCommands ( )
2015-08-17 16:38:42 -04:00
{
2017-05-26 18:49:27 -04:00
return Commands ;
2015-08-17 16:38:42 -04:00
}
2017-06-19 13:58:01 -04:00
public static ApplicationManager GetInstance ( )
2015-08-22 02:04:30 -04:00
{
2017-06-19 13:58:01 -04:00
return Instance ? ? ( Instance = new ApplicationManager ( ) ) ;
2015-08-22 02:04:30 -04:00
}
2019-04-08 13:29:48 -04:00
public async Task UpdateServerStates ( CancellationToken token )
2018-04-26 02:13:04 -04:00
{
2018-07-01 20:30:38 -04:00
// store the server hash code and task for it
2018-11-27 19:31:48 -05:00
var runningUpdateTasks = new Dictionary < long , Task > ( ) ;
2018-04-26 20:19:42 -04:00
2018-04-28 01:22:18 -04:00
while ( Running )
2018-04-26 02:13:04 -04:00
{
2018-07-01 20:30:38 -04:00
// select the server ids that have completed the update task
var serverTasksToRemove = runningUpdateTasks
2018-08-27 18:07:54 -04:00
. Where ( ut = > ut . Value . Status = = TaskStatus . RanToCompletion | |
ut . Value . Status = = TaskStatus . Canceled | |
ut . Value . Status = = TaskStatus . Faulted )
2018-07-01 20:30:38 -04:00
. Select ( ut = > ut . Key )
. ToList ( ) ;
2018-07-04 22:09:42 -04:00
// this is to prevent the log reader from starting before the initial
2018-07-29 15:43:42 -04:00
// query of players on the server
2018-07-04 22:09:42 -04:00
if ( serverTasksToRemove . Count > 0 )
{
IsInitialized = true ;
}
2018-07-01 20:30:38 -04:00
// remove the update tasks as they have completd
2018-11-27 19:31:48 -05:00
foreach ( long serverId in serverTasksToRemove )
2018-04-28 01:22:18 -04:00
{
2018-07-01 20:30:38 -04:00
runningUpdateTasks . Remove ( serverId ) ;
}
// select the servers where the tasks have completed
2018-11-27 19:31:48 -05:00
var serverIds = Servers . Select ( s = > s . EndPoint ) . Except ( runningUpdateTasks . Select ( r = > r . Key ) ) . ToList ( ) ;
foreach ( var server in Servers . Where ( s = > serverIds . Contains ( s . EndPoint ) ) )
2018-07-01 20:30:38 -04:00
{
2018-11-27 19:31:48 -05:00
runningUpdateTasks . Add ( server . EndPoint , Task . Run ( async ( ) = >
2018-04-28 01:22:18 -04:00
{
try
{
2019-04-23 18:27:20 -04:00
await server . ProcessUpdatesAsync ( token ) ;
if ( server . Throttled )
{
await Task . Delay ( ( int ) _throttleTimeout . TotalMilliseconds ) ;
}
2018-04-28 01:22:18 -04:00
}
catch ( Exception e )
{
Logger . WriteWarning ( $"Failed to update status for {server}" ) ;
2018-09-23 20:45:54 -04:00
Logger . WriteDebug ( e . GetExceptionInfo ( ) ) ;
2018-04-28 01:22:18 -04:00
}
2019-04-02 21:20:37 -04:00
finally
{
server . IsInitialized = true ;
}
2018-06-30 21:55:16 -04:00
} ) ) ;
2018-04-28 01:22:18 -04:00
}
#if DEBUG
2018-07-01 20:30:38 -04:00
Logger . WriteDebug ( $"{runningUpdateTasks.Count} servers queued for stats updates" ) ;
2018-04-28 01:22:18 -04:00
ThreadPool . GetMaxThreads ( out int workerThreads , out int n ) ;
ThreadPool . GetAvailableThreads ( out int availableThreads , out int m ) ;
Logger . WriteDebug ( $"There are {workerThreads - availableThreads} active threading tasks" ) ;
#endif
2019-04-08 13:29:48 -04:00
try
{
await Task . Delay ( ConfigHandler . Configuration ( ) . RConPollRate , token ) ;
}
// if a cancellation is received, we want to return immediately
catch { break ; }
2018-04-28 01:22:18 -04:00
}
2018-04-26 02:13:04 -04:00
}
2018-03-06 02:22:19 -05:00
public async Task Init ( )
2015-07-06 13:13:42 -04:00
{
2018-05-10 01:34:29 -04:00
Running = true ;
2019-04-08 13:29:48 -04:00
ExternalIPAddress = await Utilities . GetExternalIP ( ) ;
2019-02-04 20:38:24 -05:00
#region PLUGINS
SharedLibraryCore . Plugins . PluginImporter . Load ( this ) ;
foreach ( var Plugin in SharedLibraryCore . Plugins . PluginImporter . ActivePlugins )
{
try
{
await Plugin . OnLoadAsync ( this ) ;
}
catch ( Exception ex )
{
Logger . WriteError ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_PLUGIN "]} {Plugin.Name}" ) ;
Logger . WriteDebug ( ex . GetExceptionInfo ( ) ) ;
}
}
#endregion
2019-01-27 19:41:54 -05:00
#region CONFIG
2019-01-27 20:45:35 -05:00
var config = ConfigHandler . Configuration ( ) ;
2018-04-19 01:48:14 -04:00
// copy over default config if it doesn't exist
if ( config = = null )
2018-03-18 22:25:11 -04:00
{
2018-04-19 01:48:14 -04:00
var defaultConfig = new BaseConfigurationHandler < DefaultConfiguration > ( "DefaultSettings" ) . Configuration ( ) ;
ConfigHandler . Set ( ( ApplicationConfiguration ) new ApplicationConfiguration ( ) . Generate ( ) ) ;
var newConfig = ConfigHandler . Configuration ( ) ;
newConfig . AutoMessages = defaultConfig . AutoMessages ;
newConfig . GlobalRules = defaultConfig . GlobalRules ;
newConfig . Maps = defaultConfig . Maps ;
2019-04-08 21:31:32 -04:00
newConfig . DisallowedClientNames = defaultConfig . DisallowedClientNames ;
2019-03-31 20:56:31 -04:00
newConfig . QuickMessages = defaultConfig . QuickMessages ;
2018-04-19 01:48:14 -04:00
if ( newConfig . Servers = = null )
{
ConfigHandler . Set ( newConfig ) ;
2018-04-23 17:03:50 -04:00
newConfig . Servers = new List < ServerConfiguration > ( ) ;
do
{
2019-02-04 20:38:24 -05:00
var serverConfig = new ServerConfiguration ( ) ;
foreach ( var parser in AdditionalRConParsers )
{
serverConfig . AddRConParser ( parser ) ;
}
foreach ( var parser in AdditionalEventParsers )
{
serverConfig . AddEventParser ( parser ) ;
}
newConfig . Servers . Add ( ( ServerConfiguration ) serverConfig . Generate ( ) ) ;
2018-05-05 16:36:26 -04:00
} while ( Utilities . PromptBool ( Utilities . CurrentLocalization . LocalizationIndex [ "SETUP_SERVER_SAVE" ] ) ) ;
2018-04-23 17:03:50 -04:00
2018-04-19 01:48:14 -04:00
config = newConfig ;
await ConfigHandler . Save ( ) ;
}
2018-03-18 22:25:11 -04:00
}
2019-04-08 21:31:32 -04:00
else
2018-04-18 16:46:53 -04:00
{
if ( string . IsNullOrEmpty ( config . Id ) )
{
config . Id = Guid . NewGuid ( ) . ToString ( ) ;
await ConfigHandler . Save ( ) ;
}
2018-04-19 01:48:14 -04:00
if ( string . IsNullOrEmpty ( config . WebfrontBindUrl ) )
{
2018-09-23 20:45:54 -04:00
config . WebfrontBindUrl = "http://0.0.0.0:1624" ;
2018-04-19 01:48:14 -04:00
await ConfigHandler . Save ( ) ;
}
2019-02-05 12:14:43 -05:00
foreach ( var serverConfig in config . Servers )
{
2019-02-09 16:35:13 -05:00
Migration . ConfigurationMigration . ModifyLogPath020919 ( serverConfig ) ;
2019-02-05 12:14:43 -05:00
if ( serverConfig . RConParserVersion = = null | | serverConfig . EventParserVersion = = null )
{
foreach ( var parser in AdditionalRConParsers )
{
serverConfig . AddRConParser ( parser ) ;
}
foreach ( var parser in AdditionalEventParsers )
{
serverConfig . AddEventParser ( parser ) ;
}
serverConfig . ModifyParsers ( ) ;
}
2019-02-09 16:35:13 -05:00
await ConfigHandler . Save ( ) ;
2019-02-05 12:14:43 -05:00
}
2018-04-18 16:46:53 -04:00
}
2019-04-08 21:31:32 -04:00
if ( config . Servers . Count = = 0 )
2018-11-25 21:00:36 -05:00
{
2018-03-18 22:25:11 -04:00
throw new ServerException ( "A server configuration in IW4MAdminSettings.json is invalid" ) ;
2018-11-25 21:00:36 -05:00
}
2018-03-18 22:25:11 -04:00
2018-04-21 18:18:20 -04:00
Encoding . RegisterProvider ( CodePagesEncodingProvider . Instance ) ;
2018-04-22 16:04:18 -04:00
Utilities . EncodingType = Encoding . GetEncoding ( ! string . IsNullOrEmpty ( config . CustomParserEncoding ) ? config . CustomParserEncoding : "windows-1252" ) ;
2018-04-21 18:18:20 -04:00
2018-07-29 15:43:42 -04:00
#endregion
2019-01-27 19:41:54 -05:00
#region DATABASE
using ( var db = new DatabaseContext ( GetApplicationSettings ( ) . Configuration ( ) ? . ConnectionString ,
GetApplicationSettings ( ) . Configuration ( ) ? . DatabaseProvider ) )
{
await new ContextSeed ( db ) . Seed ( ) ;
}
PrivilegedClients = ( await ClientSvc . GetPrivilegedClients ( ) ) . ToDictionary ( _client = > _client . ClientId ) ;
#endregion
2018-07-29 15:43:42 -04:00
#region COMMANDS
2017-11-25 20:29:58 -05:00
if ( ClientSvc . GetOwners ( ) . Result . Count = = 0 )
2018-11-25 21:00:36 -05:00
{
2017-11-15 16:04:13 -05:00
Commands . Add ( new COwner ( ) ) ;
2018-11-25 21:00:36 -05:00
}
2017-11-15 16:04:13 -05:00
Commands . Add ( new CQuit ( ) ) ;
Commands . Add ( new CKick ( ) ) ;
Commands . Add ( new CSay ( ) ) ;
Commands . Add ( new CTempBan ( ) ) ;
Commands . Add ( new CBan ( ) ) ;
Commands . Add ( new CWhoAmI ( ) ) ;
Commands . Add ( new CList ( ) ) ;
Commands . Add ( new CHelp ( ) ) ;
Commands . Add ( new CFastRestart ( ) ) ;
Commands . Add ( new CMapRotate ( ) ) ;
Commands . Add ( new CSetLevel ( ) ) ;
Commands . Add ( new CUsage ( ) ) ;
Commands . Add ( new CUptime ( ) ) ;
Commands . Add ( new CWarn ( ) ) ;
Commands . Add ( new CWarnClear ( ) ) ;
Commands . Add ( new CUnban ( ) ) ;
Commands . Add ( new CListAdmins ( ) ) ;
Commands . Add ( new CLoadMap ( ) ) ;
Commands . Add ( new CFindPlayer ( ) ) ;
Commands . Add ( new CListRules ( ) ) ;
Commands . Add ( new CPrivateMessage ( ) ) ;
Commands . Add ( new CFlag ( ) ) ;
2018-05-08 00:58:46 -04:00
Commands . Add ( new CUnflag ( ) ) ;
2017-11-15 16:04:13 -05:00
Commands . Add ( new CReport ( ) ) ;
Commands . Add ( new CListReports ( ) ) ;
Commands . Add ( new CListBanInfo ( ) ) ;
Commands . Add ( new CListAlias ( ) ) ;
Commands . Add ( new CExecuteRCON ( ) ) ;
Commands . Add ( new CPlugins ( ) ) ;
Commands . Add ( new CIP ( ) ) ;
2017-11-18 01:59:37 -05:00
Commands . Add ( new CMask ( ) ) ;
2018-02-10 01:26:38 -05:00
Commands . Add ( new CPruneAdmins ( ) ) ;
2018-05-05 16:36:26 -04:00
Commands . Add ( new CKillServer ( ) ) ;
2018-04-04 15:38:34 -04:00
Commands . Add ( new CSetPassword ( ) ) ;
2018-04-14 00:51:38 -04:00
Commands . Add ( new CPing ( ) ) ;
2018-06-02 00:48:10 -04:00
Commands . Add ( new CSetGravatar ( ) ) ;
2018-06-02 22:21:01 -04:00
Commands . Add ( new CNextMap ( ) ) ;
2019-02-16 16:04:40 -05:00
Commands . Add ( new RequestTokenCommand ( ) ) ;
2017-06-19 13:58:01 -04:00
2018-04-08 02:44:42 -04:00
foreach ( Command C in SharedLibraryCore . Plugins . PluginImporter . ActiveCommands )
2018-11-25 21:00:36 -05:00
{
2017-06-19 13:58:01 -04:00
Commands . Add ( C ) ;
2018-11-25 21:00:36 -05:00
}
2018-07-29 15:43:42 -04:00
#endregion
2017-06-19 13:58:01 -04:00
2019-02-22 20:06:51 -05:00
#region META
2019-03-29 22:56:56 -04:00
async Task < List < ProfileMeta > > getProfileMeta ( int clientId , int offset , int count , DateTime ? startAt )
2019-02-22 20:06:51 -05:00
{
2019-03-27 20:40:26 -04:00
var metaList = new List < ProfileMeta > ( ) ;
2019-02-22 20:06:51 -05:00
2019-03-27 20:40:26 -04:00
// we don't want to return anything because it means we're trying to retrieve paged meta data
if ( count > 1 )
2019-02-22 20:06:51 -05:00
{
2019-03-27 20:40:26 -04:00
return metaList ;
}
2019-02-22 20:06:51 -05:00
2019-03-27 20:40:26 -04:00
var lastMapMeta = await _metaService . GetPersistentMeta ( "LastMapPlayed" , new EFClient ( ) { ClientId = clientId } ) ;
2019-02-22 20:06:51 -05:00
2019-03-29 22:56:56 -04:00
if ( lastMapMeta ! = null )
2019-03-27 20:40:26 -04:00
{
2019-03-29 22:56:56 -04:00
metaList . Add ( new ProfileMeta ( )
{
Id = lastMapMeta . MetaId ,
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_LAST_MAP" ] ,
Value = lastMapMeta . Value ,
Show = true ,
2019-03-30 18:21:01 -04:00
Type = ProfileMeta . MetaType . Information ,
2019-03-29 22:56:56 -04:00
} ) ;
}
2019-02-22 20:06:51 -05:00
2019-03-27 20:40:26 -04:00
var lastServerMeta = await _metaService . GetPersistentMeta ( "LastServerPlayed" , new EFClient ( ) { ClientId = clientId } ) ;
2019-03-29 22:56:56 -04:00
if ( lastServerMeta ! = null )
2019-02-22 20:06:51 -05:00
{
2019-03-29 22:56:56 -04:00
metaList . Add ( new ProfileMeta ( )
{
Id = lastServerMeta . MetaId ,
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_LAST_SERVER" ] ,
Value = lastServerMeta . Value ,
Show = true ,
Type = ProfileMeta . MetaType . Information
} ) ;
}
2019-02-22 20:06:51 -05:00
2019-03-27 20:40:26 -04:00
var client = await GetClientService ( ) . Get ( clientId ) ;
metaList . Add ( new ProfileMeta ( )
{
Id = client . ClientId ,
Key = $"{Utilities.CurrentLocalization.LocalizationIndex[" GLOBAL_TIME_HOURS "]} {Utilities.CurrentLocalization.LocalizationIndex[" WEBFRONT_PROFILE_PLAYER "]}" ,
2019-03-30 18:21:01 -04:00
Value = Math . Round ( client . TotalConnectionTime / 3600.0 , 1 ) . ToString ( "#,##0" , new System . Globalization . CultureInfo ( Utilities . CurrentLocalization . LocalizationName ) ) ,
2019-03-27 20:40:26 -04:00
Show = true ,
2019-03-30 18:21:01 -04:00
Column = 1 ,
Order = 0 ,
2019-03-27 20:40:26 -04:00
Type = ProfileMeta . MetaType . Information
} ) ;
metaList . Add ( new ProfileMeta ( )
{
Id = client . ClientId ,
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_PROFILE_FSEEN" ] ,
Value = Utilities . GetTimePassed ( client . FirstConnection , false ) ,
Show = true ,
2019-03-30 18:21:01 -04:00
Column = 1 ,
Order = 1 ,
2019-03-27 20:40:26 -04:00
Type = ProfileMeta . MetaType . Information
} ) ;
metaList . Add ( new ProfileMeta ( )
{
Id = client . ClientId ,
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_PROFILE_LSEEN" ] ,
Value = Utilities . GetTimePassed ( client . LastConnection , false ) ,
Show = true ,
2019-03-30 18:21:01 -04:00
Column = 1 ,
Order = 2 ,
2019-03-27 20:40:26 -04:00
Type = ProfileMeta . MetaType . Information
} ) ;
metaList . Add ( new ProfileMeta ( )
{
Id = client . ClientId ,
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_CONNECTIONS" ] ,
2019-03-30 18:21:01 -04:00
Value = client . Connections . ToString ( "#,##0" , new System . Globalization . CultureInfo ( Utilities . CurrentLocalization . LocalizationName ) ) ,
2019-03-27 20:40:26 -04:00
Show = true ,
2019-03-30 18:21:01 -04:00
Column = 1 ,
Order = 3 ,
2019-03-27 20:40:26 -04:00
Type = ProfileMeta . MetaType . Information
} ) ;
2019-03-29 22:56:56 -04:00
metaList . Add ( new ProfileMeta ( )
{
Key = Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_MASKED" ] ,
Value = client . Masked ? Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_TRUE" ] : Utilities . CurrentLocalization . LocalizationIndex [ "WEBFRONT_CLIENT_META_FALSE" ] ,
Sensitive = true ,
2019-03-30 18:21:01 -04:00
Column = 1 ,
Order = 4 ,
2019-03-29 22:56:56 -04:00
Type = ProfileMeta . MetaType . Information
} ) ;
2019-03-27 20:40:26 -04:00
return metaList ;
2019-03-29 22:56:56 -04:00
}
async Task < List < ProfileMeta > > getPenaltyMeta ( int clientId , int offset , int count , DateTime ? startAt )
{
if ( count < = 1 )
{
return new List < ProfileMeta > ( ) ;
}
2019-03-31 20:56:31 -04:00
var penalties = await GetPenaltyService ( ) . GetClientPenaltyForMetaAsync ( clientId , count , offset , startAt ) ;
2019-03-29 22:56:56 -04:00
return penalties . Select ( _penalty = > new ProfileMeta ( )
{
2019-03-31 20:56:31 -04:00
Id = _penalty . Id ,
2019-03-29 22:56:56 -04:00
Type = _penalty . PunisherId = = clientId ? ProfileMeta . MetaType . Penalized : ProfileMeta . MetaType . ReceivedPenalty ,
2019-03-31 20:56:31 -04:00
Value = _penalty ,
When = _penalty . TimePunished ,
Sensitive = _penalty . Sensitive
2019-03-29 22:56:56 -04:00
} )
. ToList ( ) ;
}
2019-02-22 20:06:51 -05:00
2019-03-27 20:40:26 -04:00
MetaService . AddRuntimeMeta ( getProfileMeta ) ;
2019-03-29 22:56:56 -04:00
MetaService . AddRuntimeMeta ( getPenaltyMeta ) ;
2019-02-22 20:06:51 -05:00
#endregion
2018-07-29 15:43:42 -04:00
#region INIT
2019-02-24 20:35:59 -05:00
int failedServers = 0 ;
int successServers = 0 ;
Exception lastException = null ;
2018-04-16 16:31:14 -04:00
async Task Init ( ServerConfiguration Conf )
{
2018-05-10 01:34:29 -04:00
// setup the event handler after the class is initialized
2019-02-24 20:35:59 -05:00
2018-05-10 01:34:29 -04:00
Handler = new GameEventHandler ( this ) ;
2019-02-24 20:35:59 -05:00
2018-04-16 16:31:14 -04:00
try
{
var ServerInstance = new IW4MServer ( this , Conf ) ;
await ServerInstance . Initialize ( ) ;
lock ( _servers )
{
_servers . Add ( ServerInstance ) ;
}
2019-04-09 16:02:49 -04:00
Logger . WriteVerbose ( Utilities . CurrentLocalization . LocalizationIndex [ "MANAGER_MONITORING_TEXT" ] . FormatExt ( ServerInstance . Hostname ) ) ;
2018-04-26 02:13:04 -04:00
// add the start event for this server
2018-08-30 21:53:00 -04:00
var e = new GameEvent ( )
{
Type = GameEvent . EventType . Start ,
Data = $"{ServerInstance.GameName} started" ,
Owner = ServerInstance
} ;
Handler . AddEvent ( e ) ;
2019-02-24 20:35:59 -05:00
successServers + + ;
2018-04-16 16:31:14 -04:00
}
catch ( ServerException e )
{
2019-04-21 17:28:13 -04:00
Logger . WriteError ( Utilities . CurrentLocalization . LocalizationIndex [ "SERVER_ERROR_UNFIXABLE" ] . FormatExt ( $"[{Conf.IPAddress}:{Conf.Port}]" ) ) ;
2019-02-24 20:35:59 -05:00
2018-04-16 16:31:14 -04:00
if ( e . GetType ( ) = = typeof ( DvarException ) )
2018-11-25 21:00:36 -05:00
{
2019-04-09 16:02:49 -04:00
Logger . WriteDebug ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_DVAR "].FormatExt((e as DvarException).Data[" dvar_name "])} ({Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_DVAR_HELP "]})" ) ;
2018-11-25 21:00:36 -05:00
}
2019-02-24 20:35:59 -05:00
2018-04-16 16:31:14 -04:00
else if ( e . GetType ( ) = = typeof ( NetworkException ) )
{
Logger . WriteDebug ( e . Message ) ;
}
2019-02-24 20:35:59 -05:00
failedServers + + ;
lastException = e ;
2018-04-16 16:31:14 -04:00
}
}
await Task . WhenAll ( config . Servers . Select ( c = > Init ( c ) ) . ToArray ( ) ) ;
2019-02-24 20:35:59 -05:00
2019-04-16 12:32:42 -04:00
if ( successServers - failedServers < = 0 )
2019-02-24 20:35:59 -05:00
{
if ( ! Utilities . PromptBool ( Utilities . CurrentLocalization . LocalizationIndex [ "MANAGER_START_WITH_ERRORS" ] ) )
{
throw lastException ;
}
}
2018-07-29 15:43:42 -04:00
#endregion
2015-07-03 00:10:01 -04:00
}
2017-08-09 00:35:23 -04:00
2018-05-17 19:31:58 -04:00
private async Task SendHeartbeat ( object state )
2018-04-18 16:46:53 -04:00
{
2018-04-28 01:22:18 -04:00
var heartbeatState = ( HeartbeatState ) state ;
2018-05-16 00:57:37 -04:00
while ( Running )
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
if ( ! heartbeatState . Connected )
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
try
{
2018-05-17 19:31:58 -04:00
await Heartbeat . Send ( this , true ) ;
2018-05-16 00:57:37 -04:00
heartbeatState . Connected = true ;
}
2018-04-18 16:46:53 -04:00
2018-05-16 00:57:37 -04:00
catch ( Exception e )
{
heartbeatState . Connected = false ;
Logger . WriteWarning ( $"Could not connect to heartbeat server - {e.Message}" ) ;
}
2018-04-18 16:46:53 -04:00
}
2018-05-16 00:57:37 -04:00
else
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
try
{
2018-05-17 19:31:58 -04:00
await Heartbeat . Send ( this ) ;
2018-05-16 00:57:37 -04:00
}
2018-04-18 16:46:53 -04:00
2018-05-16 00:57:37 -04:00
catch ( System . Net . Http . HttpRequestException e )
{
Logger . WriteWarning ( $"Could not send heartbeat - {e.Message}" ) ;
}
2018-04-18 16:46:53 -04:00
2018-05-16 00:57:37 -04:00
catch ( AggregateException e )
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
Logger . WriteWarning ( $"Could not send heartbeat - {e.Message}" ) ;
var exceptions = e . InnerExceptions . Where ( ex = > ex . GetType ( ) = = typeof ( RestEase . ApiException ) ) ;
foreach ( var ex in exceptions )
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
if ( ( ( RestEase . ApiException ) ex ) . StatusCode = = System . Net . HttpStatusCode . Unauthorized )
{
heartbeatState . Connected = false ;
}
2018-04-18 16:46:53 -04:00
}
}
2018-05-16 00:57:37 -04:00
catch ( RestEase . ApiException e )
2018-04-18 16:46:53 -04:00
{
2018-05-16 00:57:37 -04:00
Logger . WriteWarning ( $"Could not send heartbeat - {e.Message}" ) ;
if ( e . StatusCode = = System . Net . HttpStatusCode . Unauthorized )
{
heartbeatState . Connected = false ;
}
}
catch ( Exception e )
{
Logger . WriteWarning ( $"Could not send heartbeat - {e.Message}" ) ;
2018-04-18 16:46:53 -04:00
}
2018-05-16 00:57:37 -04:00
}
2019-04-08 13:29:48 -04:00
try
{
await Task . Delay ( 30000 , heartbeatState . Token ) ;
}
catch { break ; }
2018-04-18 16:46:53 -04:00
}
}
2018-08-27 18:07:54 -04:00
public void Start ( )
2015-07-03 00:10:01 -04:00
{
2019-04-08 13:29:48 -04:00
var tokenSource = new CancellationTokenSource ( ) ;
2018-05-16 00:57:37 -04:00
// this needs to be run seperately from the main thread
2019-04-08 13:29:48 -04:00
_ = Task . Run ( ( ) = > SendHeartbeat ( new HeartbeatState ( ) { Token = tokenSource . Token } ) ) ;
_ = Task . Run ( ( ) = > UpdateServerStates ( tokenSource . Token ) ) ;
2018-05-08 00:58:46 -04:00
2018-04-28 17:39:45 -04:00
while ( Running )
2015-07-03 00:10:01 -04:00
{
2018-08-28 17:32:59 -04:00
OnQuit . Wait ( ) ;
2019-04-08 13:29:48 -04:00
tokenSource . Cancel ( ) ;
2018-08-28 17:32:59 -04:00
OnQuit . Reset ( ) ;
2018-04-28 17:39:45 -04:00
}
2015-07-03 00:10:01 -04:00
}
2015-07-06 15:51:08 -04:00
2017-05-26 18:49:27 -04:00
public void Stop ( )
2015-07-06 15:51:08 -04:00
{
2017-05-26 18:49:27 -04:00
Running = false ;
2019-04-08 13:29:48 -04:00
OnQuit . Set ( ) ;
2015-07-06 15:51:08 -04:00
}
2017-05-27 18:08:04 -04:00
2018-11-27 19:31:48 -05:00
public ILogger GetLogger ( long serverId )
2017-05-27 19:29:20 -04:00
{
2018-10-06 12:47:14 -04:00
if ( Loggers . ContainsKey ( serverId ) )
{
return Loggers [ serverId ] ;
}
else
{
Logger newLogger ;
if ( serverId = = 0 )
{
newLogger = new Logger ( "IW4MAdmin-Manager" ) ;
}
else
{
newLogger = new Logger ( $"IW4MAdmin-Server-{serverId}" ) ;
}
Loggers . Add ( serverId , newLogger ) ;
return newLogger ;
}
2017-05-27 19:29:20 -04:00
}
2017-05-31 01:31:56 -04:00
2017-06-19 13:58:01 -04:00
public IList < MessageToken > GetMessageTokens ( )
2017-05-31 01:31:56 -04:00
{
return MessageTokens ;
}
2017-06-12 13:50:00 -04:00
2018-11-25 21:00:36 -05:00
public IList < EFClient > GetActiveClients ( )
{
return _servers . SelectMany ( s = > s . Clients ) . Where ( p = > p ! = null ) . ToList ( ) ;
}
2017-08-17 19:28:08 -04:00
2018-11-25 21:00:36 -05:00
public ClientService GetClientService ( )
{
return ClientSvc ;
}
public AliasService GetAliasService ( )
{
return AliasSvc ;
}
public PenaltyService GetPenaltyService ( )
{
return PenaltySvc ;
}
public IConfigurationHandler < ApplicationConfiguration > GetApplicationSettings ( )
{
return ConfigHandler ;
}
public IDictionary < int , EFClient > GetPrivilegedClients ( )
{
return PrivilegedClients ;
}
public bool ShutdownRequested ( )
{
return ! Running ;
}
public IEventHandler GetEventHandler ( )
{
return Handler ;
}
2018-04-26 02:13:04 -04:00
public void SetHasEvent ( )
{
2019-04-08 13:29:48 -04:00
2018-04-26 02:13:04 -04:00
}
2018-05-28 21:30:31 -04:00
2018-11-25 21:00:36 -05:00
public IList < Assembly > GetPluginAssemblies ( )
{
2019-02-16 18:18:50 -05:00
return SharedLibraryCore . Plugins . PluginImporter . PluginAssemblies . Union ( SharedLibraryCore . Plugins . PluginImporter . Assemblies ) . ToList ( ) ;
2018-11-25 21:00:36 -05:00
}
2018-08-03 18:10:20 -04:00
2018-11-25 21:00:36 -05:00
public IPageList GetPageList ( )
{
return PageList ;
}
2019-01-26 21:33:37 -05:00
2019-01-27 19:41:54 -05:00
public IRConParser GenerateDynamicRConParser ( )
{
return new DynamicRConParser ( ) ;
}
2019-01-26 21:33:37 -05:00
2019-01-27 19:41:54 -05:00
public IEventParser GenerateDynamicEventParser ( )
{
return new DynamicEventParser ( ) ;
}
2015-07-03 00:10:01 -04:00
}
}