2015-07-03 00:10:01 -04:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading ;
2017-05-26 18:49:27 -04:00
using System.Threading.Tasks ;
2018-09-29 15:52:22 -04:00
using System.Text ;
using System.Reflection ;
2015-07-03 00:10:01 -04:00
2018-04-08 02:44:42 -04:00
using SharedLibraryCore ;
using SharedLibraryCore.Interfaces ;
using SharedLibraryCore.Commands ;
using SharedLibraryCore.Helpers ;
using SharedLibraryCore.Exceptions ;
using SharedLibraryCore.Objects ;
using SharedLibraryCore.Services ;
using SharedLibraryCore.Configuration ;
2018-08-28 17:32:59 -04:00
using SharedLibraryCore.Database ;
2018-08-30 21:53:00 -04:00
using SharedLibraryCore.Events ;
2017-06-19 13:58:01 -04:00
2018-09-29 15:52:22 -04:00
using IW4MAdmin.Application.API.Master ;
2018-10-05 23:10:39 -04:00
using IW4MAdmin.Application.Migration ;
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-04-02 01:25:06 -04:00
public Dictionary < int , Player > 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
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-10-06 12:47:14 -04:00
readonly Dictionary < int , ILogger > Loggers = new Dictionary < int , ILogger > ( ) ;
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
{
2018-10-05 23:10:39 -04:00
// do any needed migrations
2018-10-06 12:47:14 -04:00
// todo: move out
2018-10-05 23:10:39 -04:00
ConfigurationMigration . MoveConfigFolder10518 ( Logger ) ;
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-04-02 01:25:06 -04:00
PrivilegedClients = new Dictionary < int , Player > ( ) ;
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 ( ) ;
2018-09-29 15:52:22 -04:00
OnServerEvent + = OnGameEvent ;
2018-08-30 21:53:00 -04:00
OnServerEvent + = EventApi . OnGameEvent ;
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
{
// if the origin client is not in an authorized state (detected by RCon) don't execute the event
if ( GameEvent . ShouldOriginEventBeDelayed ( newEvent ) )
{
Logger . WriteDebug ( $"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed" ) ;
2018-09-02 17:59:27 -04:00
if ( newEvent . Type = = GameEvent . EventType . Command )
{
2018-09-29 15:52:22 -04:00
newEvent . Origin . Tell ( Utilities . CurrentLocalization . LocalizationIndex [ "SERVER_DELAYED_EVENT_WAIT" ] ) ;
2018-09-02 17:59:27 -04:00
}
2018-08-27 18:07:54 -04:00
// offload it to the player to keep
newEvent . Origin . DelayedEvents . Enqueue ( newEvent ) ;
}
// if the target client is not in an authorized state (detected by RCon) don't execute the event
2018-09-29 15:52:22 -04:00
else if ( GameEvent . ShouldTargetEventBeDelayed ( newEvent ) )
2018-08-27 18:07:54 -04:00
{
Logger . WriteDebug ( $"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed" ) ;
// offload it to the player to keep
newEvent . Target . DelayedEvents . Enqueue ( newEvent ) ;
}
2018-09-29 15:52:22 -04:00
else
2018-08-27 18:07:54 -04:00
{
2018-09-29 15:52:22 -04:00
await newEvent . Owner . ExecuteEvent ( newEvent ) ;
// save the event info to the database
var changeHistorySvc = new ChangeHistoryService ( ) ;
await changeHistorySvc . Add ( args . Event ) ;
// todo: this is a hacky mess
if ( newEvent . Origin ? . DelayedEvents . Count > 0 & &
( //newEvent.Origin?.State == Player.ClientState.Connected ||
newEvent . Type = = GameEvent . EventType . Connect ) )
2018-08-27 18:07:54 -04:00
{
2018-09-29 15:52:22 -04:00
var events = newEvent . Origin . DelayedEvents ;
2018-08-30 21:53:00 -04:00
2018-09-29 15:52:22 -04:00
// add the delayed event to the queue
while ( events . Count > 0 )
2018-08-27 18:07:54 -04:00
{
2018-09-29 15:52:22 -04:00
var oldEvent = events . Dequeue ( ) ;
var e = new GameEvent ( )
{
Type = oldEvent . Type ,
Origin = newEvent . Origin ,
Data = oldEvent . Data ,
Extra = oldEvent . Extra ,
Owner = oldEvent . Owner ,
Message = oldEvent . Message ,
Target = oldEvent . Target ,
Remote = oldEvent . Remote
} ;
e . Origin = newEvent . Origin ;
// check if the target was assigned
if ( e . Target ! = null )
2018-08-27 18:07:54 -04:00
{
2018-09-29 15:52:22 -04:00
// update the target incase they left or have newer info
e . Target = newEvent . Owner . GetPlayersAsList ( )
. FirstOrDefault ( p = > p . NetworkId = = e . Target . NetworkId ) ;
// we have to throw out the event because they left
if ( e . Target = = null )
{
Logger . WriteWarning ( $"Delayed event for {e.Origin} was ignored because the target has left" ) ;
// hack: don't do anything with the event because the target is invalid
e . Type = GameEvent . EventType . Unknown ;
}
2018-08-27 18:07:54 -04:00
}
2018-09-29 15:52:22 -04:00
Logger . WriteDebug ( $"Adding delayed event of type {e.Type} for {e.Origin} back for processing" ) ;
this . GetEventHandler ( ) . AddEvent ( e ) ;
2018-08-27 18:07:54 -04:00
}
}
}
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 ;
2018-08-27 18:07:54 -04:00
Logger . WriteError ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_EXCEPTION "]} {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-10-02 13:39:08 -04:00
skip :
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
}
2018-07-01 20:30:38 -04:00
public async Task UpdateServerStates ( )
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
var runningUpdateTasks = new Dictionary < int , 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
foreach ( int 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-08-27 18:07:54 -04:00
var serverIds = Servers . Select ( s = > s . GetHashCode ( ) ) . Except ( runningUpdateTasks . Select ( r = > r . Key ) ) . ToList ( ) ;
foreach ( var server in Servers . Where ( s = > serverIds . Contains ( s . GetHashCode ( ) ) ) )
2018-07-01 20:30:38 -04:00
{
runningUpdateTasks . Add ( server . GetHashCode ( ) , Task . Run ( async ( ) = >
2018-04-28 01:22:18 -04:00
{
try
{
await server . ProcessUpdatesAsync ( new CancellationToken ( ) ) ;
}
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
}
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
2018-07-04 22:09:42 -04:00
#if DEBUG
2018-08-27 18:07:54 -04:00
await Task . Delay ( 10000 ) ;
2018-07-04 22:09:42 -04:00
#else
2018-06-07 22:19:12 -04:00
await Task . Delay ( ConfigHandler . Configuration ( ) . RConPollRate ) ;
2018-07-04 22:09:42 -04:00
#endif
2018-04-28 01:22:18 -04:00
}
2018-09-23 20:45:54 -04:00
// trigger the event processing loop to end
SetHasEvent ( ) ;
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 ;
2018-07-29 15:43:42 -04:00
#region DATABASE
2018-09-29 22:49:12 -04:00
using ( var db = new DatabaseContext ( GetApplicationSettings ( ) . Configuration ( ) ? . ConnectionString , GetApplicationSettings ( ) . Configuration ( ) ? . DatabaseProvider ) )
2018-08-28 17:32:59 -04:00
{
await new ContextSeed ( db ) . Seed ( ) ;
}
2018-09-16 16:34:16 -04:00
// todo: optimize this (or replace it)
2018-03-27 00:54:20 -04:00
var ipList = ( await ClientSvc . Find ( c = > c . Level > Player . Permission . Trusted ) )
2018-04-04 15:38:34 -04:00
. Select ( c = > new
{
c . Password ,
c . PasswordSalt ,
c . ClientId ,
c . Level ,
c . Name
} ) ;
2018-03-27 00:54:20 -04:00
foreach ( var a in ipList )
{
try
{
2018-04-04 15:38:34 -04:00
PrivilegedClients . Add ( a . ClientId , new Player ( )
2018-04-02 01:25:06 -04:00
{
2018-04-04 15:38:34 -04:00
Name = a . Name ,
2018-04-02 01:25:06 -04:00
ClientId = a . ClientId ,
2018-04-04 15:38:34 -04:00
Level = a . Level ,
PasswordSalt = a . PasswordSalt ,
Password = a . Password
2018-04-02 01:25:06 -04:00
} ) ;
2018-03-27 00:54:20 -04:00
}
catch ( ArgumentException )
{
continue ;
}
}
2018-07-29 15:43:42 -04:00
#endregion
2017-09-29 22:42:24 -04:00
2018-07-29 15:43:42 -04:00
#region CONFIG
2018-03-18 22:25:11 -04: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 . AutoMessagePeriod = defaultConfig . AutoMessagePeriod ;
newConfig . AutoMessages = defaultConfig . AutoMessages ;
newConfig . GlobalRules = defaultConfig . GlobalRules ;
newConfig . Maps = defaultConfig . Maps ;
if ( newConfig . Servers = = null )
{
ConfigHandler . Set ( newConfig ) ;
2018-04-23 17:03:50 -04:00
newConfig . Servers = new List < ServerConfiguration > ( ) ;
do
{
newConfig . Servers . Add ( ( ServerConfiguration ) new ServerConfiguration ( ) . 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
}
2018-04-19 01:48:14 -04:00
else if ( config ! = null )
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 ( ) ;
}
2018-04-18 16:46:53 -04:00
}
2018-03-18 22:25:11 -04:00
else if ( config . Servers . Count = = 0 )
throw new ServerException ( "A server configuration in IW4MAdminSettings.json is invalid" ) ;
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
#region PLUGINS
2018-04-08 02:44:42 -04:00
SharedLibraryCore . Plugins . PluginImporter . Load ( this ) ;
2017-08-09 00:35:23 -04:00
2018-04-08 02:44:42 -04:00
foreach ( var Plugin in SharedLibraryCore . Plugins . PluginImporter . ActivePlugins )
2017-08-09 00:35:23 -04:00
{
try
{
2018-03-06 02:22:19 -05:00
await Plugin . OnLoadAsync ( this ) ;
2017-08-09 00:35:23 -04:00
}
2018-10-06 16:31:05 -04:00
catch ( Exception ex )
2017-08-09 00:35:23 -04:00
{
2018-05-05 16:36:26 -04:00
Logger . WriteError ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_PLUGIN "]} {Plugin.Name}" ) ;
2018-10-06 16:31:05 -04:00
Logger . WriteDebug ( ex . GetExceptionInfo ( ) ) ;
2017-08-09 00:35:23 -04:00
}
}
2018-07-29 15:43:42 -04:00
#endregion
2017-08-09 00:35:23 -04:00
2018-07-29 15:43:42 -04:00
#region COMMANDS
2017-11-25 20:29:58 -05:00
if ( ClientSvc . GetOwners ( ) . Result . Count = = 0 )
2017-11-15 16:04:13 -05:00
Commands . Add ( new COwner ( ) ) ;
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 ( ) ) ;
2017-06-19 13:58:01 -04:00
2018-04-08 02:44:42 -04:00
foreach ( Command C in SharedLibraryCore . Plugins . PluginImporter . ActiveCommands )
2017-06-19 13:58:01 -04:00
Commands . Add ( C ) ;
2018-07-29 15:43:42 -04:00
#endregion
2017-06-19 13:58:01 -04:00
2018-07-29 15:43:42 -04:00
#region INIT
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
Handler = new GameEventHandler ( this ) ;
2018-04-16 16:31:14 -04:00
try
{
var ServerInstance = new IW4MServer ( this , Conf ) ;
await ServerInstance . Initialize ( ) ;
lock ( _servers )
{
_servers . Add ( ServerInstance ) ;
}
2018-05-05 16:36:26 -04:00
Logger . WriteVerbose ( $"{Utilities.CurrentLocalization.LocalizationIndex[" MANAGER_MONITORING_TEXT "]} {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 ) ;
2018-04-16 16:31:14 -04:00
}
catch ( ServerException e )
{
2018-05-05 16:36:26 -04:00
Logger . WriteError ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_UNFIXABLE "]} [{Conf.IPAddress}:{Conf.Port}]" ) ;
2018-04-16 16:31:14 -04:00
if ( e . GetType ( ) = = typeof ( DvarException ) )
2018-05-05 16:36:26 -04:00
Logger . WriteDebug ( $"{Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_DVAR "]} {(e as DvarException).Data[" dvar_name "]} ({Utilities.CurrentLocalization.LocalizationIndex[" SERVER_ERROR_DVAR_HELP "]})" ) ;
2018-04-16 16:31:14 -04:00
else if ( e . GetType ( ) = = typeof ( NetworkException ) )
{
Logger . WriteDebug ( e . Message ) ;
}
// throw the exception to the main method to stop before instantly exiting
throw e ;
}
}
await Task . WhenAll ( config . Servers . Select ( c = > Init ( c ) ) . ToArray ( ) ) ;
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
}
2018-05-17 19:31:58 -04:00
await Task . Delay ( 30000 ) ;
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
{
2018-05-16 00:57:37 -04:00
// this needs to be run seperately from the main thread
2018-09-23 20:45:54 -04:00
var _ = Task . Run ( ( ) = > SendHeartbeat ( new HeartbeatState ( ) ) ) ;
_ = Task . Run ( ( ) = > UpdateServerStates ( ) ) ;
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 ( ) ;
OnQuit . Reset ( ) ;
2018-04-28 17:39:45 -04:00
}
_servers . Clear ( ) ;
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 ;
2015-07-06 15:51:08 -04:00
}
2017-05-27 18:08:04 -04:00
2018-10-06 12:47:14 -04:00
public ILogger GetLogger ( int 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-09-23 20:45:54 -04:00
public IList < Player > GetActiveClients ( ) = > _servers . SelectMany ( s = > s . Players ) . Where ( p = > p ! = null ) . ToList ( ) ;
2017-08-17 19:28:08 -04:00
2017-11-25 20:29:58 -05:00
public ClientService GetClientService ( ) = > ClientSvc ;
public AliasService GetAliasService ( ) = > AliasSvc ;
public PenaltyService GetPenaltyService ( ) = > PenaltySvc ;
2018-03-18 22:25:11 -04:00
public IConfigurationHandler < ApplicationConfiguration > GetApplicationSettings ( ) = > ConfigHandler ;
2018-04-05 00:38:45 -04:00
public IDictionary < int , Player > GetPrivilegedClients ( ) = > PrivilegedClients ;
2018-04-08 17:50:58 -04:00
public bool ShutdownRequested ( ) = > ! Running ;
2018-04-26 02:13:04 -04:00
public IEventHandler GetEventHandler ( ) = > Handler ;
public void SetHasEvent ( )
{
2018-08-28 17:32:59 -04:00
OnQuit . Set ( ) ;
2018-04-26 02:13:04 -04:00
}
2018-05-28 21:30:31 -04:00
public IList < Assembly > GetPluginAssemblies ( ) = > SharedLibraryCore . Plugins . PluginImporter . PluginAssemblies ;
2018-08-03 18:10:20 -04:00
public IPageList GetPageList ( ) = > PageList ;
2015-07-03 00:10:01 -04:00
}
}