moved event API stuff around
finally fixed threading issue (which actually had to do with IW4x log outputs being out of sync (not an issue with my code). What a lot of headache over something that wasn't my fault.
This commit is contained in:
parent
bbefd53db4
commit
46bdc2ac33
@ -5,7 +5,7 @@
|
|||||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||||
<Version>2.1.5</Version>
|
<Version>2.1.6</Version>
|
||||||
<Authors>RaidMax</Authors>
|
<Authors>RaidMax</Authors>
|
||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Product>IW4MAdmin</Product>
|
<Product>IW4MAdmin</Product>
|
||||||
|
@ -1,27 +1,72 @@
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Events;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application
|
namespace IW4MAdmin.Application
|
||||||
{
|
{
|
||||||
class GameEventHandler : IEventHandler
|
class GameEventHandler : IEventHandler
|
||||||
{
|
{
|
||||||
private readonly IManager Manager;
|
private readonly IManager Manager;
|
||||||
|
static long NextEventId = 1;
|
||||||
|
private readonly SortedList<long, GameEvent> OutOfOrderEvents;
|
||||||
|
private readonly SemaphoreSlim ProcessingEvent;
|
||||||
|
|
||||||
public GameEventHandler(IManager mgr)
|
public GameEventHandler(IManager mgr)
|
||||||
{
|
{
|
||||||
Manager = mgr;
|
Manager = mgr;
|
||||||
|
OutOfOrderEvents = new SortedList<long, GameEvent>();
|
||||||
|
ProcessingEvent = new SemaphoreSlim(0);
|
||||||
|
ProcessingEvent.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddEvent(GameEvent gameEvent)
|
public bool AddEvent(GameEvent gameEvent)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner}");
|
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
|
||||||
#endif
|
#endif
|
||||||
// todo: later
|
while (OutOfOrderEvents.Values.FirstOrDefault()?.Id == Interlocked.Read(ref NextEventId))
|
||||||
((Manager as ApplicationManager).OnServerEvent)(this, new ApplicationManager.GameEventArgs(null, false, gameEvent));
|
{
|
||||||
|
lock (OutOfOrderEvents)
|
||||||
|
{
|
||||||
|
var fixedEvent = OutOfOrderEvents.Values[0];
|
||||||
|
OutOfOrderEvents.RemoveAt(0);
|
||||||
|
AddEvent(fixedEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
|
||||||
|
// event occurs
|
||||||
|
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
|
||||||
|
{
|
||||||
|
Manager.GetLogger().WriteDebug($"Starting to wait for event with id {gameEvent.Id}");
|
||||||
|
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
|
||||||
|
Manager.GetLogger().WriteDebug($"Finished waiting for event with id {gameEvent.Id}");
|
||||||
|
Interlocked.Increment(ref NextEventId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a "newer" event has been added before and "older" one has been added (due to threads and context switching)
|
||||||
|
// so me must wait until the next expected one arrives
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Manager.GetLogger().WriteWarning("Incoming event is out of order");
|
||||||
|
Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
|
||||||
|
|
||||||
|
// this prevents multiple threads from adding simultaneously
|
||||||
|
lock (OutOfOrderEvents)
|
||||||
|
{
|
||||||
|
if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
|
||||||
|
{
|
||||||
|
OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.IO
|
namespace IW4MAdmin.Application.IO
|
||||||
@ -31,44 +33,36 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
Reader = new GameLogReader(gameLogPath, server.EventParser);
|
Reader = new GameLogReader(gameLogPath, server.EventParser);
|
||||||
}
|
}
|
||||||
|
|
||||||
Server = server;
|
Server = server;
|
||||||
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
while (!server.Manager.ShutdownRequested())
|
|
||||||
{
|
|
||||||
if ((server.Manager as ApplicationManager).IsInitialized)
|
|
||||||
{
|
|
||||||
OnEvent(new EventState()
|
|
||||||
{
|
|
||||||
Log = server.Manager.GetLogger(),
|
|
||||||
ServerId = server.ToString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await Task.Delay(100);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEvent(object state)
|
public void PollForChanges()
|
||||||
{
|
{
|
||||||
long newLength = Reader.Length;
|
while (!Server.Manager.ShutdownRequested())
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
UpdateLogEvents(newLength);
|
if ((Server.Manager as ApplicationManager).IsInitialized)
|
||||||
}
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
UpdateLogEvents();
|
||||||
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
((EventState)state).Log.WriteWarning($"Failed to update log event for {((EventState)state).ServerId}");
|
Server.Logger.WriteWarning($"Failed to update log event for {Server.GetHashCode()}");
|
||||||
((EventState)state).Log.WriteDebug($"Exception: {e.Message}");
|
Server.Logger.WriteDebug($"Exception: {e.Message}");
|
||||||
((EventState)state).Log.WriteDebug($"StackTrace: {e.StackTrace}");
|
Server.Logger.WriteDebug($"StackTrace: {e.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Thread.Sleep(Reader.UpdateInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLogEvents(long fileSize)
|
private void UpdateLogEvents()
|
||||||
{
|
{
|
||||||
|
long fileSize = Reader.Length;
|
||||||
|
|
||||||
if (PreviousFileSize == 0)
|
if (PreviousFileSize == 0)
|
||||||
PreviousFileSize = fileSize;
|
PreviousFileSize = fileSize;
|
||||||
|
|
||||||
@ -79,9 +73,12 @@ namespace IW4MAdmin.Application.IO
|
|||||||
|
|
||||||
PreviousFileSize = fileSize;
|
PreviousFileSize = fileSize;
|
||||||
|
|
||||||
var events = Reader.EventsFromLog(Server, fileDiff, 0);
|
var events = Reader.ReadEventsFromLog(Server, fileDiff, 0);
|
||||||
|
|
||||||
foreach (var ev in events)
|
foreach (var ev in events)
|
||||||
|
{
|
||||||
Server.Manager.GetEventHandler().AddEvent(ev);
|
Server.Manager.GetEventHandler().AddEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
PreviousFileSize = fileSize;
|
PreviousFileSize = fileSize;
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
|
|
||||||
public long Length => new FileInfo(LogFile).Length;
|
public long Length => new FileInfo(LogFile).Length;
|
||||||
|
|
||||||
public int UpdateInterval => 100;
|
public int UpdateInterval => 300;
|
||||||
|
|
||||||
public GameLogReader(string logFile, IEventParser parser)
|
public GameLogReader(string logFile, IEventParser parser)
|
||||||
{
|
{
|
||||||
@ -22,7 +22,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
Parser = parser;
|
Parser = parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ICollection<GameEvent> EventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||||
{
|
{
|
||||||
// allocate the bytes for the new log lines
|
// allocate the bytes for the new log lines
|
||||||
List<string> logLines = new List<string>();
|
List<string> logLines = new List<string>();
|
||||||
|
@ -29,11 +29,12 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
using (var cl = new HttpClient())
|
using (var cl = new HttpClient())
|
||||||
{
|
{
|
||||||
using (var re = cl.GetAsync($"{LogFile}?length=1").Result)
|
using (var re = cl.GetAsync($"{LogFile}&length=1").Result)
|
||||||
{
|
{
|
||||||
using (var content = re.Content)
|
using (var content = re.Content)
|
||||||
{
|
{
|
||||||
return Convert.ToInt64(content.ReadAsStringAsync().Result ?? "0");
|
string response = content.ReadAsStringAsync().Result ?? "0";
|
||||||
|
return Convert.ToInt64(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,12 +43,15 @@ namespace IW4MAdmin.Application.IO
|
|||||||
|
|
||||||
public int UpdateInterval => 1000;
|
public int UpdateInterval => 1000;
|
||||||
|
|
||||||
public ICollection<GameEvent> EventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
public ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition)
|
||||||
{
|
{
|
||||||
|
#if DEBUG == true
|
||||||
|
server.Logger.WriteDebug($"Begin reading {fileSizeDiff} from http log");
|
||||||
|
#endif
|
||||||
string log;
|
string log;
|
||||||
using (var cl = new HttpClient())
|
using (var cl = new HttpClient())
|
||||||
{
|
{
|
||||||
using (var re = cl.GetAsync($"{LogFile}?start={fileSizeDiff}").Result)
|
using (var re = cl.GetAsync($"{LogFile}&start={fileSizeDiff}").Result)
|
||||||
{
|
{
|
||||||
using (var content = re.Content)
|
using (var content = re.Content)
|
||||||
{
|
{
|
||||||
@ -55,18 +59,29 @@ namespace IW4MAdmin.Application.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if DEBUG == true
|
||||||
|
server.Logger.WriteDebug($"retrieved events from http log");
|
||||||
|
#endif
|
||||||
List<GameEvent> events = new List<GameEvent>();
|
List<GameEvent> events = new List<GameEvent>();
|
||||||
|
string[] lines = log.Split(Environment.NewLine);
|
||||||
|
|
||||||
|
#if DEBUG == true
|
||||||
|
server.Logger.WriteDebug($"Begin parse of {lines.Length} lines from http log");
|
||||||
|
#endif
|
||||||
|
|
||||||
// parse each line
|
// parse each line
|
||||||
foreach (string eventLine in log.Split(Environment.NewLine))
|
foreach (string eventLine in lines)
|
||||||
{
|
{
|
||||||
if (eventLine.Length > 0)
|
if (eventLine.Length > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// todo: catch elsewhere
|
// todo: catch elsewhere
|
||||||
events.Add(Parser.GetEvent(server, eventLine));
|
var e = Parser.GetEvent(server, eventLine);
|
||||||
|
#if DEBUG == true
|
||||||
|
server.Logger.WriteDebug($"Parsed event with id {e.Id} from http");
|
||||||
|
#endif
|
||||||
|
events.Add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -106,7 +106,7 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
ServerManager.Init().Wait();
|
ServerManager.Init().Wait();
|
||||||
|
|
||||||
var consoleTask = Task.Run(() =>
|
var consoleTask = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
String userInput;
|
String userInput;
|
||||||
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
|
Player Origin = ServerManager.GetClientService().Get(1).Result.AsPlayer();
|
||||||
@ -136,7 +136,7 @@ namespace IW4MAdmin.Application
|
|||||||
};
|
};
|
||||||
|
|
||||||
ServerManager.GetEventHandler().AddEvent(E);
|
ServerManager.GetEventHandler().AddEvent(E);
|
||||||
E.OnProcessed.Wait(5000);
|
await E.OnProcessed.WaitAsync(30 * 1000);
|
||||||
}
|
}
|
||||||
Console.Write('>');
|
Console.Write('>');
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ using System.Text;
|
|||||||
using IW4MAdmin.Application.API.Master;
|
using IW4MAdmin.Application.API.Master;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using SharedLibraryCore.Database;
|
using SharedLibraryCore.Database;
|
||||||
|
using SharedLibraryCore.Events;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application
|
namespace IW4MAdmin.Application
|
||||||
{
|
{
|
||||||
@ -33,7 +34,6 @@ namespace IW4MAdmin.Application
|
|||||||
public ILogger Logger { get; private set; }
|
public ILogger Logger { get; private set; }
|
||||||
public bool Running { get; private set; }
|
public bool Running { get; private set; }
|
||||||
public bool IsInitialized { get; private set; }
|
public bool IsInitialized { get; private set; }
|
||||||
//public EventHandler<GameEvent> ServerEventOccurred { get; private set; }
|
|
||||||
// define what the delagate function looks like
|
// define what the delagate function looks like
|
||||||
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
|
public delegate void OnServerEventEventHandler(object sender, GameEventArgs e);
|
||||||
// expose the event handler so we can execute the events
|
// expose the event handler so we can execute the events
|
||||||
@ -48,22 +48,10 @@ namespace IW4MAdmin.Application
|
|||||||
readonly AliasService AliasSvc;
|
readonly AliasService AliasSvc;
|
||||||
readonly PenaltyService PenaltySvc;
|
readonly PenaltyService PenaltySvc;
|
||||||
public BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
public BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||||
EventApi Api;
|
|
||||||
GameEventHandler Handler;
|
GameEventHandler Handler;
|
||||||
ManualResetEventSlim OnQuit;
|
ManualResetEventSlim OnQuit;
|
||||||
readonly IPageList PageList;
|
readonly IPageList PageList;
|
||||||
|
|
||||||
public class GameEventArgs : System.ComponentModel.AsyncCompletedEventArgs
|
|
||||||
{
|
|
||||||
|
|
||||||
public GameEventArgs(Exception error, bool cancelled, GameEvent userState) : base(error, cancelled, userState)
|
|
||||||
{
|
|
||||||
Event = userState;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameEvent Event { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private ApplicationManager()
|
private ApplicationManager()
|
||||||
{
|
{
|
||||||
Logger = new Logger($@"{Utilities.OperatingDirectory}IW4MAdmin.log");
|
Logger = new Logger($@"{Utilities.OperatingDirectory}IW4MAdmin.log");
|
||||||
@ -75,13 +63,12 @@ namespace IW4MAdmin.Application
|
|||||||
AliasSvc = new AliasService();
|
AliasSvc = new AliasService();
|
||||||
PenaltySvc = new PenaltyService();
|
PenaltySvc = new PenaltyService();
|
||||||
PrivilegedClients = new Dictionary<int, Player>();
|
PrivilegedClients = new Dictionary<int, Player>();
|
||||||
Api = new EventApi();
|
|
||||||
//ServerEventOccurred += Api.OnServerEvent;
|
|
||||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||||
StartTime = DateTime.UtcNow;
|
StartTime = DateTime.UtcNow;
|
||||||
OnQuit = new ManualResetEventSlim();
|
OnQuit = new ManualResetEventSlim();
|
||||||
PageList = new PageList();
|
PageList = new PageList();
|
||||||
OnServerEvent += OnServerEventAsync;
|
OnServerEvent += OnServerEventAsync;
|
||||||
|
OnServerEvent += EventApi.OnGameEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnServerEventAsync(object sender, GameEventArgs args)
|
private async void OnServerEventAsync(object sender, GameEventArgs args)
|
||||||
@ -108,7 +95,7 @@ namespace IW4MAdmin.Application
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await newEvent.Owner.ExecuteEvent(newEvent);
|
|
||||||
|
|
||||||
//// todo: this is a hacky mess
|
//// todo: this is a hacky mess
|
||||||
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
|
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
|
||||||
@ -119,7 +106,20 @@ namespace IW4MAdmin.Application
|
|||||||
// add the delayed event to the queue
|
// add the delayed event to the queue
|
||||||
while(events.Count > 0)
|
while(events.Count > 0)
|
||||||
{
|
{
|
||||||
var e = events.Dequeue();
|
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,
|
||||||
|
OnProcessed = oldEvent.OnProcessed,
|
||||||
|
Remote = oldEvent.Remote
|
||||||
|
};
|
||||||
|
|
||||||
e.Origin = newEvent.Origin;
|
e.Origin = newEvent.Origin;
|
||||||
// check if the target was assigned
|
// check if the target was assigned
|
||||||
@ -140,9 +140,10 @@ namespace IW4MAdmin.Application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Api.OnServerEvent(this, newEvent);
|
await newEvent.Owner.ExecuteEvent(newEvent);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
Logger.WriteDebug("Processed Event");
|
Logger.WriteDebug($"Processed event with id {newEvent.Id}");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +170,7 @@ namespace IW4MAdmin.Application
|
|||||||
Logger.WriteDebug("Error Trace: " + ex.StackTrace);
|
Logger.WriteDebug("Error Trace: " + ex.StackTrace);
|
||||||
}
|
}
|
||||||
// tell anyone waiting for the output that we're done
|
// tell anyone waiting for the output that we're done
|
||||||
newEvent.OnProcessed.Set();
|
newEvent.OnProcessed.Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IList<Server> GetServers()
|
public IList<Server> GetServers()
|
||||||
@ -423,7 +424,15 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationIndex["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
||||||
// add the start event for this server
|
// add the start event for this server
|
||||||
Handler.AddEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, ServerInstance));
|
|
||||||
|
var e = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Start,
|
||||||
|
Data = $"{ServerInstance.GameName} started",
|
||||||
|
Owner = ServerInstance
|
||||||
|
};
|
||||||
|
|
||||||
|
Handler.AddEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (ServerException e)
|
catch (ServerException e)
|
||||||
@ -562,7 +571,6 @@ namespace IW4MAdmin.Application
|
|||||||
public PenaltyService GetPenaltyService() => PenaltySvc;
|
public PenaltyService GetPenaltyService() => PenaltySvc;
|
||||||
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings() => ConfigHandler;
|
public IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings() => ConfigHandler;
|
||||||
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
||||||
public IEventApi GetEventApi() => Api;
|
|
||||||
public bool ShutdownRequested() => !Running;
|
public bool ShutdownRequested() => !Running;
|
||||||
public IEventHandler GetEventHandler() => Handler;
|
public IEventHandler GetEventHandler() => Handler;
|
||||||
|
|
||||||
|
@ -27,11 +27,9 @@ namespace IW4MAdmin
|
|||||||
{
|
{
|
||||||
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
|
private static readonly Index loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||||
private GameLogEventDetection LogEvent;
|
private GameLogEventDetection LogEvent;
|
||||||
private ClientAuthentication AuthQueue;
|
|
||||||
|
|
||||||
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
|
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
|
||||||
{
|
{
|
||||||
AuthQueue = new ClientAuthentication();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
@ -603,7 +601,7 @@ namespace IW4MAdmin
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var polledClients = await PollPlayersAsync();
|
var polledClients = await PollPlayersAsync();
|
||||||
var waiterList = new List<ManualResetEventSlim>();
|
var waiterList = new List<SemaphoreSlim>();
|
||||||
|
|
||||||
foreach (var disconnectingClient in polledClients[1])
|
foreach (var disconnectingClient in polledClients[1])
|
||||||
{
|
{
|
||||||
@ -625,7 +623,7 @@ namespace IW4MAdmin
|
|||||||
waiterList.Add(e.OnProcessed);
|
waiterList.Add(e.OnProcessed);
|
||||||
}
|
}
|
||||||
// wait for all the disconnect tasks to finish
|
// wait for all the disconnect tasks to finish
|
||||||
await Task.WhenAll(waiterList.Select(t => Task.Run(() => t.Wait(5000))));
|
await Task.WhenAll(waiterList.Select(t => t.WaitAsync()));
|
||||||
|
|
||||||
waiterList.Clear();
|
waiterList.Clear();
|
||||||
// this are our new connecting clients
|
// this are our new connecting clients
|
||||||
@ -643,7 +641,7 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// wait for all the connect tasks to finish
|
// wait for all the connect tasks to finish
|
||||||
await Task.WhenAll(waiterList.Select(t => Task.Run(() => t.Wait(5000))));
|
await Task.WhenAll(waiterList.Select(t => t.WaitAsync()));
|
||||||
|
|
||||||
if (ConnectionErrors > 0)
|
if (ConnectionErrors > 0)
|
||||||
{
|
{
|
||||||
@ -835,7 +833,7 @@ namespace IW4MAdmin
|
|||||||
logPath = Regex.Replace($"{Path.DirectorySeparatorChar}{logPath}", @"[A-Z]:", "");
|
logPath = Regex.Replace($"{Path.DirectorySeparatorChar}{logPath}", @"[A-Z]:", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(logPath))
|
if (!File.Exists(logPath) && !logPath.StartsWith("http"))
|
||||||
{
|
{
|
||||||
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
|
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
@ -850,6 +848,8 @@ namespace IW4MAdmin
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger.WriteInfo($"Log file is {logPath}");
|
Logger.WriteInfo($"Log file is {logPath}");
|
||||||
|
|
||||||
|
Task.Run(() => LogEvent.PollForChanges());
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
await Broadcast(loc["BROADCAST_ONLINE"]);
|
await Broadcast(loc["BROADCAST_ONLINE"]);
|
||||||
#endif
|
#endif
|
||||||
@ -1002,15 +1002,17 @@ namespace IW4MAdmin
|
|||||||
// this is set only because they're still in the server.
|
// this is set only because they're still in the server.
|
||||||
Target.Level = Player.Permission.Banned;
|
Target.Level = Player.Permission.Banned;
|
||||||
|
|
||||||
// let the api know that a ban occured
|
var e = new GameEvent()
|
||||||
Manager.GetEventHandler().AddEvent(new GameEvent()
|
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.Ban,
|
Type = GameEvent.EventType.Ban,
|
||||||
Data = Message,
|
Data = Message,
|
||||||
Origin = Origin,
|
Origin = Origin,
|
||||||
Target = Target,
|
Target = Target,
|
||||||
Owner = this
|
Owner = this
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// let the api know that a ban occured
|
||||||
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
string formattedString = String.Format(RconParser.GetCommandPrefixes().Kick, Target.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{Message} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
|
string formattedString = String.Format(RconParser.GetCommandPrefixes().Kick, Target.ClientNumber, $"{loc["SERVER_BAN_TEXT"]} - ^5{Message} ^7({loc["SERVER_BAN_APPEAL"]} {Website})^7");
|
||||||
|
@ -10,7 +10,7 @@ const plugin = {
|
|||||||
checkForVpn(origin) {
|
checkForVpn(origin) {
|
||||||
let exempt = false;
|
let exempt = false;
|
||||||
// prevent players that are exempt from being kicked
|
// prevent players that are exempt from being kicked
|
||||||
this.vpnExceptionIds.forEach(function(id) {
|
this.vpnExceptionIds.forEach(function (id) {
|
||||||
if (id === origin.ClientId) {
|
if (id === origin.ClientId) {
|
||||||
exempt = true;
|
exempt = true;
|
||||||
return false;
|
return false;
|
||||||
@ -48,7 +48,7 @@ const plugin = {
|
|||||||
onEventAsync(gameEvent, server) {
|
onEventAsync(gameEvent, server) {
|
||||||
// connect event
|
// connect event
|
||||||
if (gameEvent.Type === 3) {
|
if (gameEvent.Type === 3) {
|
||||||
this.checkForVpn(gameEvent.Origin)
|
this.checkForVpn(gameEvent.Origin);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ const plugin = {
|
|||||||
this.logger = manager.GetLogger();
|
this.logger = manager.GetLogger();
|
||||||
},
|
},
|
||||||
|
|
||||||
onUnloadAsync() {},
|
onUnloadAsync() { },
|
||||||
|
|
||||||
onTickAsync(server) {}
|
onTickAsync(server) { }
|
||||||
}
|
};
|
@ -53,7 +53,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
if ((kill.DeathType != IW4Info.MeansOfDeath.MOD_PISTOL_BULLET &&
|
||||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
kill.DeathType != IW4Info.MeansOfDeath.MOD_RIFLE_BULLET &&
|
||||||
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
kill.DeathType != IW4Info.MeansOfDeath.MOD_HEAD_SHOT) ||
|
||||||
kill.HitLoc == IW4Info.HitLocation.none)
|
kill.HitLoc == IW4Info.HitLocation.none || kill.TimeOffset - LastOffset < 0)
|
||||||
return new DetectionPenaltyResult()
|
return new DetectionPenaltyResult()
|
||||||
{
|
{
|
||||||
ClientPenalty = Penalty.PenaltyType.Any,
|
ClientPenalty = Penalty.PenaltyType.Any,
|
||||||
@ -131,7 +131,6 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
|
|||||||
}
|
}
|
||||||
|
|
||||||
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
double currentStrain = Strain.GetStrain(isDamage, kill.Damage, kill.Distance / 0.0254, kill.ViewAngles, Math.Max(50, kill.TimeOffset - LastOffset));
|
||||||
//double currentWeightedStrain = (currentStrain * ClientStats.SPM) / 170.0;
|
|
||||||
LastOffset = kill.TimeOffset;
|
LastOffset = kill.TimeOffset;
|
||||||
|
|
||||||
if (currentStrain > ClientStats.MaxStrain)
|
if (currentStrain > ClientStats.MaxStrain)
|
||||||
|
@ -470,6 +470,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
Owner = attacker.CurrentServer,
|
Owner = attacker.CurrentServer,
|
||||||
Type = GameEvent.EventType.Flag
|
Type = GameEvent.EventType.Flag
|
||||||
};
|
};
|
||||||
|
// because we created an event it must be processed by the manager
|
||||||
|
// even if it didn't really do anything
|
||||||
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
await new CFlag().ExecuteAsync(e);
|
await new CFlag().ExecuteAsync(e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -834,9 +837,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
|||||||
// calculate how much the KDR should weigh
|
// calculate how much the KDR should weigh
|
||||||
// 1.637 is a Eddie-Generated number that weights the KDR nicely
|
// 1.637 is a Eddie-Generated number that weights the KDR nicely
|
||||||
double currentKDR = clientStats.SessionDeaths == 0 ? clientStats.SessionKills : clientStats.SessionKills / clientStats.SessionDeaths;
|
double currentKDR = clientStats.SessionDeaths == 0 ? clientStats.SessionKills : clientStats.SessionKills / clientStats.SessionDeaths;
|
||||||
double alpha = Math.Sqrt(2) / Math.Min(600, clientStats.Kills + clientStats.Deaths);
|
double alpha = Math.Sqrt(2) / Math.Min(600, Math.Max(clientStats.Kills + clientStats.Deaths, 1));
|
||||||
clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * clientStats.KDR;
|
clientStats.RollingWeightedKDR = (alpha * currentKDR) + (1.0 - alpha) * clientStats.KDR;
|
||||||
double KDRWeight = clientStats.RollingWeightedKDR != 0 ? Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3) : 0;
|
double KDRWeight = Math.Round(Math.Pow(clientStats.RollingWeightedKDR, 1.637 / Math.E), 3);
|
||||||
|
|
||||||
// calculate the weight of the new play time against last 10 hours of gameplay
|
// calculate the weight of the new play time against last 10 hours of gameplay
|
||||||
int totalPlayTime = (clientStats.TimePlayed == 0) ?
|
int totalPlayTime = (clientStats.TimePlayed == 0) ?
|
||||||
|
@ -76,8 +76,12 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
case GameEvent.EventType.ScriptKill:
|
case GameEvent.EventType.ScriptKill:
|
||||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||||
if (killInfo.Length >= 13)
|
if (killInfo.Length >= 13)
|
||||||
|
{
|
||||||
|
// todo: remove me
|
||||||
|
E.Owner.Logger.WriteDebug($"Starting Add script hit (kill) for event with id {E.Id}");
|
||||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GameEvent.EventType.Kill:
|
case GameEvent.EventType.Kill:
|
||||||
if (!E.Owner.CustomCallback)
|
if (!E.Owner.CustomCallback)
|
||||||
@ -90,8 +94,12 @@ namespace IW4MAdmin.Plugins.Stats
|
|||||||
case GameEvent.EventType.ScriptDamage:
|
case GameEvent.EventType.ScriptDamage:
|
||||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||||
if (killInfo.Length >= 13)
|
if (killInfo.Length >= 13)
|
||||||
|
{
|
||||||
|
// todo: remove me
|
||||||
|
E.Owner.Logger.WriteDebug($"Starting Add script hit (damage) for event with id {E.Id}");
|
||||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ namespace Tests
|
|||||||
public void AddAndRemoveClientsViaJoinShouldSucceed()
|
public void AddAndRemoveClientsViaJoinShouldSucceed()
|
||||||
{
|
{
|
||||||
var server = Manager.GetServers().First();
|
var server = Manager.GetServers().First();
|
||||||
var waiters = new Queue<ManualResetEventSlim>();
|
var waiters = new Queue<SemaphoreSlim>();
|
||||||
|
|
||||||
int clientStartIndex = 4;
|
int clientStartIndex = 4;
|
||||||
int clientNum = 10;
|
int clientNum = 10;
|
||||||
@ -103,7 +103,7 @@ namespace Tests
|
|||||||
public void AddAndRemoveClientsViaRconShouldSucceed()
|
public void AddAndRemoveClientsViaRconShouldSucceed()
|
||||||
{
|
{
|
||||||
var server = Manager.GetServers().First();
|
var server = Manager.GetServers().First();
|
||||||
var waiters = new Queue<ManualResetEventSlim>();
|
var waiters = new Queue<SemaphoreSlim>();
|
||||||
|
|
||||||
int clientIndexStart = 1;
|
int clientIndexStart = 1;
|
||||||
int clientNum = 8;
|
int clientNum = 8;
|
||||||
@ -187,7 +187,6 @@ namespace Tests
|
|||||||
|
|
||||||
|
|
||||||
resetEvent.Wait(5000);
|
resetEvent.Wait(5000);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,17 @@ namespace SharedLibraryCore.Commands
|
|||||||
{
|
{
|
||||||
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.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}");
|
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}");
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Kick, E.Data, E.Origin, E.Target, E.Owner));
|
|
||||||
|
var e = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Kick,
|
||||||
|
Data = E.Data,
|
||||||
|
Origin = E.Origin,
|
||||||
|
Target = E.Target,
|
||||||
|
Owner = E.Owner
|
||||||
|
};
|
||||||
|
|
||||||
|
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
|
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
|
||||||
@ -179,14 +189,17 @@ namespace SharedLibraryCore.Commands
|
|||||||
{
|
{
|
||||||
await E.Target.TempBan(Message, length, E.Origin);
|
await E.Target.TempBan(Message, length, E.Origin);
|
||||||
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}");
|
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}");
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent()
|
|
||||||
|
var e = new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.TempBan,
|
Type = GameEvent.EventType.TempBan,
|
||||||
Data = E.Data,
|
Data = E.Data,
|
||||||
Origin = E.Origin,
|
Origin = E.Origin,
|
||||||
Target = E.Target,
|
Target = E.Target,
|
||||||
Owner = E.Owner
|
Owner = E.Owner
|
||||||
});
|
};
|
||||||
|
|
||||||
|
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
|
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
|
||||||
@ -732,7 +745,17 @@ namespace SharedLibraryCore.Commands
|
|||||||
};
|
};
|
||||||
|
|
||||||
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
|
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Flag, E.Data, E.Origin, E.Target, E.Owner));
|
|
||||||
|
var e = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Flag,
|
||||||
|
Data = E.Data,
|
||||||
|
Origin = E.Origin,
|
||||||
|
Target = E.Target,
|
||||||
|
Owner = E.Owner
|
||||||
|
};
|
||||||
|
|
||||||
|
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||||
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
|
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,15 +789,16 @@ namespace SharedLibraryCore.Commands
|
|||||||
await E.Owner.Manager.GetClientService().Update(E.Target);
|
await E.Owner.Manager.GetClientService().Update(E.Target);
|
||||||
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
|
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
|
||||||
|
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent()
|
var e = new GameEvent()
|
||||||
{
|
{
|
||||||
Data = E.Data,
|
Data = E.Data,
|
||||||
Origin = E.Origin,
|
Origin = E.Origin,
|
||||||
Target = E.Target,
|
Target = E.Target,
|
||||||
Owner = E.Owner,
|
Owner = E.Owner,
|
||||||
Type = GameEvent.EventType.Unflag
|
Type = GameEvent.EventType.Unflag
|
||||||
});
|
};
|
||||||
|
|
||||||
|
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
@ -846,7 +870,17 @@ namespace SharedLibraryCore.Commands
|
|||||||
await E.Owner.Manager.GetPenaltyService().Create(newReport);
|
await E.Owner.Manager.GetPenaltyService().Create(newReport);
|
||||||
|
|
||||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
|
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
|
||||||
E.Owner.Manager.GetEventHandler().AddEvent(new GameEvent(GameEvent.EventType.Report, E.Data, E.Origin, E.Target, E.Owner));
|
|
||||||
|
var e = new GameEvent()
|
||||||
|
{
|
||||||
|
Type = GameEvent.EventType.Report,
|
||||||
|
Data = E.Data,
|
||||||
|
Origin = E.Origin,
|
||||||
|
Target = E.Target,
|
||||||
|
Owner = E.Owner
|
||||||
|
};
|
||||||
|
|
||||||
|
E.Owner.Manager.GetEventHandler().AddEvent(e);
|
||||||
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Dtos;
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using SharedLibraryCore.Objects;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API
|
namespace SharedLibraryCore.Events
|
||||||
{
|
{
|
||||||
class EventApi : IEventApi
|
public class EventApi
|
||||||
{
|
{
|
||||||
private const int MaxEvents = 100;
|
const int MaxEvents = 100;
|
||||||
private Queue<EventInfo> RecentEvents = new Queue<EventInfo>();
|
static Queue<EventInfo> RecentEvents = new Queue<EventInfo>();
|
||||||
|
|
||||||
public IEnumerable<EventInfo> GetEvents(bool shouldConsume)
|
public static IEnumerable<EventInfo> GetEvents(bool shouldConsume)
|
||||||
{
|
{
|
||||||
var eventList = RecentEvents.ToArray();
|
var eventList = RecentEvents.ToArray();
|
||||||
|
|
||||||
@ -26,8 +22,9 @@ namespace IW4MAdmin.Application.API
|
|||||||
return eventList;
|
return eventList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnServerEvent(object sender, GameEvent E)
|
public static void OnGameEvent(object sender, GameEventArgs eventState)
|
||||||
{
|
{
|
||||||
|
var E = eventState.Event;
|
||||||
// don't want to clog up the api with unknown events
|
// don't want to clog up the api with unknown events
|
||||||
if (E.Type == GameEvent.EventType.Unknown)
|
if (E.Type == GameEvent.EventType.Unknown)
|
||||||
return;
|
return;
|
||||||
@ -71,7 +68,7 @@ namespace IW4MAdmin.Application.API
|
|||||||
/// Adds event to the list and removes first added if reached max capacity
|
/// Adds event to the list and removes first added if reached max capacity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">EventInfo to add</param>
|
/// <param name="info">EventInfo to add</param>
|
||||||
private void AddNewEvent(EventInfo info)
|
private static void AddNewEvent(EventInfo info)
|
||||||
{
|
{
|
||||||
// remove the first added event
|
// remove the first added event
|
||||||
if (RecentEvents.Count >= MaxEvents)
|
if (RecentEvents.Count >= MaxEvents)
|
@ -44,29 +44,17 @@ namespace SharedLibraryCore
|
|||||||
StatusUpdate
|
StatusUpdate
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameEvent(EventType t, string d, Player O, Player T, Server S)
|
static long NextEventId;
|
||||||
{
|
static long GetNextEventId() => Interlocked.Increment(ref NextEventId);
|
||||||
Type = t;
|
|
||||||
Data = d?.Trim();
|
|
||||||
Origin = O;
|
|
||||||
Target = T;
|
|
||||||
Owner = S;
|
|
||||||
OnProcessed = new ManualResetEventSlim();
|
|
||||||
Time = DateTime.UtcNow;
|
|
||||||
CurrentEventId++;
|
|
||||||
Id = CurrentEventId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameEvent()
|
public GameEvent()
|
||||||
{
|
{
|
||||||
OnProcessed = new ManualResetEventSlim();
|
OnProcessed = new SemaphoreSlim(0);
|
||||||
|
OnProcessed.Release();
|
||||||
Time = DateTime.UtcNow;
|
Time = DateTime.UtcNow;
|
||||||
CurrentEventId++;
|
Id = GetNextEventId();
|
||||||
Id = CurrentEventId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long CurrentEventId;
|
|
||||||
|
|
||||||
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
|
||||||
public string Message;
|
public string Message;
|
||||||
@ -75,8 +63,8 @@ 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; set; }
|
public SemaphoreSlim OnProcessed { get; set; }
|
||||||
public DateTime Time { get; private set; }
|
public DateTime Time { get; set; }
|
||||||
public long Id { get; private set; }
|
public long Id { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
23
SharedLibraryCore/Events/GameEventArgs.cs
Normal file
23
SharedLibraryCore/Events/GameEventArgs.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Events
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// represents the state of a game event for event processing
|
||||||
|
/// </summary>
|
||||||
|
public class GameEventArgs : System.ComponentModel.AsyncCompletedEventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
public GameEventArgs(Exception error, bool cancelled, GameEvent userState) : base(error, cancelled, userState)
|
||||||
|
{
|
||||||
|
Event = userState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Game event that occured on a server
|
||||||
|
/// </summary>
|
||||||
|
public GameEvent Event { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
using SharedLibraryCore.Dtos;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SharedLibraryCore.Interfaces
|
|
||||||
{
|
|
||||||
public interface IEventApi
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Processes event from server as event info
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender">Object state from Delegate method call</param>
|
|
||||||
/// <param name="E">Event to process</param>
|
|
||||||
void OnServerEvent(object sender, GameEvent E);
|
|
||||||
/// <summary>
|
|
||||||
/// Get list of recent events
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="shouldConsume">specify wether the request should clear all events after retrieving</param>
|
|
||||||
/// <returns>List of recent event</returns>
|
|
||||||
IEnumerable<EventInfo> GetEvents(bool shouldConsume);
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,6 +13,6 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// Add a game event event to the queue to be processed
|
/// Add a game event event to the queue to be processed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="gameEvent">Game event</param>
|
/// <param name="gameEvent">Game event</param>
|
||||||
void AddEvent(GameEvent gameEvent);
|
bool AddEvent(GameEvent gameEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <param name="fileSizeDiff"></param>
|
/// <param name="fileSizeDiff"></param>
|
||||||
/// <param name="startPosition"></param>
|
/// <param name="startPosition"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ICollection<GameEvent> EventsFromLog(Server server, long fileSizeDiff, long startPosition);
|
ICollection<GameEvent> ReadEventsFromLog(Server server, long fileSizeDiff, long startPosition);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// how long the log file is
|
/// how long the log file is
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -23,7 +23,6 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
AliasService GetAliasService();
|
AliasService GetAliasService();
|
||||||
PenaltyService GetPenaltyService();
|
PenaltyService GetPenaltyService();
|
||||||
IDictionary<int, Player> GetPrivilegedClients();
|
IDictionary<int, Player> GetPrivilegedClients();
|
||||||
IEventApi GetEventApi();
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the event handlers
|
/// Get the event handlers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -150,7 +150,8 @@ namespace SharedLibraryCore.RCon
|
|||||||
// will this really prevent flooding?
|
// will this really prevent flooding?
|
||||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||||
{
|
{
|
||||||
await Task.Delay(350);
|
//await Task.Delay(350);
|
||||||
|
Thread.Sleep(350);
|
||||||
}
|
}
|
||||||
|
|
||||||
LastQuery = DateTime.Now;
|
LastQuery = DateTime.Now;
|
||||||
|
@ -118,25 +118,17 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, Message);
|
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, Message);
|
||||||
|
|
||||||
var e = new GameEvent()
|
|
||||||
{
|
|
||||||
Message = formattedMessage,
|
|
||||||
Data = formattedMessage,
|
|
||||||
Owner = this,
|
|
||||||
Type = GameEvent.EventType.Broadcast,
|
|
||||||
};
|
|
||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(e);
|
|
||||||
#else
|
#else
|
||||||
Logger.WriteVerbose(Message.StripColors());
|
Logger.WriteVerbose(Message.StripColors());
|
||||||
#endif
|
#endif
|
||||||
Manager.GetEventHandler().AddEvent(new GameEvent()
|
var e = new GameEvent()
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.Broadcast,
|
Type = GameEvent.EventType.Broadcast,
|
||||||
Data = Message,
|
Data = Message,
|
||||||
Owner = this
|
Owner = this
|
||||||
});
|
};
|
||||||
|
|
||||||
|
Manager.GetEventHandler().AddEvent(e);
|
||||||
|
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
|
using SharedLibraryCore.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -14,7 +15,7 @@ namespace WebfrontCore.Controllers.API
|
|||||||
[HttpGet]
|
[HttpGet]
|
||||||
public IActionResult Event(bool shouldConsume = true)
|
public IActionResult Event(bool shouldConsume = true)
|
||||||
{
|
{
|
||||||
var events = Manager.GetEventApi().GetEvents(shouldConsume);
|
var events = EventApi.GetEvents(shouldConsume);
|
||||||
return Json(events);
|
return Json(events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
Manager.GetEventHandler().AddEvent(remoteEvent);
|
Manager.GetEventHandler().AddEvent(remoteEvent);
|
||||||
// wait for the event to process
|
// wait for the event to process
|
||||||
await Task.Run(() => remoteEvent.OnProcessed.Wait(5000));
|
await remoteEvent.OnProcessed.WaitAsync(60*1000);
|
||||||
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
|
||||||
|
Loading…
Reference in New Issue
Block a user