Added AsyncStatus class to keep track of the timing of each update on servers

This commit is contained in:
RaidMax 2017-05-28 15:47:21 -05:00
parent ac7908de91
commit 28fcc7b922
12 changed files with 111 additions and 41 deletions

4
.gitignore vendored
View File

@ -190,3 +190,7 @@ ModelManifest.xml
/.vs/IW4M Admin/v15 /.vs/IW4M Admin/v15
/.vs/IW4M Admin/v15/Browse.VC.db /.vs/IW4M Admin/v15/Browse.VC.db
/.vs/IW4M Admin/v15 /.vs/IW4M Admin/v15
/DEPLOY
/DEPLOY
*.bat
/DEPLOY/clean_for_publish.bat

View File

@ -26,10 +26,11 @@
<MapFileExtensions>false</MapFileExtensions> <MapFileExtensions>false</MapFileExtensions>
<InstallUrl>http://raidmax.org/IW4M/Admin/</InstallUrl> <InstallUrl>http://raidmax.org/IW4M/Admin/</InstallUrl>
<SupportUrl>http://raidmax.org/IW4MAdmin</SupportUrl> <SupportUrl>http://raidmax.org/IW4MAdmin</SupportUrl>
<TargetCulture>en</TargetCulture>
<ProductName>IW4M Administration</ProductName> <ProductName>IW4M Administration</ProductName>
<PublisherName>RaidMax LLC</PublisherName> <PublisherName>ForeverNone LLC</PublisherName>
<CreateWebPageOnPublish>true</CreateWebPageOnPublish>
<WebPage>publish.htm</WebPage> <WebPage>publish.htm</WebPage>
<OpenBrowserOnPublish>false</OpenBrowserOnPublish>
<ApplicationRevision>6</ApplicationRevision> <ApplicationRevision>6</ApplicationRevision>
<ApplicationVersion>1.3.1.%2a</ApplicationVersion> <ApplicationVersion>1.3.1.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust> <UseApplicationTrust>false</UseApplicationTrust>
@ -318,7 +319,10 @@ copy /Y "$(TargetDir)$(TargetName).exe" "$(SolutionDir)BUILD"
copy /Y "$(TargetDir)IW4MAdmin.exe.config" "$(SolutionDir)BUILD" copy /Y "$(TargetDir)IW4MAdmin.exe.config" "$(SolutionDir)BUILD"
copy /Y "$(ProjectDir)lib\Kayak.dll" "$(SolutionDir)BUILD\lib" copy /Y "$(ProjectDir)lib\Kayak.dll" "$(SolutionDir)BUILD\lib"
copy /Y "$(ProjectDir)lib\SQLite.Interop.dll" "$(SolutionDir)BUILD\lib" copy /Y "$(ProjectDir)lib\SQLite.Interop.dll" "$(SolutionDir)BUILD\lib"
</PostBuildEvent>
if $(ConfigurationName) == Release powershell.exe -file "$(SolutionDir)DEPLOY\publish_nightly.ps1" %25iw4madmin_version%25</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -32,12 +32,12 @@ namespace IW4MAdmin
// lets keep it simple and dispose of everything quickly as logging wont be that much (relatively) // lets keep it simple and dispose of everything quickly as logging wont be that much (relatively)
Console.WriteLine(LogLine); Console.WriteLine(LogLine);
File.AppendAllText(FileName, LogLine); File.AppendAllText(FileName, LogLine + Environment.NewLine);
#else #else
if (type == LogType.Error || type == LogType.Verbose) if (type == LogType.Error || type == LogType.Verbose)
Console.WriteLine(LogLine); Console.WriteLine(LogLine);
if (type != LogType.Debug) if (type != LogType.Debug)
File.AppendAllText(FileName, LogLine); File.AppendAllText(FileName, LogLine + Environment.NewLine);
#endif #endif
} }

View File

@ -17,6 +17,7 @@ namespace IW4MAdmin
{ {
static Manager Instance; static Manager Instance;
public List<Server> Servers { get; private set; } public List<Server> Servers { get; private set; }
List<AsyncStatus> TaskStatuses;
Database ClientDatabase; Database ClientDatabase;
SharedLibrary.Interfaces.IPenaltyList ClientPenalties; SharedLibrary.Interfaces.IPenaltyList ClientPenalties;
List<Command> Commands; List<Command> Commands;
@ -25,9 +26,9 @@ namespace IW4MAdmin
public SharedLibrary.Interfaces.ILogger Logger { get; private set; } public SharedLibrary.Interfaces.ILogger Logger { get; private set; }
public bool Running { get; private set; } public bool Running { get; private set; }
#if FTP_LOG #if FTP_LOG
const double UPDATE_FREQUENCY = 15000; const int UPDATE_FREQUENCY = 15000;
#else #else
const double UPDATE_FREQUENCY = 300; const int UPDATE_FREQUENCY = 300;
#endif #endif
private Manager() private Manager()
@ -37,6 +38,8 @@ namespace IW4MAdmin
//Logger = new Log(logFile, Log.Level.Production, 0); //Logger = new Log(logFile, Log.Level.Production, 0);
Servers = new List<Server>(); Servers = new List<Server>();
Commands = new List<Command>(); Commands = new List<Command>();
TaskStatuses = new List<AsyncStatus>();
ClientDatabase = new ClientsDB("Database/clients.rm"); ClientDatabase = new ClientsDB("Database/clients.rm");
ClientPenalties = new PenaltyList(); ClientPenalties = new PenaltyList();
@ -78,6 +81,11 @@ namespace IW4MAdmin
{ {
await ServerInstance.Initialize(); await ServerInstance.Initialize();
Servers.Add(ServerInstance); Servers.Add(ServerInstance);
// this way we can keep track of execution time and see if problems arise.
var Status = new AsyncStatus(ServerInstance);
TaskStatuses.Add(Status);
Logger.WriteVerbose($"Now monitoring {ServerInstance.Hostname}"); Logger.WriteVerbose($"Now monitoring {ServerInstance.Hostname}");
} }
@ -95,37 +103,31 @@ namespace IW4MAdmin
webServiceTask = WebService.getScheduler(); webServiceTask = WebService.getScheduler();
WebThread = new Thread(webServiceTask.Start); WebThread = new Thread(webServiceTask.Start)
WebThread.Name = "Web Thread"; {
Name = "Web Thread"
};
WebThread.Start(); WebThread.Start();
while (Servers.Count < 1)
Thread.Sleep(500);
Running = true; Running = true;
} }
public void Start() public void Start()
{ {
int Processed; while (Running)
DateTime Start;
while(Running)
{ {
Processed = 0; foreach (var Status in TaskStatuses)
Start = DateTime.Now;
foreach (Server S in Servers)
Processed += S.ProcessUpdatesAsync().Result;
// ideally we don't want to sleep on the thread, but polling
// as much as possible will use unnecessary CPU
int ElapsedTime = (int)(DateTime.Now - Start).TotalMilliseconds;
while ((Processed != Servers.Count || ElapsedTime < UPDATE_FREQUENCY) && Running)
{ {
Thread.Sleep((int)(UPDATE_FREQUENCY - ElapsedTime)); if (Status.RequestedTask == null || Status.RequestedTask.IsCompleted)
ElapsedTime = (int)(DateTime.Now - Start).TotalMilliseconds; {
Status.Update(new Task(() => (Status.Dependant as Server).ProcessUpdatesAsync(Status.GetToken())));
if (Status.RunAverage > 500)
Logger.WriteWarning($"Update task average execution is longer than desired for {(Status.Dependant as Server).getIP()}::{(Status.Dependant as Server).getPort()}");
}
} }
Thread.Sleep(UPDATE_FREQUENCY);
} }
#if !DEBUG #if !DEBUG
foreach (var S in Servers) foreach (var S in Servers)

View File

@ -199,7 +199,7 @@ namespace IW4MAdmin
#if DEBUG == false #if DEBUG == false
catch (Exception E) catch (Exception E)
{ {
Log.Write("Unable to add player " + P.Name + " - " + E.Message, Log.Level.Debug); Manager.GetLogger().WriteError("Unable to add player " + P.Name + " - " + E.Message);
return false; return false;
} }
#endif #endif
@ -376,7 +376,7 @@ namespace IW4MAdmin
DateTime lastCount = DateTime.Now; DateTime lastCount = DateTime.Now;
DateTime tickTime = DateTime.Now; DateTime tickTime = DateTime.Now;
override public async Task<int> ProcessUpdatesAsync() override public async Task ProcessUpdatesAsync(CancellationToken cts)
{ {
#if DEBUG == false #if DEBUG == false
try try
@ -389,8 +389,13 @@ namespace IW4MAdmin
if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000) if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000)
{ {
// We don't want to await here, just in case user plugins are really slow :c
foreach (var Plugin in PluginImporter.potentialPlugins) foreach (var Plugin in PluginImporter.potentialPlugins)
await Plugin.OnTickAsync(this); #if !DEBUG
Plugin.OnTickAsync(this);
#else
await Plugin.OnTickAsync(this);
#endif
tickTime = DateTime.Now; tickTime = DateTime.Now;
} }
@ -459,16 +464,13 @@ namespace IW4MAdmin
} }
oldLines = lines; oldLines = lines;
l_size = logFile.getSize(); l_size = logFile.getSize();
return 1;
} }
#if DEBUG == false #if DEBUG == false
catch (Exception E) catch (Exception E)
{ {
Log.Write("Unexpected error on \"" + Hostname + "\"", Log.Level.Debug); Logger.WriteError("Unexpected error on \"" + Hostname + "\"");
Log.Write("Error Message: " + E.Message, Log.Level.Debug); Logger.WriteDebug("Error Message: " + E.Message);
Log.Write("Error Trace: " + E.StackTrace, Log.Level.Debug); Logger.WriteDebug("Error Trace: " + E.StackTrace);
return 1; return 1;
} }
#endif #endif
@ -532,7 +534,6 @@ namespace IW4MAdmin
logFile = new IFile(logPath); logFile = new IFile(logPath);
Logger.WriteInfo("Log file is " + logPath); Logger.WriteInfo("Log file is " + logPath);
await ExecuteEvent(new Event(Event.GType.Start, "Server started", null, null, this)); await ExecuteEvent(new Event(Event.GType.Start, "Server started", null, null, this));
//Bans = Manager.GetClientDatabase().getBans();
#if !DEBUG #if !DEBUG
Broadcast("IW4M Admin is now ^2ONLINE"); Broadcast("IW4M Admin is now ^2ONLINE");
#endif #endif

Binary file not shown.

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SharedLibrary
{
public sealed class AsyncStatus
{
CancellationToken Token;
DateTime StartTime;
int TimesRun;
public double RunAverage { get; private set; }
public object Dependant { get; private set; }
public Task RequestedTask { get; private set; }
public AsyncStatus(object dependant)
{
Token = new CancellationToken();
StartTime = DateTime.Now;
Dependant = dependant;
// technically 0 but it's faster than checking for division by 0
TimesRun = 1;
}
public CancellationToken GetToken()
{
return Token;
}
public double ElapsedMillisecondsTime()
{
return (DateTime.Now - StartTime).TotalMilliseconds;
}
public void Update(Task T)
{
RequestedTask = T;
RequestedTask.Start();
if (TimesRun > 100)
TimesRun = 1;
RunAverage = RunAverage + ((DateTime.Now - StartTime).TotalMilliseconds - RunAverage) / TimesRun;
StartTime = DateTime.Now;
}
}
}

View File

@ -236,7 +236,10 @@ namespace SharedLibrary.Commands
public override async Task ExecuteAsync(Event E) public override async Task ExecuteAsync(Event E)
{ {
await E.Owner.Broadcast($"Fast restarting in ^53 ^7seconds [^5{E.Origin.Name}^7]"); if (!E.Origin.Masked)
await E.Owner.Broadcast($"Fast restarting in ^53 ^7seconds [^5{E.Origin.Name}^7]");
else
await E.Owner.Broadcast($"Fast restarting in ^53 ^7seconds [^5Masked Admin^7]");
await Task.Delay(3000); await Task.Delay(3000);
await E.Owner.ExecuteCommandAsync("fast_restart"); await E.Owner.ExecuteCommandAsync("fast_restart");
} }
@ -248,7 +251,10 @@ namespace SharedLibrary.Commands
public override async Task ExecuteAsync(Event E) public override async Task ExecuteAsync(Event E)
{ {
await E.Owner.Broadcast($"Map rotating in ^55 ^7seconds [^5{E.Origin.Name}^7]"); if (!E.Origin.Masked)
await E.Owner.Broadcast($"Map rotating in ^55 ^7seconds [^5{E.Origin.Name}^7]");
else
await E.Owner.Broadcast($"Map rotating in ^55 ^7seconds [^5Masked Admin^7]");
await Task.Delay(5000); await Task.Delay(5000);
await E.Owner.ExecuteCommandAsync("map_rotate"); await E.Owner.ExecuteCommandAsync("map_rotate");
} }

View File

@ -153,7 +153,7 @@ namespace SharedLibrary
#if DEBUG == false #if DEBUG == false
catch (Exception E) catch (Exception E)
{ {
SV.Log.Write("Error requesting event " + E.Message, Log.Level.Debug); SV.Manager.GetLogger().WriteError("Error requesting event " + E.Message);
return null; return null;
} }
#endif #endif

View File

@ -189,7 +189,7 @@ namespace SharedLibrary
/// <returns></returns> /// <returns></returns>
abstract public Task<Command> ProcessCommand(Event E, Command C); abstract public Task<Command> ProcessCommand(Event E, Command C);
virtual public Task<int> ProcessUpdatesAsync() virtual public Task ProcessUpdatesAsync(CancellationToken cts)
{ {
return null; return null;
} }

View File

@ -54,6 +54,7 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AsyncStatus.cs" />
<Compile Include="Commands\NativeCommands.cs" /> <Compile Include="Commands\NativeCommands.cs" />
<Compile Include="Exceptions\CommandException.cs" /> <Compile Include="Exceptions\CommandException.cs" />
<Compile Include="Exceptions\DvarException.cs" /> <Compile Include="Exceptions\DvarException.cs" />

View File

@ -5,6 +5,7 @@ using SharedLibrary;
using SharedLibrary.Network; using SharedLibrary.Network;
using SharedLibrary.Interfaces; using SharedLibrary.Interfaces;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading;
namespace Votemap_Plugin namespace Votemap_Plugin
{ {
@ -222,6 +223,7 @@ namespace Votemap_Plugin
public async Task OnTickAsync(Server S) public async Task OnTickAsync(Server S)
{ {
var serverVotes = getServerVotes(S.getPort()); var serverVotes = getServerVotes(S.getPort());
Thread.Sleep(500);
if (serverVotes != null) if (serverVotes != null)
{ {