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>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
<Win32Resource />
|
||||
<RootNamespace>IW4MAdmin.Application</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<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)
|
||||
{
|
||||
string stringType;
|
||||
|
||||
try
|
||||
{
|
||||
stringType = Utilities.CurrentLocalization.LocalizationSet[$"GLOBAL_{type.ToString().ToUpper()}"];
|
||||
}
|
||||
|
||||
catch(KeyNotFoundException)
|
||||
{
|
||||
if (!Utilities.CurrentLocalization.LocalizationSet.TryGetValue($"GLOBAL_{type.ToString().ToUpper()}", out string stringType))
|
||||
stringType = type.ToString();
|
||||
}
|
||||
|
||||
string LogLine = $"[{DateTime.Now.ToString("HH:mm:ss")}] - {stringType}: {msg}";
|
||||
lock (ThreadLock)
|
||||
|
@ -24,6 +24,7 @@ namespace IW4MAdmin.Application
|
||||
Localization.Configure.Initialize();
|
||||
var loc = Utilities.CurrentLocalization.LocalizationSet;
|
||||
Console.OutputEncoding = Encoding.UTF8;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
|
||||
Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f;
|
||||
Version = Math.Round(Version, 2);
|
||||
@ -40,7 +41,7 @@ namespace IW4MAdmin.Application
|
||||
|
||||
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();
|
||||
|
||||
var api = API.Master.Endpoint.Get();
|
||||
@ -69,7 +70,7 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(loc["MANAGER_VERSION_FAIL"]);
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
#if !PRERELEASE
|
||||
@ -78,7 +79,7 @@ namespace IW4MAdmin.Application
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
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.ForegroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
#else
|
||||
else if (version.CurrentVersionPrerelease > Version)
|
||||
@ -86,14 +87,14 @@ namespace IW4MAdmin.Application
|
||||
Console.ForegroundColor = ConsoleColor.DarkYellow;
|
||||
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.ForegroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.WriteLine(loc["MANAGER_VERSION_SUCCESS"]);
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.ForegroundColor = ConsoleColor.Gray;
|
||||
}
|
||||
|
||||
ServerManager.Init().Wait();
|
||||
@ -112,13 +113,13 @@ namespace IW4MAdmin.Application
|
||||
|
||||
if (ServerManager.Servers.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No servers are currently being monitored");
|
||||
Console.WriteLine(loc["MANAGER_CONSOLE_NOSERV"]);
|
||||
continue;
|
||||
}
|
||||
|
||||
Origin.CurrentServer = 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('>');
|
||||
|
||||
} while (ServerManager.Running);
|
||||
@ -128,10 +129,6 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Task.Run(() => WebfrontCore.Program.Init(ServerManager));
|
||||
}
|
||||
|
||||
ServerManager.Start();
|
||||
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
|
||||
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
@ -145,6 +142,10 @@ namespace IW4MAdmin.Application
|
||||
Console.WriteLine(loc["MANAGER_EXIT"]);
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
|
||||
ServerManager.Start();
|
||||
ServerManager.Logger.WriteVerbose(loc["MANAGER_SHUTDOWN_SUCCESS"]);
|
||||
}
|
||||
|
||||
static void CheckDirectories()
|
||||
|
@ -41,10 +41,13 @@ namespace IW4MAdmin.Application
|
||||
PenaltyService PenaltySvc;
|
||||
BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||
EventApi Api;
|
||||
GameEventHandler Handler;
|
||||
ManualResetEventSlim OnEvent;
|
||||
Timer StatusUpdateTimer;
|
||||
#if FTP_LOG
|
||||
const int UPDATE_FREQUENCY = 700;
|
||||
#else
|
||||
const int UPDATE_FREQUENCY = 450;
|
||||
const int UPDATE_FREQUENCY = 2000;
|
||||
#endif
|
||||
|
||||
private ApplicationManager()
|
||||
@ -63,6 +66,7 @@ namespace IW4MAdmin.Application
|
||||
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
|
||||
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKey);
|
||||
StartTime = DateTime.UtcNow;
|
||||
OnEvent = new ManualResetEventSlim();
|
||||
}
|
||||
|
||||
private void OnCancelKey(object sender, ConsoleCancelEventArgs args)
|
||||
@ -86,8 +90,18 @@ namespace IW4MAdmin.Application
|
||||
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()
|
||||
{
|
||||
// setup the event handler after the class is initialized
|
||||
Handler = new GameEventHandler(this);
|
||||
#region DATABASE
|
||||
var ipList = (await ClientSvc.Find(c => c.Level > Player.Permission.Trusted))
|
||||
.Select(c => new
|
||||
@ -248,13 +262,8 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
Logger.WriteVerbose($"{Utilities.CurrentLocalization.LocalizationSet["MANAGER_MONITORING_TEXT"]} {ServerInstance.Hostname}");
|
||||
|
||||
// this way we can keep track of execution time and see if problems arise.
|
||||
var Status = new AsyncStatus(ServerInstance, UPDATE_FREQUENCY);
|
||||
lock (TaskStatuses)
|
||||
{
|
||||
TaskStatuses.Add(Status);
|
||||
}
|
||||
// add the start event for this server
|
||||
Handler.AddEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, ServerInstance));
|
||||
}
|
||||
|
||||
catch (ServerException e)
|
||||
@ -273,6 +282,8 @@ namespace IW4MAdmin.Application
|
||||
}
|
||||
|
||||
await Task.WhenAll(config.Servers.Select(c => Init(c)).ToArray());
|
||||
// start polling servers
|
||||
StatusUpdateTimer = new Timer(UpdateStatus, null, 0, 10000);
|
||||
|
||||
#endregion
|
||||
|
||||
@ -344,38 +355,32 @@ namespace IW4MAdmin.Application
|
||||
public void Start()
|
||||
{
|
||||
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];
|
||||
|
||||
// task is read to be rerun
|
||||
if (Status.RequestedTask == null || Status.RequestedTask.Status == TaskStatus.RanToCompletion)
|
||||
try
|
||||
{
|
||||
// remove the task when we want to quit and last run has finished
|
||||
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]");
|
||||
}
|
||||
Task.WaitAll(newEvent.Owner.ExecuteEvent(newEvent));
|
||||
}
|
||||
|
||||
if (Status.RequestedTask.Status == TaskStatus.Faulted)
|
||||
catch (Exception E)
|
||||
{
|
||||
Logger.WriteWarning($"Update task for {(Status.Dependant as Server)} faulted, restarting");
|
||||
Status.Abort();
|
||||
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationSet["SERVER_ERROR_EXCEPTION"]} {newEvent.Owner}");
|
||||
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
|
||||
foreach (var S in Servers)
|
||||
@ -388,6 +393,9 @@ namespace IW4MAdmin.Application
|
||||
public void Stop()
|
||||
{
|
||||
Running = false;
|
||||
|
||||
// trigger the event processing loop to end
|
||||
SetHasEvent();
|
||||
}
|
||||
|
||||
public ILogger GetLogger()
|
||||
@ -417,5 +425,11 @@ namespace IW4MAdmin.Application
|
||||
public IDictionary<int, Player> GetPrivilegedClients() => PrivilegedClients;
|
||||
public IEventApi GetEventApi() => Api;
|
||||
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)
|
||||
{
|
||||
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (playerInfo.Length < 4)
|
||||
continue;
|
||||
int cID = -1;
|
||||
int Ping = -1;
|
||||
Int32.TryParse(playerInfo[2], out Ping);
|
||||
|
@ -18,6 +18,7 @@ using SharedLibraryCore.Exceptions;
|
||||
using Application.Misc;
|
||||
using Application.RconParsers;
|
||||
using IW4MAdmin.Application.EventParsers;
|
||||
using IW4MAdmin.Application.IO;
|
||||
|
||||
namespace IW4MAdmin
|
||||
{
|
||||
@ -25,6 +26,8 @@ namespace IW4MAdmin
|
||||
{
|
||||
private CancellationToken cts;
|
||||
private static Dictionary<string, string> loc = Utilities.CurrentLocalization.LocalizationSet;
|
||||
private GameLogEvent LogEvent;
|
||||
|
||||
|
||||
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg) { }
|
||||
|
||||
@ -180,7 +183,8 @@ namespace IW4MAdmin
|
||||
|
||||
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 &&
|
||||
@ -208,7 +212,7 @@ namespace IW4MAdmin
|
||||
Player Leaving = Players[cNum];
|
||||
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.LastConnection = DateTime.UtcNow;
|
||||
@ -255,16 +259,8 @@ namespace IW4MAdmin
|
||||
|
||||
if (C.RequiresTarget || Args.Length > 0)
|
||||
{
|
||||
int cNum = -1;
|
||||
try
|
||||
{
|
||||
cNum = Convert.ToInt32(Args[0]);
|
||||
}
|
||||
|
||||
catch (FormatException)
|
||||
{
|
||||
|
||||
}
|
||||
if (!Int32.TryParse(Args[0], out int cNum))
|
||||
cNum = -1;
|
||||
|
||||
if (Args[0][0] == '@') // user specifying target by database ID
|
||||
{
|
||||
@ -359,24 +355,27 @@ namespace IW4MAdmin
|
||||
|
||||
public override async Task ExecuteEvent(GameEvent E)
|
||||
{
|
||||
//if (Throttled)
|
||||
// return;
|
||||
|
||||
bool canExecuteCommand = true;
|
||||
await ProcessEvent(E);
|
||||
Manager.GetEventApi().OnServerEvent(this, E);
|
||||
|
||||
foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
{
|
||||
#if !DEBUG
|
||||
try
|
||||
#endif
|
||||
{
|
||||
if (cts.IsCancellationRequested)
|
||||
break;
|
||||
|
||||
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)
|
||||
{
|
||||
Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
|
||||
@ -389,10 +388,168 @@ namespace IW4MAdmin
|
||||
}
|
||||
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()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
@ -429,30 +586,16 @@ namespace IW4MAdmin
|
||||
return CurrentPlayers.Count;
|
||||
}
|
||||
|
||||
long l_size = -1;
|
||||
String[] lines = new String[8];
|
||||
String[] oldLines = new String[8];
|
||||
DateTime start = DateTime.Now;
|
||||
DateTime playerCountStart = DateTime.Now;
|
||||
DateTime lastCount = DateTime.Now;
|
||||
DateTime tickTime = DateTime.Now;
|
||||
bool firstRun = true;
|
||||
int count = 0;
|
||||
|
||||
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
|
||||
{
|
||||
this.cts = cts;
|
||||
//#if DEBUG == false
|
||||
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)
|
||||
return true;
|
||||
|
||||
@ -517,44 +660,6 @@ namespace IW4MAdmin
|
||||
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())
|
||||
{
|
||||
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
|
||||
@ -565,7 +670,6 @@ namespace IW4MAdmin
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//#if !DEBUG
|
||||
catch (NetworkException)
|
||||
{
|
||||
Logger.WriteError($"{loc["SERVER_ERROR_COMMUNICATION"]} {IP}:{Port}");
|
||||
@ -575,7 +679,6 @@ namespace IW4MAdmin
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
Logger.WriteWarning("Event could not parsed properly");
|
||||
Logger.WriteDebug($"Log Line: {lines[count]}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -586,7 +689,6 @@ namespace IW4MAdmin
|
||||
Logger.WriteDebug("Error Trace: " + E.StackTrace);
|
||||
return false;
|
||||
}
|
||||
//#endif
|
||||
}
|
||||
|
||||
public async Task Initialize()
|
||||
@ -657,7 +759,7 @@ namespace IW4MAdmin
|
||||
CustomCallback = await ScriptLoaded();
|
||||
string mainPath = EventParser.GetGameDir();
|
||||
#if DEBUG
|
||||
basepath.Value = @"\\192.168.88.253\Call of Duty 4";
|
||||
basepath.Value = @"\\192.168.88.253\mw2\";
|
||||
#endif
|
||||
string logPath;
|
||||
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}";
|
||||
}
|
||||
|
||||
|
||||
// hopefully fix wine drive name mangling
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
@ -686,183 +789,18 @@ namespace IW4MAdmin
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile = new IFile(logPath);
|
||||
}
|
||||
// LogFile = new IFile(logPath);
|
||||
|
||||
}
|
||||
LogEvent = new GameLogEvent(this, logPath, logfile.Value);
|
||||
Logger.WriteInfo($"Log file is {logPath}");
|
||||
#if DEBUG
|
||||
// LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
||||
// LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
|
||||
#else
|
||||
await Broadcast(loc["BROADCAST_ONLINE"]);
|
||||
#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)
|
||||
{
|
||||
// ensure player gets warned if command not performed on them in game
|
||||
|
@ -70,12 +70,8 @@ namespace IW4MAdmin.Plugins.Login
|
||||
Config = cfg.Configuration();
|
||||
}
|
||||
|
||||
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
|
||||
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||
|
||||
public Task OnUnloadAsync()
|
||||
{
|
||||
AuthorizedClients.Clear();
|
||||
return Utilities.CompletedTask;
|
||||
}
|
||||
public Task OnUnloadAsync() => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
BaseConfigurationHandler<Configuration> Settings;
|
||||
ConcurrentDictionary<int, Tracking> ProfanityCounts;
|
||||
IManager Manager;
|
||||
Task CompletedTask = Task.FromResult(false);
|
||||
|
||||
public async Task OnEventAsync(GameEvent E, Server S)
|
||||
{
|
||||
@ -87,8 +86,8 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
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
|
||||
var clientStats = playerStats[pl.ClientId];
|
||||
// sync their score
|
||||
clientStats.SessionScore = pl.Score;
|
||||
clientStats.SessionScore += (pl.Score - clientStats.LastScore);
|
||||
// remove the client from the stats dictionary as they're leaving
|
||||
playerStats.TryRemove(pl.ClientId, out EFClientStatistics removedValue);
|
||||
detectionStats.TryRemove(pl.ClientId, out Cheat.Detection removedValue2);
|
||||
@ -213,11 +213,13 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
var statsSvc = ContextThreads[serverId];
|
||||
Vector3 vDeathOrigin = null;
|
||||
Vector3 vKillOrigin = null;
|
||||
Vector3 vViewAngles = null;
|
||||
|
||||
try
|
||||
{
|
||||
vDeathOrigin = Vector3.Parse(deathOrigin);
|
||||
vKillOrigin = Vector3.Parse(killOrigin);
|
||||
vViewAngles = Vector3.Parse(viewAngles).FixIW4Angles();
|
||||
}
|
||||
|
||||
catch (FormatException)
|
||||
@ -241,7 +243,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
Damage = Int32.Parse(damage),
|
||||
HitLoc = ParseEnum<IW4Info.HitLocation>.Get(hitLoc, typeof(IW4Info.HitLocation)),
|
||||
Weapon = ParseEnum<IW4Info.WeaponName>.Get(weapon, typeof(IW4Info.WeaponName)),
|
||||
ViewAngles = Vector3.Parse(viewAngles).FixIW4Angles(),
|
||||
ViewAngles = vViewAngles,
|
||||
TimeOffset = Int64.Parse(offset),
|
||||
When = DateTime.UtcNow,
|
||||
IsKillstreakKill = isKillstreakKill[0] != '0',
|
||||
@ -338,11 +340,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
// update the total stats
|
||||
Servers[serverId].ServerStatistics.TotalKills += 1;
|
||||
|
||||
attackerStats.SessionScore = attacker.Score;
|
||||
victimStats.SessionScore = victim.Score;
|
||||
attackerStats.SessionScore += (attacker.Score - attackerStats.LastScore);
|
||||
victimStats.SessionScore += (victim.Score - victimStats.LastScore);
|
||||
|
||||
// calculate for the clients
|
||||
CalculateKill(attackerStats, victimStats);
|
||||
// this should fix the negative SPM
|
||||
attackerStats.LastScore = attacker.Score;
|
||||
victimStats.LastScore = victim.Score;
|
||||
|
||||
// show encouragement/discouragement
|
||||
string streakMessage = (attackerStats.ClientId != victimStats.ClientId) ?
|
||||
@ -457,7 +462,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
clientStats.LastStatCalculation = DateTime.UtcNow;
|
||||
clientStats.LastScore = clientStats.SessionScore;
|
||||
//clientStats.LastScore = clientStats.SessionScore;
|
||||
|
||||
return clientStats;
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
Manager = new StatManager(manager);
|
||||
}
|
||||
|
||||
public Task OnTickAsync(Server S) => Utilities.CompletedTask;
|
||||
public Task OnTickAsync(Server S) => Task.CompletedTask;
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
|
@ -51,8 +51,9 @@ namespace IW4MAdmin.Plugins
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
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;
|
||||
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_OWNER_SUCCESS"]);
|
||||
@ -94,7 +94,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CKick : Command
|
||||
{
|
||||
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()
|
||||
{
|
||||
@ -113,7 +113,7 @@ namespace SharedLibraryCore.Commands
|
||||
{
|
||||
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.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationSet["COMMANDS_KICK_SUCCESS"]}");
|
||||
}
|
||||
@ -144,7 +144,7 @@ namespace SharedLibraryCore.Commands
|
||||
public class CTempBan : Command
|
||||
{
|
||||
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()
|
||||
{
|
||||
@ -716,7 +716,7 @@ namespace SharedLibraryCore.Commands
|
||||
};
|
||||
|
||||
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}");
|
||||
}
|
||||
|
||||
@ -770,7 +770,7 @@ namespace SharedLibraryCore.Commands
|
||||
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -1023,7 +1023,8 @@ namespace SharedLibraryCore.Commands
|
||||
E.Origin.PasswordSalt = hashedPassword[1];
|
||||
|
||||
// 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.Origin.Tell(Utilities.CurrentLocalization.LocalizationSet["COMMANDS_PASSWORD_SUCCESS"]);
|
||||
|
@ -2,7 +2,7 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using System.Threading;
|
||||
using SharedLibraryCore.Objects;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
@ -47,10 +47,13 @@ namespace SharedLibraryCore
|
||||
Origin = O;
|
||||
Target = T;
|
||||
Owner = S;
|
||||
OnProcessed = new ManualResetEventSlim();
|
||||
}
|
||||
|
||||
public GameEvent() { }
|
||||
|
||||
public GameEvent()
|
||||
{
|
||||
OnProcessed = new ManualResetEventSlim();
|
||||
}
|
||||
|
||||
public EventType Type;
|
||||
public string Data; // Data is usually the message sent by player
|
||||
@ -60,5 +63,6 @@ namespace SharedLibraryCore
|
||||
public Server Owner;
|
||||
public Boolean Remote = false;
|
||||
public object Extra { get; set; }
|
||||
public ManualResetEventSlim OnProcessed { get; private set; }
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
|
||||
namespace SharedLibraryCore
|
||||
{
|
||||
@ -27,7 +28,12 @@ namespace SharedLibraryCore
|
||||
public override long Length()
|
||||
{
|
||||
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="logLine">single log line string</param>
|
||||
/// <returns></returns>
|
||||
/// todo: make this integrate without needing the server
|
||||
GameEvent GetEvent(Server server, string logLine);
|
||||
/// <summary>
|
||||
/// Get game specific folder prefix for log files
|
||||
|
@ -23,6 +23,15 @@ namespace SharedLibraryCore.Interfaces
|
||||
PenaltyService GetPenaltyService();
|
||||
IDictionary<int, Player> GetPrivilegedClients();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ namespace SharedLibraryCore
|
||||
PlayerHistory = new Queue<PlayerHistory>();
|
||||
ChatHistory = new List<ChatInfo>();
|
||||
NextMessage = 0;
|
||||
OnEvent = new ManualResetEventSlim();
|
||||
CustomSayEnabled = Manager.GetApplicationSettings().Configuration().EnableCustomSayName;
|
||||
CustomSayName = Manager.GetApplicationSettings().Configuration().CustomSayName;
|
||||
InitializeTokens();
|
||||
@ -132,7 +133,7 @@ namespace SharedLibraryCore
|
||||
await this.ExecuteCommandAsync(formattedMessage);
|
||||
#else
|
||||
Logger.WriteVerbose(Message.StripColors());
|
||||
await Utilities.CompletedTask;
|
||||
await Task.CompletedTask;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -150,7 +151,7 @@ namespace SharedLibraryCore
|
||||
await this.ExecuteCommandAsync(formattedMessage);
|
||||
#else
|
||||
Logger.WriteVerbose($"{Target.ClientNumber}->{Message.StripColors()}");
|
||||
await Utilities.CompletedTask;
|
||||
await Task.CompletedTask;
|
||||
#endif
|
||||
|
||||
if (Target.Level == Player.Permission.Console)
|
||||
@ -308,6 +309,7 @@ namespace SharedLibraryCore
|
||||
public RCon.Connection RemoteConnection { get; protected set; }
|
||||
public IRConParser RconParser { get; protected set; }
|
||||
public IEventParser EventParser { get; set; }
|
||||
public ManualResetEventSlim OnEvent { get; private set; }
|
||||
|
||||
// Internal
|
||||
protected string IP;
|
||||
|
@ -16,7 +16,6 @@ namespace SharedLibraryCore
|
||||
public static class Utilities
|
||||
{
|
||||
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 Localization.Layout CurrentLocalization;
|
||||
|
||||
|
@ -16,24 +16,37 @@ namespace WebfrontCore.Controllers
|
||||
protected IManager Manager;
|
||||
protected readonly DatabaseContext Context;
|
||||
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)
|
||||
{
|
||||
Manager = Program.Manager;
|
||||
|
||||
User = User ?? new EFClient()
|
||||
Client = Client ?? new EFClient()
|
||||
{
|
||||
ClientId = -1
|
||||
};
|
||||
|
||||
if (HttpContext.Connection.RemoteIpAddress.ToString() != "127.0.0.1")
|
||||
if (!HttpContext.Connection.RemoteIpAddress.GetAddressBytes().SequenceEqual(LocalHost))
|
||||
{
|
||||
try
|
||||
{
|
||||
User.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);
|
||||
User.CurrentAlias = new EFAlias() { Name = base.User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
||||
Client.ClientId = Convert.ToInt32(base.User.Claims.First(c => c.Type == ClaimTypes.Sid).Value);
|
||||
Client.Level = (Player.Permission)Enum.Parse(typeof(Player.Permission), User.Claims.First(c => c.Type == ClaimTypes.Role).Value);
|
||||
Client.CurrentAlias = new EFAlias() { Name = User.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value };
|
||||
}
|
||||
|
||||
catch (InvalidOperationException)
|
||||
@ -44,24 +57,17 @@ namespace WebfrontCore.Controllers
|
||||
|
||||
else
|
||||
{
|
||||
User.ClientId = 1;
|
||||
User.Level = Player.Permission.Console;
|
||||
User.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
|
||||
Client.ClientId = 1;
|
||||
Client.Level = Player.Permission.Console;
|
||||
Client.CurrentAlias = new EFAlias() { Name = "IW4MAdmin" };
|
||||
}
|
||||
|
||||
Authorized = User.ClientId >= 0;
|
||||
Authorized = Client.ClientId >= 0;
|
||||
ViewBag.Authorized = Authorized;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,10 @@ namespace WebfrontCore.Controllers
|
||||
var server = Manager.GetServers().First(s => s.GetHashCode() == serverId);
|
||||
var client = new Player()
|
||||
{
|
||||
ClientId = User.ClientId,
|
||||
Level = User.Level,
|
||||
ClientId = Client.ClientId,
|
||||
Level = Client.Level,
|
||||
CurrentServer = server,
|
||||
CurrentAlias = new Alias() { Name = User.Name }
|
||||
CurrentAlias = new Alias() { Name = Client.Name }
|
||||
};
|
||||
var remoteEvent = new GameEvent()
|
||||
{
|
||||
@ -46,8 +46,9 @@ namespace WebfrontCore.Controllers
|
||||
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();
|
||||
|
||||
// remove the added command response
|
||||
|
@ -33,7 +33,6 @@ a.nav-link {
|
||||
}
|
||||
|
||||
.server-history,
|
||||
.server-header,
|
||||
.server-activity,
|
||||
#mobile_seperator,
|
||||
.border-bottom {
|
||||
|
Loading…
Reference in New Issue
Block a user