fixed issue with status response erroring when incorrect length
view angle vector parse fail is now a handled exception change local host check to byte array to make it faster than comparing string kick command now requires moderator level or higher tempban now requires administrator level or higher hopefully fixed negative SPM bug pipelined the events and consolidated them to run through GameEventHandler uniform console colors
This commit is contained in:
parent
ece519251a
commit
99390f1f35
@ -19,6 +19,7 @@
|
|||||||
<AssemblyName>IW4MAdmin</AssemblyName>
|
<AssemblyName>IW4MAdmin</AssemblyName>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<Win32Resource />
|
<Win32Resource />
|
||||||
|
<RootNamespace>IW4MAdmin.Application</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
46
Application/GameEventHandler.cs
Normal file
46
Application/GameEventHandler.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application
|
||||||
|
{
|
||||||
|
class GameEventHandler : IEventHandler
|
||||||
|
{
|
||||||
|
private Queue<GameEvent> EventQueue;
|
||||||
|
private IManager Manager;
|
||||||
|
|
||||||
|
public GameEventHandler(IManager mgr)
|
||||||
|
{
|
||||||
|
EventQueue = new Queue<GameEvent>();
|
||||||
|
Manager = mgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEvent(GameEvent gameEvent)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner}");
|
||||||
|
#endif
|
||||||
|
EventQueue.Enqueue(gameEvent);
|
||||||
|
#if DEBUG
|
||||||
|
Manager.GetLogger().WriteDebug($"There are now {EventQueue.Count} events in queue");
|
||||||
|
#endif
|
||||||
|
Manager.SetHasEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetEventOutput()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameEvent GetNextEvent()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Manager.GetLogger().WriteDebug("Getting next event to be processed");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return EventQueue.Count > 0 ? EventQueue.Dequeue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
Application/IO/GameLogEvent.cs
Normal file
72
Application/IO/GameLogEvent.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.IO
|
||||||
|
{
|
||||||
|
class GameLogEvent
|
||||||
|
{
|
||||||
|
FileSystemWatcher LogPathWatcher;
|
||||||
|
Server Server;
|
||||||
|
long PreviousFileSize;
|
||||||
|
GameLogReader Reader;
|
||||||
|
Timer RefreshInfoTimer;
|
||||||
|
string GameLogFile;
|
||||||
|
FileInfo Info;
|
||||||
|
|
||||||
|
public GameLogEvent(Server server, string gameLogPath, string gameLogName)
|
||||||
|
{
|
||||||
|
GameLogFile = gameLogPath;
|
||||||
|
Reader = new GameLogReader(gameLogPath, server.EventParser);
|
||||||
|
Server = server;
|
||||||
|
RefreshInfoTimer = new Timer((sender) =>
|
||||||
|
{
|
||||||
|
var newInfo = new FileInfo(GameLogFile);
|
||||||
|
if (newInfo.Length - Info?.Length > 0)
|
||||||
|
LogPathWatcher_Changed(this, new FileSystemEventArgs(WatcherChangeTypes.Changed, "", ""));
|
||||||
|
Info = newInfo;
|
||||||
|
|
||||||
|
}, null, 0, 100);
|
||||||
|
LogPathWatcher = new FileSystemWatcher()
|
||||||
|
{
|
||||||
|
Path = gameLogPath.Replace(gameLogName, ""),
|
||||||
|
Filter = gameLogName,
|
||||||
|
NotifyFilter = (NotifyFilters)383,
|
||||||
|
InternalBufferSize = 4096
|
||||||
|
};
|
||||||
|
|
||||||
|
LogPathWatcher.Changed += LogPathWatcher_Changed;
|
||||||
|
LogPathWatcher.EnableRaisingEvents = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~GameLogEvent()
|
||||||
|
{
|
||||||
|
LogPathWatcher.EnableRaisingEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogPathWatcher_Changed(object sender, FileSystemEventArgs e)
|
||||||
|
{
|
||||||
|
// retrieve the new file size
|
||||||
|
long newFileSize = new FileInfo(GameLogFile).Length;
|
||||||
|
|
||||||
|
if (PreviousFileSize == 0)
|
||||||
|
PreviousFileSize = newFileSize;
|
||||||
|
|
||||||
|
long fileDiff = newFileSize - PreviousFileSize;
|
||||||
|
|
||||||
|
if (fileDiff < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var events = Reader.EventsFromLog(Server, fileDiff);
|
||||||
|
foreach (var ev in events)
|
||||||
|
Server.Manager.GetEventHandler().AddEvent(ev);
|
||||||
|
|
||||||
|
PreviousFileSize = newFileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
Application/IO/GameLogReader.cs
Normal file
49
Application/IO/GameLogReader.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.IO
|
||||||
|
{
|
||||||
|
class GameLogReader
|
||||||
|
{
|
||||||
|
IEventParser Parser;
|
||||||
|
string LogFile;
|
||||||
|
|
||||||
|
public GameLogReader(string logFile, IEventParser parser)
|
||||||
|
{
|
||||||
|
LogFile = logFile;
|
||||||
|
Parser = parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<GameEvent> EventsFromLog(Server server, long fileSizeDiff)
|
||||||
|
{
|
||||||
|
// allocate the bytes for the new log lines
|
||||||
|
byte[] fileBytes = new byte[fileSizeDiff];
|
||||||
|
|
||||||
|
// open the file as a stream
|
||||||
|
using (var rd = new BinaryReader(new FileStream(LogFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite), Utilities.EncodingType))
|
||||||
|
{
|
||||||
|
rd.BaseStream.Seek(rd.BaseStream.Length - fileSizeDiff - 1, SeekOrigin.Begin);
|
||||||
|
// the difference should be in the range of a int :P
|
||||||
|
rd.Read(fileBytes, 0, (int)fileSizeDiff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to event line list
|
||||||
|
string[] logLines = Utilities.EncodingType.GetString(fileBytes).Replace("\r", "").Split('\n');
|
||||||
|
|
||||||
|
List<GameEvent> events = new List<GameEvent>();
|
||||||
|
|
||||||
|
// parse each line
|
||||||
|
foreach (string eventLine in logLines)
|
||||||
|
{
|
||||||
|
if (eventLine.Length > 0)
|
||||||
|
events.Add(Parser.GetEvent(server, eventLine));
|
||||||
|
}
|
||||||
|
|
||||||
|
return events;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,17 +30,8 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
void Write(string msg, LogType type)
|
void Write(string msg, LogType type)
|
||||||
{
|
{
|
||||||
string stringType;
|
if (!Utilities.CurrentLocalization.LocalizationSet.TryGetValue($"GLOBAL_{type.ToString().ToUpper()}", out string stringType))
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
stringType = Utilities.CurrentLocalization.LocalizationSet[$"GLOBAL_{type.ToString().ToUpper()}"];
|
|
||||||
}
|
|
||||||
|
|
||||||
catch(KeyNotFoundException)
|
|
||||||
{
|
|
||||||
stringType = type.ToString();
|
stringType = type.ToString();
|
||||||
}
|
|
||||||
|
|
||||||
string LogLine = $"[{DateTime.Now.ToString("HH:mm:ss")}] - {stringType}: {msg}";
|
string LogLine = $"[{DateTime.Now.ToString("HH:mm:ss")}] - {stringType}: {msg}";
|
||||||
lock (ThreadLock)
|
lock (ThreadLock)
|
||||||
|
@ -24,6 +24,7 @@ namespace IW4MAdmin.Application
|
|||||||
Localization.Configure.Initialize();
|
Localization.Configure.Initialize();
|
||||||
var loc = Utilities.CurrentLocalization.LocalizationSet;
|
var loc = Utilities.CurrentLocalization.LocalizationSet;
|
||||||
Console.OutputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
|
|
||||||
Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f;
|
Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f;
|
||||||
Version = Math.Round(Version, 2);
|
Version = Math.Round(Version, 2);
|
||||||
@ -40,7 +41,7 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
ServerManager = ApplicationManager.GetInstance();
|
ServerManager = ApplicationManager.GetInstance();
|
||||||
|
|
||||||
using (var db = new DatabaseContext(ServerManager.GetApplicationSettings().Configuration().ConnectionString))
|
using (var db = new DatabaseContext(ServerManager.GetApplicationSettings().Configuration().ConnectionString))
|
||||||
new ContextSeed(db).Seed().Wait();
|
new ContextSeed(db).Seed().Wait();
|
||||||
|
|
||||||
var api = API.Master.Endpoint.Get();
|
var api = API.Master.Endpoint.Get();
|
||||||
@ -69,7 +70,7 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
|
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !PRERELEASE
|
#if !PRERELEASE
|
||||||
@ -78,7 +79,7 @@ namespace IW4MAdmin.Application
|
|||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
|
Console.WriteLine($"IW4MAdmin {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionStable.ToString("0.0")}]");
|
||||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}]");
|
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}]");
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
else if (version.CurrentVersionPrerelease > Version)
|
else if (version.CurrentVersionPrerelease > Version)
|
||||||
@ -86,14 +87,14 @@ namespace IW4MAdmin.Application
|
|||||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||||
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
|
Console.WriteLine($"IW4MAdmin-Prerelease {loc["MANAGER_VERSION_UPDATE"]} [v{version.CurrentVersionPrerelease.ToString("0.0")}-pr]");
|
||||||
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}-pr]");
|
Console.WriteLine($"{loc["MANAGER_VERSION_CURRENT"]} [v{Version.ToString("0.0")}-pr]");
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Green;
|
Console.ForegroundColor = ConsoleColor.Green;
|
||||||
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
|
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
|
||||||
Console.ForegroundColor = ConsoleColor.White;
|
Console.ForegroundColor = ConsoleColor.Gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerManager.Init().Wait();
|
ServerManager.Init().Wait();
|
||||||
@ -112,13 +113,13 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
if (ServerManager.Servers.Count == 0)
|
if (ServerManager.Servers.Count == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine("No servers are currently being monitored");
|
Console.WriteLine(loc["MANAGER_CONSOLE_NOSERV"]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Origin.CurrentServer = ServerManager.Servers[0];
|
Origin.CurrentServer = ServerManager.Servers[0];
|
||||||
GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
|
GameEvent E = new GameEvent(GameEvent.EventType.Say, userInput, Origin, null, ServerManager.Servers[0]);
|
||||||
ServerManager.Servers[0].ExecuteEvent(E);
|
ServerManager.GetEventHandler().AddEvent(E);
|
||||||
Console.Write('>');
|
Console.Write('>');
|
||||||
|
|
||||||
} while (ServerManager.Running);
|
} while (ServerManager.Running);
|
||||||
@ -128,10 +129,6 @@ namespace IW4MAdmin.Application
|
|||||||
{
|
{
|
||||||
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
|
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerManager.Start();
|
|
||||||
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -145,6 +142,10 @@ namespace IW4MAdmin.Application
|
|||||||
Console.WriteLine(loc["MANAGER_EXIT"]);
|
Console.WriteLine(loc["MANAGER_EXIT"]);
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ServerManager.Start();
|
||||||
|
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CheckDirectories()
|
static void CheckDirectories()
|
||||||
|
@ -41,10 +41,13 @@ namespace IW4MAdmin.Application
|
|||||||
PenaltyService PenaltySvc;
|
PenaltyService PenaltySvc;
|
||||||
BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||||
EventApi Api;
|
EventApi Api;
|
||||||
|
GameEventHandler Handler;
|
||||||
|
ManualResetEventSlim OnEvent;
|
||||||
|
Timer StatusUpdateTimer;
|
||||||
#if FTP_LOG
|
#if FTP_LOG
|
||||||
const int UPDATE_FREQUENCY = 700;
|
const int UPDATE_FREQUENCY = 700;
|
||||||
#else
|
#else
|
||||||
const int UPDATE_FREQUENCY = 450;
|
const int UPDATE_FREQUENCY = 2000;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
@ -63,6 +66,7 @@ namespace IW4MAdmin.Application
|
|||||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||||
StartTime = DateTime.UtcNow;
|
StartTime = DateTime.UtcNow;
|
||||||
|
OnEvent = new ManualResetEventSlim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCancelKey(object sender, ConsoleCancelEventArgs args)
|
private void OnCancelKey(object sender, ConsoleCancelEventArgs args)
|
||||||
@ -86,8 +90,18 @@ namespace IW4MAdmin.Application
|
|||||||
return Instance ?? (Instance = new ApplicationManager());
|
return Instance ?? (Instance = new ApplicationManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateStatus(object state)
|
||||||
|
{
|
||||||
|
foreach (var server in Servers)
|
||||||
|
{
|
||||||
|
Task.Run(() => server.ProcessUpdatesAsync(new CancellationToken()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
{
|
{
|
||||||
|
// setup the event handler after the class is initialized
|
||||||
|
Handler = new GameEventHandler(this);
|
||||||
#region DATABASE
|
#region DATABASE
|
||||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||||
.Select(c => new
|
.Select(c => new
|
||||||
@ -248,13 +262,8 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationSet["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationSet["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
||||||
|
// add the start event for this server
|
||||||
// this way we can keep track of execution time and see if problems arise.
|
Handler.AddEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, ServerInstance));
|
||||||
var Status = new AsyncStatus(ServerInstance, UPDATE_FREQUENCY);
|
|
||||||
lock (TaskStatuses)
|
|
||||||
{
|
|
||||||
TaskStatuses.Add(Status);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ServerException e)
|
catch (ServerException e)
|
||||||
@ -273,6 +282,8 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
||||||
|
// start polling servers
|
||||||
|
StatusUpdateTimer = new Timer(UpdateStatus, null, 0, 10000);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -344,38 +355,32 @@ namespace IW4MAdmin.Application
|
|||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
Task.Run(() => HeartBeatThread());
|
Task.Run(() => HeartBeatThread());
|
||||||
while (Running || TaskStatuses.Count > 0)
|
GameEvent newEvent;
|
||||||
|
while (Running)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < TaskStatuses.Count; i++)
|
// wait for new event to be added
|
||||||
|
OnEvent.Wait();
|
||||||
|
|
||||||
|
// todo: sequencially or parallelize?
|
||||||
|
while ((newEvent = Handler.GetNextEvent()) != null)
|
||||||
{
|
{
|
||||||
var Status = TaskStatuses[i];
|
try
|
||||||
|
|
||||||
// task is read to be rerun
|
|
||||||
if (Status.RequestedTask == null || Status.RequestedTask.Status == TaskStatus.RanToCompletion)
|
|
||||||
{
|
{
|
||||||
// remove the task when we want to quit and last run has finished
|
Task.WaitAll(newEvent.Owner.ExecuteEvent(newEvent));
|
||||||
if (!Running)
|
|
||||||
{
|
|
||||||
TaskStatuses.RemoveAt(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// normal operation
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Status.Update(new Task<bool>(() => { return (Status.Dependant as Server).ProcessUpdatesAsync(Status.GetToken()).Result; }));
|
|
||||||
if (Status.RunAverage > 1000 + UPDATE_FREQUENCY && !(Status.Dependant as Server).Throttled)
|
|
||||||
Logger.WriteWarning($"Update task average execution is longer than desired for {(Status.Dependant as Server)} [{Status.RunAverage}ms]");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Status.RequestedTask.Status == TaskStatus.Faulted)
|
catch (Exception E)
|
||||||
{
|
{
|
||||||
Logger.WriteWarning($"Update task for {(Status.Dependant as Server)} faulted, restarting");
|
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationSet["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}");
|
||||||
Status.Abort();
|
Logger.WriteDebug("Error Message: " + E.Message);
|
||||||
|
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
||||||
}
|
}
|
||||||
|
// tell anyone waiting for the output that we're done
|
||||||
|
newEvent.OnProcessed.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Sleep(UPDATE_FREQUENCY);
|
// signal that all events have been processed
|
||||||
|
OnEvent.Reset();
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
foreach (var S in Servers)
|
foreach (var S in Servers)
|
||||||
@ -388,6 +393,9 @@ namespace IW4MAdmin.Application
|
|||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
Running = false;
|
Running = false;
|
||||||
|
|
||||||
|
// trigger the event processing loop to end
|
||||||
|
SetHasEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILogger GetLogger()
|
public ILogger GetLogger()
|
||||||
@ -417,5 +425,11 @@ namespace IW4MAdmin.Application
|
|||||||
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
||||||
public IEventApi GetEventApi() => Api;
|
public IEventApi GetEventApi() => Api;
|
||||||
public bool ShutdownRequested() => !Running;
|
public bool ShutdownRequested() => !Running;
|
||||||
|
public IEventHandler GetEventHandler() => Handler;
|
||||||
|
|
||||||
|
public void SetHasEvent()
|
||||||
|
{
|
||||||
|
OnEvent.Set();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,6 +85,8 @@ namespace Application.RconParsers
|
|||||||
if (Regex.Matches(responseLine, @" *^\d+", RegexOptions.IgnoreCase).Count > 0)
|
if (Regex.Matches(responseLine, @" *^\d+", RegexOptions.IgnoreCase).Count > 0)
|
||||||
{
|
{
|
||||||
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
if (playerInfo.Length < 4)
|
||||||
|
continue;
|
||||||
int cID = -1;
|
int cID = -1;
|
||||||
int Ping = -1;
|
int Ping = -1;
|
||||||
Int32.TryParse(playerInfo[2], out Ping);
|
Int32.TryParse(playerInfo[2], out Ping);
|
||||||
|
@ -18,6 +18,7 @@ using SharedLibraryCore.Exceptions;
|
|||||||
using Application.Misc;
|
using Application.Misc;
|
||||||
using Application.RconParsers;
|
using Application.RconParsers;
|
||||||
using IW4MAdmin.Application.EventParsers;
|
using IW4MAdmin.Application.EventParsers;
|
||||||
|
using IW4MAdmin.Application.IO;
|
||||||
|
|
||||||
namespace IW4MAdmin
|
namespace IW4MAdmin
|
||||||
{
|
{
|
||||||
@ -25,6 +26,8 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
private CancellationToken cts;
|
private CancellationToken cts;
|
||||||
private static Dictionary<string, string> loc = Utilities.CurrentLocalization.LocalizationSet;
|
private static Dictionary<string, string> loc = Utilities.CurrentLocalization.LocalizationSet;
|
||||||
|
private GameLogEvent LogEvent;
|
||||||
|
|
||||||
|
|
||||||
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { }
|
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { }
|
||||||
|
|
||||||
@ -180,7 +183,8 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
Logger.WriteInfo($"Client {player} connecting...");
|
Logger.WriteInfo($"Client {player} connecting...");
|
||||||
|
|
||||||
await ExecuteEvent(new GameEvent(GameEvent.EventType.Connect, "", player, null, this));
|
|
||||||
|
Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Connect, "", player, null, this));
|
||||||
|
|
||||||
|
|
||||||
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
|
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
|
||||||
@ -208,7 +212,7 @@ namespace IW4MAdmin
|
|||||||
Player Leaving = Players[cNum];
|
Player Leaving = Players[cNum];
|
||||||
Logger.WriteInfo($"Client {Leaving} disconnecting...");
|
Logger.WriteInfo($"Client {Leaving} disconnecting...");
|
||||||
|
|
||||||
await ExecuteEvent(new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this));
|
Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Disconnect, "", Leaving, null, this));
|
||||||
|
|
||||||
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
Leaving.TotalConnectionTime += (int)(DateTime.UtcNow - Leaving.ConnectionTime).TotalSeconds;
|
||||||
Leaving.LastConnection = DateTime.UtcNow;
|
Leaving.LastConnection = DateTime.UtcNow;
|
||||||
@ -255,16 +259,8 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
if (C.RequiresTarget || Args.Length > 0)
|
if (C.RequiresTarget || Args.Length > 0)
|
||||||
{
|
{
|
||||||
int cNum = -1;
|
if (!Int32.TryParse(Args[0], out int cNum))
|
||||||
try
|
cNum = -1;
|
||||||
{
|
|
||||||
cNum = Convert.ToInt32(Args[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (FormatException)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Args[0][0] == '@') // user specifying target by database ID
|
if (Args[0][0] == '@') // user specifying target by database ID
|
||||||
{
|
{
|
||||||
@ -359,24 +355,27 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
public override async Task ExecuteEvent(GameEvent E)
|
public override async Task ExecuteEvent(GameEvent E)
|
||||||
{
|
{
|
||||||
//if (Throttled)
|
bool canExecuteCommand = true;
|
||||||
// return;
|
|
||||||
|
|
||||||
await ProcessEvent(E);
|
await ProcessEvent(E);
|
||||||
Manager.GetEventApi().OnServerEvent(this, E);
|
Manager.GetEventApi().OnServerEvent(this, E);
|
||||||
|
|
||||||
foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||||
{
|
{
|
||||||
#if !DEBUG
|
|
||||||
try
|
try
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
if (cts.IsCancellationRequested)
|
if (cts.IsCancellationRequested)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
await P.OnEventAsync(E, this);
|
await P.OnEventAsync(E, this);
|
||||||
}
|
}
|
||||||
#if !DEBUG
|
|
||||||
|
// this happens if a plugin (login) wants to stop commands from executing
|
||||||
|
catch (AuthorizationException e)
|
||||||
|
{
|
||||||
|
await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
|
||||||
|
canExecuteCommand = false;
|
||||||
|
}
|
||||||
|
|
||||||
catch (Exception Except)
|
catch (Exception Except)
|
||||||
{
|
{
|
||||||
Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
|
Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
|
||||||
@ -389,10 +388,168 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hack: this prevents commands from getting executing that 'shouldn't' be
|
||||||
|
if (E.Type == GameEvent.EventType.Command &&
|
||||||
|
E.Extra != null &&
|
||||||
|
(canExecuteCommand ||
|
||||||
|
E.Origin?.Level == Player.Permission.Console))
|
||||||
|
{
|
||||||
|
await (((Command)E.Extra).ExecuteAsync(E));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Perform the server specific tasks when an event occurs
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="E"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
override protected async Task ProcessEvent(GameEvent E)
|
||||||
|
{
|
||||||
|
if (E.Type == GameEvent.EventType.Connect)
|
||||||
|
{
|
||||||
|
// special case for IW5 when connect is from the log
|
||||||
|
if (E.Extra != null)
|
||||||
|
{
|
||||||
|
var logClient = (Player)E.Extra;
|
||||||
|
var client = (await this.GetStatusAsync())
|
||||||
|
.Single(c => c.ClientNumber == logClient.ClientNumber &&
|
||||||
|
c.Name == logClient.Name);
|
||||||
|
client.NetworkId = logClient.NetworkId;
|
||||||
|
|
||||||
|
await AddPlayer(client);
|
||||||
|
|
||||||
|
// hack: to prevent plugins from registering it as a real connect
|
||||||
|
E.Type = GameEvent.EventType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ChatHistory.Add(new ChatInfo()
|
||||||
|
{
|
||||||
|
Name = E.Origin.Name,
|
||||||
|
Message = "CONNECTED",
|
||||||
|
Time = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
|
||||||
|
if (E.Origin.Level > Player.Permission.Moderator)
|
||||||
|
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||||
|
{
|
||||||
|
ChatHistory.Add(new ChatInfo()
|
||||||
|
{
|
||||||
|
Name = E.Origin.Name,
|
||||||
|
Message = "DISCONNECTED",
|
||||||
|
Time = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (E.Type == GameEvent.EventType.Script)
|
||||||
|
{
|
||||||
|
Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Type == GameEvent.EventType.Say && E.Data.Length >= 2)
|
||||||
|
{
|
||||||
|
if (E.Data.Substring(0, 1) == "!" ||
|
||||||
|
E.Data.Substring(0, 1) == "@" ||
|
||||||
|
E.Origin.Level == Player.Permission.Console)
|
||||||
|
{
|
||||||
|
Command C = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
C = await ValidateCommand(E);
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (CommandException e)
|
||||||
|
{
|
||||||
|
Logger.WriteInfo(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (C != null)
|
||||||
|
{
|
||||||
|
if (C.RequiresTarget && E.Target == null)
|
||||||
|
{
|
||||||
|
Logger.WriteWarning("Requested event (command) requiring target does not have a target!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Manager.GetEventHandler().AddEvent(new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Command,
|
||||||
|
Data = E.Data,
|
||||||
|
Origin = E.Origin,
|
||||||
|
Target = E.Target,
|
||||||
|
Owner = this,
|
||||||
|
Extra = C,
|
||||||
|
Remote = E.Remote,
|
||||||
|
Message = E.Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else // Not a command
|
||||||
|
{
|
||||||
|
E.Data = E.Data.StripColors();
|
||||||
|
|
||||||
|
ChatHistory.Add(new ChatInfo()
|
||||||
|
{
|
||||||
|
Name = E.Origin.Name,
|
||||||
|
Message = E.Data,
|
||||||
|
Time = DateTime.UtcNow
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Type == GameEvent.EventType.MapChange)
|
||||||
|
{
|
||||||
|
Logger.WriteInfo($"New map loaded - {ClientNum} active players");
|
||||||
|
|
||||||
|
// iw4 doesn't log the game info
|
||||||
|
if (E.Extra == null)
|
||||||
|
{
|
||||||
|
var dict = await this.GetInfoAsync();
|
||||||
|
|
||||||
|
Gametype = dict["gametype"].StripColors();
|
||||||
|
Hostname = dict["hostname"].StripColors();
|
||||||
|
|
||||||
|
string mapname = dict["mapname"].StripColors();
|
||||||
|
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dict = (Dictionary<string, string>)E.Extra;
|
||||||
|
Gametype = dict["g_gametype"].StripColors();
|
||||||
|
Hostname = dict["sv_hostname"].StripColors();
|
||||||
|
|
||||||
|
string mapname = dict["mapname"].StripColors();
|
||||||
|
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (E.Type == GameEvent.EventType.MapEnd)
|
||||||
|
{
|
||||||
|
Logger.WriteInfo("Game ending...");
|
||||||
|
}
|
||||||
|
|
||||||
|
//todo: move
|
||||||
|
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
|
||||||
|
ChatHistory.RemoveAt(0);
|
||||||
|
|
||||||
|
// the last client hasn't fully disconnected yet
|
||||||
|
// so there will still be at least 1 client left
|
||||||
|
if (ClientNum < 2)
|
||||||
|
ChatHistory.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async Task<int> PollPlayersAsync()
|
async Task<int> PollPlayersAsync()
|
||||||
{
|
{
|
||||||
var now = DateTime.Now;
|
var now = DateTime.Now;
|
||||||
@ -429,30 +586,16 @@ namespace IW4MAdmin
|
|||||||
return CurrentPlayers.Count;
|
return CurrentPlayers.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
long l_size = -1;
|
|
||||||
String[] lines = new String[8];
|
|
||||||
String[] oldLines = new String[8];
|
|
||||||
DateTime start = DateTime.Now;
|
DateTime start = DateTime.Now;
|
||||||
DateTime playerCountStart = DateTime.Now;
|
DateTime playerCountStart = DateTime.Now;
|
||||||
DateTime lastCount = DateTime.Now;
|
DateTime lastCount = DateTime.Now;
|
||||||
DateTime tickTime = DateTime.Now;
|
DateTime tickTime = DateTime.Now;
|
||||||
bool firstRun = true;
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
||||||
{
|
{
|
||||||
this.cts = cts;
|
this.cts = cts;
|
||||||
//#if DEBUG == false
|
|
||||||
try
|
try
|
||||||
//#endif
|
|
||||||
{
|
{
|
||||||
// first start
|
|
||||||
if (firstRun)
|
|
||||||
{
|
|
||||||
await ExecuteEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, this));
|
|
||||||
firstRun = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
|
if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -517,44 +660,6 @@ namespace IW4MAdmin
|
|||||||
start = DateTime.Now;
|
start = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LogFile == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (l_size != LogFile.Length())
|
|
||||||
{
|
|
||||||
lines = l_size != -1 ? await LogFile.Tail(12) : lines;
|
|
||||||
if (lines != oldLines)
|
|
||||||
{
|
|
||||||
l_size = LogFile.Length();
|
|
||||||
int end = (lines.Length == oldLines.Length) ? lines.Length - 1 : Math.Abs((lines.Length - oldLines.Length)) - 1;
|
|
||||||
|
|
||||||
for (count = 0; count < lines.Length; count++)
|
|
||||||
{
|
|
||||||
if (lines.Length < 1 && oldLines.Length < 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (lines[count] == oldLines[oldLines.Length - 1])
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (lines[count].Length < 10) // it's not a needed line
|
|
||||||
continue;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GameEvent event_ = EventParser.GetEvent(this, lines[count]);
|
|
||||||
if (event_ != null)
|
|
||||||
{
|
|
||||||
if (event_.Origin == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
await ExecuteEvent(event_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
oldLines = lines;
|
|
||||||
l_size = LogFile.Length();
|
|
||||||
if (Manager.ShutdownRequested())
|
if (Manager.ShutdownRequested())
|
||||||
{
|
{
|
||||||
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||||
@ -565,7 +670,6 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
//#if !DEBUG
|
|
||||||
catch (NetworkException)
|
catch (NetworkException)
|
||||||
{
|
{
|
||||||
Logger.WriteError($"{loc["SERVER_ERROR_COMMUNICATION"]} {IP}:{Port}");
|
Logger.WriteError($"{loc["SERVER_ERROR_COMMUNICATION"]} {IP}:{Port}");
|
||||||
@ -575,7 +679,6 @@ namespace IW4MAdmin
|
|||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
{
|
{
|
||||||
Logger.WriteWarning("Event could not parsed properly");
|
Logger.WriteWarning("Event could not parsed properly");
|
||||||
Logger.WriteDebug($"Log Line: {lines[count]}");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,7 +689,6 @@ namespace IW4MAdmin
|
|||||||
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
@ -657,7 +759,7 @@ namespace IW4MAdmin
|
|||||||
CustomCallback = await ScriptLoaded();
|
CustomCallback = await ScriptLoaded();
|
||||||
string mainPath = EventParser.GetGameDir();
|
string mainPath = EventParser.GetGameDir();
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
basepath.Value = @"\\192.168.88.253\Call of Duty 4";
|
basepath.Value = @"\\192.168.88.253\mw2\";
|
||||||
#endif
|
#endif
|
||||||
string logPath;
|
string logPath;
|
||||||
if (GameName == Game.IW5)
|
if (GameName == Game.IW5)
|
||||||
@ -671,6 +773,7 @@ namespace IW4MAdmin
|
|||||||
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Value.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
|
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Value.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// hopefully fix wine drive name mangling
|
// hopefully fix wine drive name mangling
|
||||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
{
|
{
|
||||||
@ -686,183 +789,18 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogFile = new IFile(logPath);
|
// LogFile = new IFile(logPath);
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
LogEvent = new GameLogEvent(this, logPath, logfile.Value);
|
||||||
Logger.WriteInfo($"Log file is {logPath}");
|
Logger.WriteInfo($"Log file is {logPath}");
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
// LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
||||||
#else
|
#else
|
||||||
await Broadcast(loc["BROADCAST_ONLINE"]);
|
await Broadcast(loc["BROADCAST_ONLINE"]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//Process any server event
|
|
||||||
override protected async Task ProcessEvent(GameEvent E)
|
|
||||||
{
|
|
||||||
if (E.Type == GameEvent.EventType.Connect)
|
|
||||||
{
|
|
||||||
// special case for IW5 when connect is from the log
|
|
||||||
if (E.Extra != null)
|
|
||||||
{
|
|
||||||
var logClient = (Player)E.Extra;
|
|
||||||
var client = (await this.GetStatusAsync())
|
|
||||||
.Single(c => c.ClientNumber == logClient.ClientNumber &&
|
|
||||||
c.Name == logClient.Name);
|
|
||||||
client.NetworkId = logClient.NetworkId;
|
|
||||||
|
|
||||||
await AddPlayer(client);
|
|
||||||
|
|
||||||
// hack: to prevent plugins from registering it as a real connect
|
|
||||||
E.Type = GameEvent.EventType.Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ChatHistory.Add(new ChatInfo()
|
|
||||||
{
|
|
||||||
Name = E.Origin.Name,
|
|
||||||
Message = "CONNECTED",
|
|
||||||
Time = DateTime.UtcNow
|
|
||||||
});
|
|
||||||
|
|
||||||
if (E.Origin.Level > Player.Permission.Moderator)
|
|
||||||
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
|
||||||
{
|
|
||||||
ChatHistory.Add(new ChatInfo()
|
|
||||||
{
|
|
||||||
Name = E.Origin.Name,
|
|
||||||
Message = "DISCONNECTED",
|
|
||||||
Time = DateTime.UtcNow
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (E.Type == GameEvent.EventType.Script)
|
|
||||||
{
|
|
||||||
await ExecuteEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.Say && E.Data.Length >= 2)
|
|
||||||
{
|
|
||||||
if (E.Data.Substring(0, 1) == "!" || E.Data.Substring(0, 1) == "@" || E.Origin.Level == Player.Permission.Console)
|
|
||||||
{
|
|
||||||
Command C = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
C = await ValidateCommand(E);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (CommandException e)
|
|
||||||
{
|
|
||||||
Logger.WriteInfo(e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (C != null)
|
|
||||||
{
|
|
||||||
if (C.RequiresTarget && E.Target == null)
|
|
||||||
{
|
|
||||||
Logger.WriteWarning("Requested event (command) requiring target does not have a target!");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!E.Remote && E.Origin.Level != Player.Permission.Console)
|
|
||||||
{
|
|
||||||
await ExecuteEvent(new GameEvent()
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Command,
|
|
||||||
Data = string.Empty,
|
|
||||||
Origin = E.Origin,
|
|
||||||
Target = E.Target,
|
|
||||||
Owner = this,
|
|
||||||
Extra = C,
|
|
||||||
Remote = E.Remote
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await C.ExecuteAsync(E);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (AuthorizationException e)
|
|
||||||
{
|
|
||||||
await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (Exception Except)
|
|
||||||
{
|
|
||||||
Logger.WriteError(String.Format($"\"{0}\" {loc["SERVER_ERROR_COMMAND_LOG"]}", C.Name));
|
|
||||||
Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
|
|
||||||
Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
|
|
||||||
await E.Origin.Tell($"^1{loc["SERVER_ERROR_COMMAND_INGAME"]}");
|
|
||||||
#if DEBUG
|
|
||||||
await E.Origin.Tell(Except.Message);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else // Not a command
|
|
||||||
{
|
|
||||||
E.Data = E.Data.StripColors();
|
|
||||||
|
|
||||||
ChatHistory.Add(new ChatInfo()
|
|
||||||
{
|
|
||||||
Name = E.Origin.Name,
|
|
||||||
Message = E.Data,
|
|
||||||
Time = DateTime.UtcNow
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.MapChange)
|
|
||||||
{
|
|
||||||
Logger.WriteInfo($"New map loaded - {ClientNum} active players");
|
|
||||||
|
|
||||||
// iw4 doesn't log the game info
|
|
||||||
if (E.Extra == null)
|
|
||||||
{
|
|
||||||
var dict = await this.GetInfoAsync();
|
|
||||||
|
|
||||||
Gametype = dict["gametype"].StripColors();
|
|
||||||
Hostname = dict["hostname"].StripColors();
|
|
||||||
|
|
||||||
string mapname = dict["mapname"].StripColors();
|
|
||||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
{
|
|
||||||
var dict = (Dictionary<string, string>)E.Extra;
|
|
||||||
Gametype = dict["g_gametype"].StripColors();
|
|
||||||
Hostname = dict["sv_hostname"].StripColors();
|
|
||||||
|
|
||||||
string mapname = dict["mapname"].StripColors();
|
|
||||||
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (E.Type == GameEvent.EventType.MapEnd)
|
|
||||||
{
|
|
||||||
Logger.WriteInfo("Game ending...");
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: move
|
|
||||||
while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
|
|
||||||
ChatHistory.RemoveAt(0);
|
|
||||||
|
|
||||||
// the last client hasn't fully disconnected yet
|
|
||||||
// so there will still be at least 1 client left
|
|
||||||
if (ClientNum < 2)
|
|
||||||
ChatHistory.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task Warn(String Reason, Player Target, Player Origin)
|
public override async Task Warn(String Reason, Player Target, Player Origin)
|
||||||
{
|
{
|
||||||
// ensure player gets warned if command not performed on them in game
|
// ensure player gets warned if command not performed on them in game
|
||||||
|
@ -70,12 +70,8 @@ namespace IW4MAdmin.Plugins.Login
|
|||||||
Config = cfg.Configuration();
|
Config = cfg.Configuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
|
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||||
|
|
||||||
public Task OnUnloadAsync()
|
public Task OnUnloadAsync() => Task.CompletedTask;
|
||||||
{
|
|
||||||
AuthorizedClients.Clear();
|
|
||||||
return Utilities.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
|||||||
BaseConfigurationHandler<Configuration> Settings;
|
BaseConfigurationHandler<Configuration> Settings;
|
||||||
ConcurrentDictionary<int, Tracking> ProfanityCounts;
|
ConcurrentDictionary<int, Tracking> ProfanityCounts;
|
||||||
IManager Manager;
|
IManager Manager;
|
||||||
Task CompletedTask = Task.FromResult(false);
|
|
||||||
|
|
||||||
public async Task OnEventAsync(GameEvent E, Server S)
|
public async Task OnEventAsync(GameEvent E, Server S)
|
||||||
{
|
{
|
||||||
@ -87,8 +86,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
|||||||
Manager = manager;
|
Manager = manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnTickAsync(Server S) => CompletedTask;
|
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||||
|
|
||||||
public Task OnUnloadAsync() => CompletedTask;
|
public Task OnUnloadAsync() => Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// get individual client's stats
|
// get individual client's stats
|
||||||
var clientStats = playerStats[pl.ClientId];
|
var clientStats = playerStats[pl.ClientId];
|
||||||
// sync their score
|
// sync their score
|
||||||
clientStats.SessionScore = pl.Score;
|
clientStats.SessionScore += (pl.Score - clientStats.LastScore);
|
||||||
// remove the client from the stats dictionary as they're leaving
|
// remove the client from the stats dictionary as they're leaving
|
||||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
||||||
@ -213,11 +213,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
var statsSvc = ContextThreads[serverId];
|
var statsSvc = ContextThreads[serverId];
|
||||||
Vector3 vDeathOrigin = null;
|
Vector3 vDeathOrigin = null;
|
||||||
Vector3 vKillOrigin = null;
|
Vector3 vKillOrigin = null;
|
||||||
|
Vector3 vViewAngles = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
vDeathOrigin = Vector3.Parse(deathOrigin);
|
vDeathOrigin = Vector3.Parse(deathOrigin);
|
||||||
vKillOrigin = Vector3.Parse(killOrigin);
|
vKillOrigin = Vector3.Parse(killOrigin);
|
||||||
|
vViewAngles = Vector3.Parse(viewAngles).FixIW4Angles();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (FormatException)
|
catch (FormatException)
|
||||||
@ -241,7 +243,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
Damage = Int32.Parse(damage),
|
Damage = Int32.Parse(damage),
|
||||||
HitLoc = ParseEnum<IW4Info.HitLocation>.Get(hitLoc, typeof(IW4Info.HitLocation)),
|
HitLoc = ParseEnum<IW4Info.HitLocation>.Get(hitLoc, typeof(IW4Info.HitLocation)),
|
||||||
Weapon = ParseEnum<IW4Info.WeaponName>.Get(weapon, typeof(IW4Info.WeaponName)),
|
Weapon = ParseEnum<IW4Info.WeaponName>.Get(weapon, typeof(IW4Info.WeaponName)),
|
||||||
ViewAngles = Vector3.Parse(viewAngles).FixIW4Angles(),
|
ViewAngles = vViewAngles,
|
||||||
TimeOffset = Int64.Parse(offset),
|
TimeOffset = Int64.Parse(offset),
|
||||||
When = DateTime.UtcNow,
|
When = DateTime.UtcNow,
|
||||||
IsKillstreakKill = isKillstreakKill[0] != '0',
|
IsKillstreakKill = isKillstreakKill[0] != '0',
|
||||||
@ -338,11 +340,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// update the total stats
|
// update the total stats
|
||||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||||
|
|
||||||
attackerStats.SessionScore = attacker.Score;
|
attackerStats.SessionScore += (attacker.Score - attackerStats.LastScore);
|
||||||
victimStats.SessionScore = victim.Score;
|
victimStats.SessionScore += (victim.Score - victimStats.LastScore);
|
||||||
|
|
||||||
// calculate for the clients
|
// calculate for the clients
|
||||||
CalculateKill(attackerStats, victimStats);
|
CalculateKill(attackerStats, victimStats);
|
||||||
|
// this should fix the negative SPM
|
||||||
|
attackerStats.LastScore = attacker.Score;
|
||||||
|
victimStats.LastScore = victim.Score;
|
||||||
|
|
||||||
// show encouragement/discouragement
|
// show encouragement/discouragement
|
||||||
string streakMessage = (attackerStats.ClientId != victimStats.ClientId) ?
|
string streakMessage = (attackerStats.ClientId != victimStats.ClientId) ?
|
||||||
@ -457,7 +462,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
}
|
}
|
||||||
|
|
||||||
clientStats.LastStatCalculation = DateTime.UtcNow;
|
clientStats.LastStatCalculation = DateTime.UtcNow;
|
||||||
clientStats.LastScore = clientStats.SessionScore;
|
//clientStats.LastScore = clientStats.SessionScore;
|
||||||
|
|
||||||
return clientStats;
|
return clientStats;
|
||||||
}
|
}
|
||||||
|
@ -253,7 +253,7 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
Manager = new StatManager(manager);
|
Manager = new StatManager(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
|
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||||
|
|
||||||
public async Task OnUnloadAsync()
|
public async Task OnUnloadAsync()
|
||||||
{
|
{
|
||||||
|
@ -51,8 +51,9 @@ namespace IW4MAdmin.Plugins
|
|||||||
|
|
||||||
public Task OnLoadAsync(IManager manager) => Task.CompletedTask;
|
public Task OnLoadAsync(IManager manager) => Task.CompletedTask;
|
||||||
|
|
||||||
public async Task OnTickAsync(Server S)
|
public Task OnTickAsync(Server S)
|
||||||
{
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
/*
|
/*
|
||||||
if ((DateTime.Now - Interval).TotalSeconds > 1)
|
if ((DateTime.Now - Interval).TotalSeconds > 1)
|
||||||
{
|
{
|
||||||
|
@ -72,9 +72,9 @@ namespace IW4MAdmin.Plugins.Welcome
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task OnUnloadAsync() => Utilities.CompletedTask;
|
public Task OnUnloadAsync() => Task.CompletedTask;
|
||||||
|
|
||||||
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
|
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||||
|
|
||||||
public async Task OnEventAsync(GameEvent E, Server S)
|
public async Task OnEventAsync(GameEvent E, Server S)
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent E)
|
public override async Task ExecuteAsync(GameEvent E)
|
||||||
{
|
{
|
||||||
if ((await (E.Owner.Manager.GetClientService() as Services.ClientService).GetOwners()).Count == 0)
|
if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
|
||||||
{
|
{
|
||||||
E.Origin.Level = Player.Permission.Owner;
|
E.Origin.Level = Player.Permission.Owner;
|
||||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_OWNER_SUCCESS"]);
|
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_OWNER_SUCCESS"]);
|
||||||
@ -94,7 +94,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
public class CKick : Command
|
public class CKick : Command
|
||||||
{
|
{
|
||||||
public CKick() :
|
public CKick() :
|
||||||
base("kick", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_DESC"], "k", Player.Permission.Trusted, true, new CommandArgument[]
|
base("kick", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_DESC"], "k", Player.Permission.Moderator, true, new CommandArgument[]
|
||||||
{
|
{
|
||||||
new CommandArgument()
|
new CommandArgument()
|
||||||
{
|
{
|
||||||
@ -113,7 +113,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
{
|
{
|
||||||
if (E.Origin.Level > E.Target.Level)
|
if (E.Origin.Level > E.Target.Level)
|
||||||
{
|
{
|
||||||
await E.Owner.ExecuteEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
|
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
|
||||||
await E.Target.Kick(E.Data, E.Origin);
|
await E.Target.Kick(E.Data, E.Origin);
|
||||||
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_SUCCESS"]}");
|
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_SUCCESS"]}");
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
public class CTempBan : Command
|
public class CTempBan : Command
|
||||||
{
|
{
|
||||||
public CTempBan() :
|
public CTempBan() :
|
||||||
base("tempban", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_TEMPBAN_DESC"], "tb", Player.Permission.Moderator, true, new CommandArgument[]
|
base("tempban", Utilities.CurrentLocalization.LocalizationSet["COMMANDS_TEMPBAN_DESC"], "tb", Player.Permission.Administrator, true, new CommandArgument[]
|
||||||
{
|
{
|
||||||
new CommandArgument()
|
new CommandArgument()
|
||||||
{
|
{
|
||||||
@ -716,7 +716,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
};
|
};
|
||||||
|
|
||||||
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
|
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
|
||||||
await E.Owner.ExecuteEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
|
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
|
||||||
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
|
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,7 +770,7 @@ namespace SharedLibraryCore.Commands
|
|||||||
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
|
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
|
||||||
|
|
||||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_REPORT_SUCCESS"]);
|
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_REPORT_SUCCESS"]);
|
||||||
await E.Owner.ExecuteEvent(new GameEvent(GameEvent.EventType.Report, E.Data, E.Origin, E.Target, E.Owner));
|
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Report, E.Data, E.Origin, E.Target, E.Owner));
|
||||||
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
|
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1023,7 +1023,8 @@ namespace SharedLibraryCore.Commands
|
|||||||
E.Origin.PasswordSalt = hashedPassword[1];
|
E.Origin.PasswordSalt = hashedPassword[1];
|
||||||
|
|
||||||
// update the password for the client in privileged
|
// update the password for the client in privileged
|
||||||
E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId] = E.Origin;
|
E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].Password = hashedPassword[0];
|
||||||
|
E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].PasswordSalt = hashedPassword[1];
|
||||||
|
|
||||||
await E.Owner.Manager.GetClientService().Update(E.Origin);
|
await E.Owner.Manager.GetClientService().Update(E.Origin);
|
||||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_PASSWORD_SUCCESS"]);
|
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_PASSWORD_SUCCESS"]);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
using SharedLibraryCore.Objects;
|
using SharedLibraryCore.Objects;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
@ -47,10 +47,13 @@ namespace SharedLibraryCore
|
|||||||
Origin = O;
|
Origin = O;
|
||||||
Target = T;
|
Target = T;
|
||||||
Owner = S;
|
Owner = S;
|
||||||
|
OnProcessed = new ManualResetEventSlim();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameEvent() { }
|
public GameEvent()
|
||||||
|
{
|
||||||
|
OnProcessed = new ManualResetEventSlim();
|
||||||
|
}
|
||||||
|
|
||||||
public EventType Type;
|
public EventType Type;
|
||||||
public string Data; // Data is usually the message sent by player
|
public string Data; // Data is usually the message sent by player
|
||||||
@ -60,5 +63,6 @@ namespace SharedLibraryCore
|
|||||||
public Server Owner;
|
public Server Owner;
|
||||||
public Boolean Remote = false;
|
public Boolean Remote = false;
|
||||||
public object Extra { get; set; }
|
public object Extra { get; set; }
|
||||||
|
public ManualResetEventSlim OnProcessed { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.IO;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace SharedLibraryCore
|
namespace SharedLibraryCore
|
||||||
{
|
{
|
||||||
@ -27,7 +28,12 @@ namespace SharedLibraryCore
|
|||||||
public override long Length()
|
public override long Length()
|
||||||
{
|
{
|
||||||
Retrieve();
|
Retrieve();
|
||||||
return FileCache[0].Length;
|
return FileCache.Sum(l => l.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Task<string[]> Tail(int lineCount)
|
||||||
|
{
|
||||||
|
return Task.FromResult(FileCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
28
SharedLibraryCore/Interfaces/IEventHandler.cs
Normal file
28
SharedLibraryCore/Interfaces/IEventHandler.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Interfaces
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This class handle games events (from log, manual events, etc)
|
||||||
|
/// </summary>
|
||||||
|
public interface IEventHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add a game event event to the queue to be processed
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameEvent">Game event</param>
|
||||||
|
void AddEvent(GameEvent gameEvent);
|
||||||
|
/// <summary>
|
||||||
|
/// Get the next event to be processed
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Game event that needs to be processed</returns>
|
||||||
|
GameEvent GetNextEvent();
|
||||||
|
/// <summary>
|
||||||
|
/// If an event has output. Like executing a command wait until it's available
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of output strings</returns>
|
||||||
|
string[] GetEventOutput();
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <param name="server">server the event occurred on</param>
|
/// <param name="server">server the event occurred on</param>
|
||||||
/// <param name="logLine">single log line string</param>
|
/// <param name="logLine">single log line string</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
/// todo: make this integrate without needing the server
|
||||||
GameEvent GetEvent(Server server, string logLine);
|
GameEvent GetEvent(Server server, string logLine);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get game specific folder prefix for log files
|
/// Get game specific folder prefix for log files
|
||||||
|
@ -23,6 +23,15 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
PenaltyService GetPenaltyService();
|
PenaltyService GetPenaltyService();
|
||||||
IDictionary<int, Player> GetPrivilegedClients();
|
IDictionary<int, Player> GetPrivilegedClients();
|
||||||
IEventApi GetEventApi();
|
IEventApi GetEventApi();
|
||||||
|
/// <summary>
|
||||||
|
/// Get the event handlers
|
||||||
|
/// </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();
|
bool ShutdownRequested();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,7 @@ namespace SharedLibraryCore
|
|||||||
PlayerHistory = new Queue<PlayerHistory>();
|
PlayerHistory = new Queue<PlayerHistory>();
|
||||||
ChatHistory = new List<ChatInfo>();
|
ChatHistory = new List<ChatInfo>();
|
||||||
NextMessage = 0;
|
NextMessage = 0;
|
||||||
|
OnEvent = new ManualResetEventSlim();
|
||||||
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
|
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
|
||||||
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
|
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
|
||||||
InitializeTokens();
|
InitializeTokens();
|
||||||
@ -132,7 +133,7 @@ namespace SharedLibraryCore
|
|||||||
await this.ExecuteCommandAsync(formattedMessage);
|
await this.ExecuteCommandAsync(formattedMessage);
|
||||||
#else
|
#else
|
||||||
Logger.WriteVerbose(Message.StripColors());
|
Logger.WriteVerbose(Message.StripColors());
|
||||||
await Utilities.CompletedTask;
|
await Task.CompletedTask;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +151,7 @@ namespace SharedLibraryCore
|
|||||||
await this.ExecuteCommandAsync(formattedMessage);
|
await this.ExecuteCommandAsync(formattedMessage);
|
||||||
#else
|
#else
|
||||||
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
|
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
|
||||||
await Utilities.CompletedTask;
|
await Task.CompletedTask;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (Target.Level == Player.Permission.Console)
|
if (Target.Level == Player.Permission.Console)
|
||||||
@ -308,6 +309,7 @@ namespace SharedLibraryCore
|
|||||||
public RCon.Connection RemoteConnection { get; protected set; }
|
public RCon.Connection RemoteConnection { get; protected set; }
|
||||||
public IRConParser RconParser { get; protected set; }
|
public IRConParser RconParser { get; protected set; }
|
||||||
public IEventParser EventParser { get; set; }
|
public IEventParser EventParser { get; set; }
|
||||||
|
public ManualResetEventSlim OnEvent { get; private set; }
|
||||||
|
|
||||||
// Internal
|
// Internal
|
||||||
protected string IP;
|
protected string IP;
|
||||||
|
@ -16,7 +16,6 @@ namespace SharedLibraryCore
|
|||||||
public static class Utilities
|
public static class Utilities
|
||||||
{
|
{
|
||||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||||
public static readonly Task CompletedTask = Task.FromResult(false);
|
|
||||||
public static Encoding EncodingType;
|
public static Encoding EncodingType;
|
||||||
public static Localization.Layout CurrentLocalization;
|
public static Localization.Layout CurrentLocalization;
|
||||||
|
|
||||||
|
@ -16,24 +16,37 @@ namespace WebfrontCore.Controllers
|
|||||||
protected IManager Manager;
|
protected IManager Manager;
|
||||||
protected readonly DatabaseContext Context;
|
protected readonly DatabaseContext Context;
|
||||||
protected bool Authorized { get; private set; }
|
protected bool Authorized { get; private set; }
|
||||||
protected EFClient User { get; private set; }
|
protected EFClient Client { get; private set; }
|
||||||
|
private static byte[] LocalHost = { 127, 0, 0, 1 };
|
||||||
|
private static string DiscordLink;
|
||||||
|
|
||||||
|
public BaseController()
|
||||||
|
{
|
||||||
|
var Manager = Program.Manager;
|
||||||
|
if (Manager.GetApplicationSettings().Configuration().EnableDiscordLink)
|
||||||
|
{
|
||||||
|
string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
|
||||||
|
if (inviteLink != null)
|
||||||
|
DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
|
||||||
|
else
|
||||||
|
DiscordLink = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void OnActionExecuting(ActionExecutingContext context)
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
{
|
{
|
||||||
Manager = Program.Manager;
|
Client = Client ?? new EFClient()
|
||||||
|
|
||||||
User = User ?? new EFClient()
|
|
||||||
{
|
{
|
||||||
ClientId = -1
|
ClientId = -1
|
||||||
};
|
};
|
||||||
|
|
||||||
if (HttpContext.Connection.RemoteIpAddress.ToString() != "127.0.0.1")
|
if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
User.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
|
Client.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
|
||||||
User.Level = (Player.Permission)Enum.Parse(typeof(Player.Permission), base.User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
|
Client.Level = (Player.Permission)Enum.Parse(typeof(Player.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
|
||||||
User.CurrentAlias = new EFAlias() { Name = base.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (InvalidOperationException)
|
catch (InvalidOperationException)
|
||||||
@ -44,24 +57,17 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
User.ClientId = 1;
|
Client.ClientId = 1;
|
||||||
User.Level = Player.Permission.Console;
|
Client.Level = Player.Permission.Console;
|
||||||
User.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
|
Client.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
|
||||||
}
|
}
|
||||||
|
|
||||||
Authorized = User.ClientId >= 0;
|
Authorized = Client.ClientId >= 0;
|
||||||
ViewBag.Authorized = Authorized;
|
ViewBag.Authorized = Authorized;
|
||||||
ViewBag.Url = Startup.Configuration["Web:Address"];
|
ViewBag.Url = Startup.Configuration["Web:Address"];
|
||||||
ViewBag.User = User;
|
ViewBag.User = Client;
|
||||||
|
ViewBag.DiscordLink = DiscordLink;
|
||||||
|
|
||||||
if (Manager.GetApplicationSettings().Configuration().EnableDiscordLink)
|
|
||||||
{
|
|
||||||
string inviteLink = Manager.GetApplicationSettings().Configuration().DiscordInviteCode;
|
|
||||||
if (inviteLink != null)
|
|
||||||
ViewBag.DiscordLink = inviteLink.Contains("https") ? inviteLink : $"https://discordapp.com/invite/{inviteLink}";
|
|
||||||
else
|
|
||||||
ViewBag.DiscordLink = "";
|
|
||||||
}
|
|
||||||
base.OnActionExecuting(context);
|
base.OnActionExecuting(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,10 @@ namespace WebfrontCore.Controllers
|
|||||||
var server = Manager.GetServers().First(s => s.GetHashCode() == serverId);
|
var server = Manager.GetServers().First(s => s.GetHashCode() == serverId);
|
||||||
var client = new Player()
|
var client = new Player()
|
||||||
{
|
{
|
||||||
ClientId = User.ClientId,
|
ClientId = Client.ClientId,
|
||||||
Level = User.Level,
|
Level = Client.Level,
|
||||||
CurrentServer = server,
|
CurrentServer = server,
|
||||||
CurrentAlias = new Alias() { Name = User.Name }
|
CurrentAlias = new Alias() { Name = Client.Name }
|
||||||
};
|
};
|
||||||
var remoteEvent = new GameEvent()
|
var remoteEvent = new GameEvent()
|
||||||
{
|
{
|
||||||
@ -46,8 +46,9 @@ namespace WebfrontCore.Controllers
|
|||||||
Remote = true
|
Remote = true
|
||||||
};
|
};
|
||||||
|
|
||||||
await server.ExecuteEvent(remoteEvent);
|
Manager.GetEventHandler().AddEvent(remoteEvent);
|
||||||
|
// wait for the event to process
|
||||||
|
remoteEvent.OnProcessed.Wait();
|
||||||
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
|
var response = server.CommandResult.Where(c => c.ClientId == client.ClientId).ToList();
|
||||||
|
|
||||||
// remove the added command response
|
// remove the added command response
|
||||||
|
@ -33,7 +33,6 @@ a.nav-link {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.server-history,
|
.server-history,
|
||||||
.server-header,
|
|
||||||
.server-activity,
|
.server-activity,
|
||||||
#mobile_seperator,
|
#mobile_seperator,
|
||||||
.border-bottom {
|
.border-bottom {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user