From d45729d7e1a46f8518b2925c8ce6fd6572d7d72e Mon Sep 17 00:00:00 2001 From: RaidMax Date: Sat, 29 Sep 2018 14:52:22 -0500 Subject: [PATCH] clean up rcon, fix a bunch of little things --- Application/GameEventHandler.cs | 56 +-- Application/Manager.cs | 121 +++-- Application/Server.cs | 36 +- Master/Master.pyproj | 5 + Master/master/models/servermodel.py | 3 +- Master/master/resources/instance.py | 5 + Master/master/resources/server.py | 6 + Master/master/routes.py | 4 +- Master/master/schema/serverschema.py | 9 +- Master/master/templates/layout.html | 1 + Master/master/templates/serverlist.html | 113 +++++ Master/master/views.py | 16 +- Plugins/Login/Commands/CLogin.cs | 7 +- Plugins/ProfanityDeterment/Plugin.cs | 11 +- Plugins/Stats/Cheat/Detection.cs | 5 +- Plugins/Stats/Commands/MostPlayed.cs | 8 +- Plugins/Stats/Commands/ResetStats.cs | 4 +- Plugins/Stats/Commands/TopStats.cs | 7 +- Plugins/Stats/Commands/ViewStats.cs | 19 +- Plugins/Stats/Helpers/StatManager.cs | 126 +++--- .../Stats/Helpers/ThreadSafeStatsService.cs | 4 +- Plugins/Stats/Plugin.cs | 4 + Plugins/Welcome/Plugin.cs | 8 +- .../Commands/CommandProcessing.cs | 28 +- SharedLibraryCore/Commands/NativeCommands.cs | 426 ++++++++---------- SharedLibraryCore/Events/GameEvent.cs | 72 ++- .../Exceptions/NetworkException.cs | 5 + .../20180922231600_ReaddACSnapshot.cs | 10 +- SharedLibraryCore/Objects/Player.cs | 297 +++++++++++- SharedLibraryCore/RCon/Connection.cs | 356 +++++---------- SharedLibraryCore/RCon/StaticHelpers.cs | 3 +- SharedLibraryCore/Server.cs | 20 +- .../Services/GenericRepository.cs | 11 +- 33 files changed, 993 insertions(+), 813 deletions(-) create mode 100644 Master/master/resources/server.py create mode 100644 Master/master/templates/serverlist.html diff --git a/Application/GameEventHandler.cs b/Application/GameEventHandler.cs index 7ba8069d2..c186ef975 100644 --- a/Application/GameEventHandler.cs +++ b/Application/GameEventHandler.cs @@ -9,69 +9,15 @@ namespace IW4MAdmin.Application { class GameEventHandler : IEventHandler { - static long NextEventId = 1; readonly IManager Manager; - readonly SortedList OutOfOrderEvents; - readonly SemaphoreSlim IsProcessingEvent; - public GameEventHandler(IManager mgr) { Manager = mgr; - OutOfOrderEvents = new SortedList(); - IsProcessingEvent = new SemaphoreSlim(1, 1); } public void AddEvent(GameEvent gameEvent) { -#if DEBUG - Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}"); -#endif - GameEvent sortedEvent = null; - lock (OutOfOrderEvents) - { - sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); - - while (sortedEvent?.Id == Interlocked.Read(ref NextEventId)) - { - if (OutOfOrderEvents.Count > 0) - { - OutOfOrderEvents.RemoveAt(0); - } - - AddEvent(sortedEvent); - sortedEvent = OutOfOrderEvents.Values.FirstOrDefault(); - } - } - - // 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 as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); -#if DEBUG == true - Manager.GetLogger().WriteDebug($"notified event {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}"); -#endif - 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 - { -#if DEBUG == true - 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"); -#endif - - // this prevents multiple threads from adding simultaneously - lock (OutOfOrderEvents) - { - if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard)) - { - OutOfOrderEvents.Add(gameEvent.Id, gameEvent); - } - } - } + ((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent)); } } } diff --git a/Application/Manager.cs b/Application/Manager.cs index add51aac0..509b0403f 100644 --- a/Application/Manager.cs +++ b/Application/Manager.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.IO; using System.Threading.Tasks; +using System.Text; +using System.Reflection; using SharedLibraryCore; using SharedLibraryCore.Interfaces; @@ -12,18 +13,12 @@ using SharedLibraryCore.Helpers; using SharedLibraryCore.Exceptions; using SharedLibraryCore.Objects; using SharedLibraryCore.Services; -using IW4MAdmin.Application.API; -using Microsoft.Extensions.Configuration; -using WebfrontCore; using SharedLibraryCore.Configuration; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Text; -using IW4MAdmin.Application.API.Master; -using System.Reflection; using SharedLibraryCore.Database; using SharedLibraryCore.Events; +using IW4MAdmin.Application.API.Master; + namespace IW4MAdmin.Application { public class ApplicationManager : IManager @@ -69,16 +64,15 @@ namespace IW4MAdmin.Application StartTime = DateTime.UtcNow; OnQuit = new ManualResetEventSlim(); PageList = new PageList(); - OnServerEvent += OnServerEventAsync; + OnServerEvent += OnGameEvent; OnServerEvent += EventApi.OnGameEvent; } - private async void OnServerEventAsync(object sender, GameEventArgs args) + private async void OnGameEvent(object sender, GameEventArgs args) { - //if (!await ProcessingEvent.WaitAsync(60000)) - //{ - // Logger.WriteWarning(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EVENT_TIMEOUT"]); - //} +#if DEBUG == true + Logger.WriteDebug($"Entering event process for {args.Event.Id}"); +#endif var newEvent = args.Event; @@ -90,68 +84,72 @@ namespace IW4MAdmin.Application Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed"); if (newEvent.Type == GameEvent.EventType.Command) { - await newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]); + newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]); } // offload it to the player to keep newEvent.Origin.DelayedEvents.Enqueue(newEvent); - newEvent.OnProcessed.Set(); - goto finish; } // if the target client is not in an authorized state (detected by RCon) don't execute the event - if (GameEvent.ShouldTargetEventBeDelayed(newEvent)) + else if (GameEvent.ShouldTargetEventBeDelayed(newEvent)) { Logger.WriteDebug($"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed"); // offload it to the player to keep newEvent.Target.DelayedEvents.Enqueue(newEvent); - newEvent.OnProcessed.Set(); - goto finish; } - await newEvent.Owner.ExecuteEvent(newEvent); - - // todo: this is a hacky mess - if (newEvent.Origin?.DelayedEvents.Count > 0 && - (//newEvent.Origin?.State == Player.ClientState.Connected || - newEvent.Type == GameEvent.EventType.Connect)) + else { - var events = newEvent.Origin.DelayedEvents; - // add the delayed event to the queue - while (events.Count > 0) + await newEvent.Owner.ExecuteEvent(newEvent); + + // save the event info to the database + var changeHistorySvc = new ChangeHistoryService(); + await changeHistorySvc.Add(args.Event); + + // todo: this is a hacky mess + if (newEvent.Origin?.DelayedEvents.Count > 0 && + (//newEvent.Origin?.State == Player.ClientState.Connected || + newEvent.Type == GameEvent.EventType.Connect)) { - var oldEvent = events.Dequeue(); + var events = newEvent.Origin.DelayedEvents; - var e = new GameEvent() + // add the delayed event to the queue + while (events.Count > 0) { - Type = oldEvent.Type, - Origin = newEvent.Origin, - Data = oldEvent.Data, - Extra = oldEvent.Extra, - Owner = oldEvent.Owner, - Message = oldEvent.Message, - Target = oldEvent.Target, - Remote = oldEvent.Remote - }; + var oldEvent = events.Dequeue(); - e.Origin = newEvent.Origin; - // check if the target was assigned - if (e.Target != null) - { - // update the target incase they left or have newer info - e.Target = newEvent.Owner.GetPlayersAsList() - .FirstOrDefault(p => p.NetworkId == e.Target.NetworkId); - // we have to throw out the event because they left - if (e.Target == null) + var e = new GameEvent() { - Logger.WriteWarning($"Delayed event for {e.Origin} was removed because the target has left"); - // hack: don't do anything with the event because the target is invalid - e.Type = GameEvent.EventType.Unknown; + Type = oldEvent.Type, + Origin = newEvent.Origin, + Data = oldEvent.Data, + Extra = oldEvent.Extra, + Owner = oldEvent.Owner, + Message = oldEvent.Message, + Target = oldEvent.Target, + Remote = oldEvent.Remote + }; + + e.Origin = newEvent.Origin; + // check if the target was assigned + if (e.Target != null) + { + // update the target incase they left or have newer info + e.Target = newEvent.Owner.GetPlayersAsList() + .FirstOrDefault(p => p.NetworkId == e.Target.NetworkId); + // we have to throw out the event because they left + if (e.Target == null) + { + Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left"); + // hack: don't do anything with the event because the target is invalid + e.Type = GameEvent.EventType.Unknown; + } } + Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing"); + this.GetEventHandler().AddEvent(e); } - Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing"); - this.GetEventHandler().AddEvent(e); } } @@ -163,7 +161,7 @@ namespace IW4MAdmin.Application // this happens if a plugin requires login catch (AuthorizationException ex) { - await newEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_NOTAUTHORIZED"]} - {ex.Message}"); + newEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_NOTAUTHORIZED"]} - {ex.Message}"); } catch (NetworkException ex) @@ -183,16 +181,8 @@ namespace IW4MAdmin.Application Logger.WriteDebug(ex.GetExceptionInfo()); } - finish: - //if (ProcessingEvent.CurrentCount < 1) - //{ - // ProcessingEvent.Release(1); - //} // tell anyone waiting for the output that we're done newEvent.OnProcessed.Set(); - - var changeHistorySvc = new ChangeHistoryService(); - await changeHistorySvc.Add(args.Event); } public IList GetServers() @@ -547,10 +537,7 @@ namespace IW4MAdmin.Application public void Start() { // this needs to be run seperately from the main thread -#if !DEBUG - // start heartbeat var _ = Task.Run(() => SendHeartbeat(new HeartbeatState())); -#endif _ = Task.Run(() => UpdateServerStates()); while (Running) diff --git a/Application/Server.cs b/Application/Server.cs index c8b86b5aa..8ebb86ba3 100644 --- a/Application/Server.cs +++ b/Application/Server.cs @@ -71,7 +71,6 @@ namespace IW4MAdmin polledPlayer.Ping < 1 || polledPlayer.ClientNumber < 0) { - //Logger.WriteDebug($"Skipping client not in connected state {P}"); return false; } @@ -237,7 +236,7 @@ namespace IW4MAdmin { autoKickClient.AdministeredPenalties.Add(new EFPenalty() { AutomatedOffense = currentBan.AutomatedOffense }); } - await player.Ban($"{currentBan.Offense}", autoKickClient); + player.Ban($"{currentBan.Offense}", autoKickClient); } // they didn't fully connect so empty their slot @@ -264,7 +263,7 @@ namespace IW4MAdmin if (cNum >= 0 && Players[cNum] != null) { Player Leaving = Players[cNum]; - + // occurs when the player disconnects via log before being authenticated by RCon if (Leaving.State != Player.ClientState.Connected) { @@ -286,6 +285,7 @@ namespace IW4MAdmin public override async Task ExecuteEvent(GameEvent E) { bool canExecuteCommand = true; + if (!await ProcessEvent(E)) { return; @@ -318,7 +318,7 @@ namespace IW4MAdmin } catch (AuthorizationException e) { - await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}"); + E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}"); canExecuteCommand = false; } catch (Exception Except) @@ -334,7 +334,7 @@ namespace IW4MAdmin (canExecuteCommand || E.Origin?.Level == Player.Permission.Console)) { - await (((Command)E.Extra).ExecuteAsync(E)); + var _ = (((Command)E.Extra).ExecuteAsync(E)); } } @@ -345,7 +345,7 @@ namespace IW4MAdmin /// override protected async Task ProcessEvent(GameEvent E) { - if (E.Type == GameEvent.EventType.Connect) + if (E.Type == GameEvent.EventType.Connect) { E.Origin.State = Player.ClientState.Authenticated; // add them to the server @@ -366,7 +366,9 @@ namespace IW4MAdmin }); if (E.Origin.Level > Player.Permission.Moderator) - await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count)); + { + E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count)); + } } else if (E.Type == GameEvent.EventType.Join) @@ -558,8 +560,8 @@ namespace IW4MAdmin } // only check every 2 minutes if the server doesn't seem to be responding - /* if ((DateTime.Now - LastPoll).TotalMinutes < 0.5 && ConnectionErrors >= 1) - return true;*/ + /* if ((DateTime.Now - LastPoll).TotalMinutes < 0.5 && ConnectionErrors >= 1) + return true;*/ try { @@ -668,7 +670,9 @@ namespace IW4MAdmin string[] messages = this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]).Split(Environment.NewLine); foreach (string message in messages) - await Broadcast(message); + { + Broadcast(message); + } NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1; start = DateTime.Now; @@ -778,7 +782,7 @@ namespace IW4MAdmin Logger.WriteWarning("Game log file not properly initialized, restarting map..."); await this.ExecuteCommandAsync("map_restart"); logfile = await this.GetDvarAsync("g_log"); - } + } CustomCallback = await ScriptLoaded(); string mainPath = EventParser.GetGameDir(); @@ -811,7 +815,7 @@ namespace IW4MAdmin Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}"); #if !DEBUG throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {logPath}"); -//#else + //#else LogEvent = new GameLogEventDetection(this, logPath, logfile.Value); #endif } @@ -824,7 +828,7 @@ namespace IW4MAdmin _ = Task.Run(() => LogEvent.PollForChanges()); #if !DEBUG - await Broadcast(loc["BROADCAST_ONLINE"]); + Broadcast(loc["BROADCAST_ONLINE"]); #endif } @@ -847,13 +851,13 @@ namespace IW4MAdmin { if (Target.Warnings >= 4) { - await Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient); + Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient); return; } Target.Warnings++; - String Message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}"; - await Target.CurrentServer.Broadcast(Message); + String message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}"; + Target.CurrentServer.Broadcast(message); } Penalty newPenalty = new Penalty() diff --git a/Master/Master.pyproj b/Master/Master.pyproj index 379708079..271fede2d 100644 --- a/Master/Master.pyproj +++ b/Master/Master.pyproj @@ -73,6 +73,9 @@ Code + + Code + Code @@ -108,7 +111,9 @@ + + diff --git a/Master/master/models/servermodel.py b/Master/master/models/servermodel.py index c81d2d0d8..e93a2a054 100644 --- a/Master/master/models/servermodel.py +++ b/Master/master/models/servermodel.py @@ -1,6 +1,6 @@ class ServerModel(object): - def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype): + def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype, ip): self.id = id self.port = port self.game = game @@ -9,6 +9,7 @@ class ServerModel(object): self.maxclientnum = maxclientnum self.map = map self.gametype = gametype + self.ip = ip def __repr__(self): return ''.format(id=self.id) diff --git a/Master/master/resources/instance.py b/Master/master/resources/instance.py index c84154a13..c616fdb30 100644 --- a/Master/master/resources/instance.py +++ b/Master/master/resources/instance.py @@ -4,6 +4,7 @@ from flask_jwt_extended import jwt_required from marshmallow import ValidationError from master.schema.instanceschema import InstanceSchema from master import ctx +import json class Instance(Resource): def get(self, id=None): @@ -21,6 +22,8 @@ class Instance(Resource): @jwt_required def put(self, id): try: + for server in request.json['servers']: + server['ip'] = request.remote_addr instance = InstanceSchema().load(request.json) except ValidationError as err: return {'message' : err.messages }, 400 @@ -30,6 +33,8 @@ class Instance(Resource): @jwt_required def post(self): try: + for server in request.json['servers']: + server['ip'] = request.remote_addr instance = InstanceSchema().load(request.json) except ValidationError as err: return {'message' : err.messages }, 400 diff --git a/Master/master/resources/server.py b/Master/master/resources/server.py new file mode 100644 index 000000000..b7cb98a42 --- /dev/null +++ b/Master/master/resources/server.py @@ -0,0 +1,6 @@ +from flask_restful import Resource + +class Server(Resource): + """description of class""" + + diff --git a/Master/master/routes.py b/Master/master/routes.py index 52f618b78..f5751a8e9 100644 --- a/Master/master/routes.py +++ b/Master/master/routes.py @@ -6,10 +6,12 @@ from master.resources.authenticate import Authenticate from master.resources.version import Version from master.resources.history_graph import HistoryGraph from master.resources.localization import Localization +from master.resources.server import Server api.add_resource(Null, '/null') api.add_resource(Instance, '/instance/', '/instance/') api.add_resource(Version, '/version') api.add_resource(Authenticate, '/authenticate') api.add_resource(HistoryGraph, '/history/', '/history/') -api.add_resource(Localization, '/localization/', '/localization/') \ No newline at end of file +api.add_resource(Localization, '/localization/', '/localization/') +api.add_resource(Server, '/server') \ No newline at end of file diff --git a/Master/master/schema/serverschema.py b/Master/master/schema/serverschema.py index 81e6d3e44..dcfce8d48 100644 --- a/Master/master/schema/serverschema.py +++ b/Master/master/schema/serverschema.py @@ -6,17 +6,20 @@ class ServerSchema(Schema): required=True, validate=validate.Range(1, 2147483647, 'invalid id') ) + ip = fields.Str( + required=True + ) port = fields.Int( required=True, - validate=validate.Range(1, 665535, 'invalid port') + validate=validate.Range(1, 65535, 'invalid port') ) game = fields.String( required=True, - validate=validate.Length(1, 8, 'invalid game name') + validate=validate.Length(1, 5, 'invalid game name') ) hostname = fields.String( required=True, - validate=validate.Length(1, 48, 'invalid hostname') + validate=validate.Length(1, 64, 'invalid hostname') ) clientnum = fields.Int( required=True, diff --git a/Master/master/templates/layout.html b/Master/master/templates/layout.html index 2526a533a..35bb3b6d1 100644 --- a/Master/master/templates/layout.html +++ b/Master/master/templates/layout.html @@ -39,6 +39,7 @@ + {% block scripts %}{% endblock %} diff --git a/Master/master/templates/serverlist.html b/Master/master/templates/serverlist.html new file mode 100644 index 000000000..761c4e789 --- /dev/null +++ b/Master/master/templates/serverlist.html @@ -0,0 +1,113 @@ +{% extends "layout.html" %} + +{% block content %} + + + + + +
+
+ +
+ {% for game, servers in games.items() %} + +
+ + + + + + + + + + + + + + {% for server in servers %} + + + + + + + + + + {% endfor %} + + +
Server NameMap NamePlayersModeConnect
{{server.hostname}}{{server.map}} + {{server.clientnum}}/{{server.maxclientnum}} + {{server.gametype}}
+
+ + {% endfor %} +
+
+ Developed by RaidMax
+ PRERELEASE +
+
+
+ {% endblock %} + + {% block scripts %} + + {% endblock %} diff --git a/Master/master/views.py b/Master/master/views.py index 5b3fdee36..cc5edd42b 100644 --- a/Master/master/views.py +++ b/Master/master/views.py @@ -4,8 +4,9 @@ Routes and views for the flask application. from datetime import datetime from flask import render_template -from master import app +from master import app, ctx from master.resources.history_graph import HistoryGraph +from collections import defaultdict @app.route('/') def home(): @@ -19,3 +20,16 @@ def home(): client_count = _history_graph[0]['client_count'], server_count = _history_graph[0]['server_count'] ) + +@app.route('/servers') +def servers(): + servers = defaultdict(list) + if len(ctx.instance_list.values()) > 0: + ungrouped_servers = [server for instance in ctx.instance_list.values() for server in instance.servers] + for server in ungrouped_servers: + servers[server.game].append(server) + return render_template( + 'serverlist.html', + title = 'Server List', + games = servers + ) diff --git a/Plugins/Login/Commands/CLogin.cs b/Plugins/Login/Commands/CLogin.cs index 8e914d11e..450fced17 100644 --- a/Plugins/Login/Commands/CLogin.cs +++ b/Plugins/Login/Commands/CLogin.cs @@ -15,7 +15,8 @@ namespace IW4MAdmin.Plugins.Login.Commands Name = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_PASSWORD"], Required = true } - }){ } + }) + { } public override async Task ExecuteAsync(GameEvent E) { @@ -25,12 +26,12 @@ namespace IW4MAdmin.Plugins.Login.Commands if (hashedPassword[0] == client.Password) { Plugin.AuthorizedClients[E.Origin.ClientId] = true; - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]); } else { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]); } } } diff --git a/Plugins/ProfanityDeterment/Plugin.cs b/Plugins/ProfanityDeterment/Plugin.cs index 2a460aea5..08e230b2e 100644 --- a/Plugins/ProfanityDeterment/Plugin.cs +++ b/Plugins/ProfanityDeterment/Plugin.cs @@ -22,10 +22,10 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment ConcurrentDictionary ProfanityCounts; IManager Manager; - public async Task OnEventAsync(GameEvent E, Server S) + public Task OnEventAsync(GameEvent E, Server S) { if (!Settings.Configuration().EnableProfanityDeterment) - return; + return Task.CompletedTask; ; if (E.Type == GameEvent.EventType.Connect) { @@ -48,7 +48,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment if (containsObjectionalWord) { - await E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player() + E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player() { ClientId = 1 }); @@ -84,7 +84,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment var clientProfanity = ProfanityCounts[E.Origin.ClientId]; if (clientProfanity.Infringements >= Settings.Configuration().KickAfterInfringementCount) { - await clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player() + clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player() { ClientId = 1 }); @@ -94,13 +94,14 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment { clientProfanity.Infringements++; - await clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player() + clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player() { ClientId = 1 }); } } } + return Task.CompletedTask; } public async Task OnLoadAsync(IManager manager) diff --git a/Plugins/Stats/Cheat/Detection.cs b/Plugins/Stats/Cheat/Detection.cs index 5f281517e..e51d75055 100644 --- a/Plugins/Stats/Cheat/Detection.cs +++ b/Plugins/Stats/Cheat/Detection.cs @@ -139,10 +139,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat } // flag - if ( -#if DEBUG == false - currentStrain > Thresholds.MaxStrainFlag && -#endif + if (currentStrain > Thresholds.MaxStrainFlag && HitCount >= 10) { result = new DetectionPenaltyResult() diff --git a/Plugins/Stats/Commands/MostPlayed.cs b/Plugins/Stats/Commands/MostPlayed.cs index 54686dab2..0d52608d0 100644 --- a/Plugins/Stats/Commands/MostPlayed.cs +++ b/Plugins/Stats/Commands/MostPlayed.cs @@ -63,12 +63,16 @@ namespace IW4MAdmin.Plugins.Stats.Commands if (!E.Message.IsBroadcastCommand()) { foreach (var stat in topStats) - await E.Origin.Tell(stat); + { + E.Origin.Tell(stat); + } } else { foreach (var stat in topStats) - await E.Owner.Broadcast(stat); + { + E.Owner.Broadcast(stat); + } } } } diff --git a/Plugins/Stats/Commands/ResetStats.cs b/Plugins/Stats/Commands/ResetStats.cs index ce3e142d0..ebcbe88f7 100644 --- a/Plugins/Stats/Commands/ResetStats.cs +++ b/Plugins/Stats/Commands/ResetStats.cs @@ -34,12 +34,12 @@ namespace IW4MAdmin.Plugins.Stats.Commands // fixme: this doesn't work properly when another context exists await svc.SaveChangesAsync(); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]); } else { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_FAIL"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_FAIL"]); } } } diff --git a/Plugins/Stats/Commands/TopStats.cs b/Plugins/Stats/Commands/TopStats.cs index 36a343fac..23edf63a0 100644 --- a/Plugins/Stats/Commands/TopStats.cs +++ b/Plugins/Stats/Commands/TopStats.cs @@ -76,16 +76,15 @@ namespace IW4MAdmin.Plugins.Stats.Commands { foreach (var stat in topStats) { - await E.Origin.Tell(stat); - await Task.Delay(SharedLibraryCore.RCon.StaticHelpers.FloodProtectionInterval); + E.Origin.Tell(stat); + } } else { foreach (var stat in topStats) { - await E.Owner.Broadcast(stat); - await Task.Delay(SharedLibraryCore.RCon.StaticHelpers.FloodProtectionInterval); + E.Owner.Broadcast(stat); } } } diff --git a/Plugins/Stats/Commands/ViewStats.cs b/Plugins/Stats/Commands/ViewStats.cs index 0836d13ca..80a559851 100644 --- a/Plugins/Stats/Commands/ViewStats.cs +++ b/Plugins/Stats/Commands/ViewStats.cs @@ -35,39 +35,40 @@ namespace IW4MAdmin.Plugins.Stats.Commands if (E.Target == null) { - await E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]); - return; + E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]); } } var clientStats = new GenericRepository(); int serverId = E.Owner.GetHashCode(); - if (E.Target != null) { - pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId).First(); + pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId)).First(); statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}"; } else { - pStats = pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId).First(); + pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)).First(); statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}"; } if (E.Message.IsBroadcastCommand()) { string name = E.Target == null ? E.Origin.Name : E.Target.Name; - await E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7"); - await E.Owner.Broadcast(statLine); + E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7"); + E.Owner.Broadcast(statLine); } else { if (E.Target != null) - await E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7"); - await E.Origin.Tell(statLine); + { + E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7"); + } + + E.Origin.Tell(statLine); } } } diff --git a/Plugins/Stats/Helpers/StatManager.cs b/Plugins/Stats/Helpers/StatManager.cs index bde034570..1449e6607 100644 --- a/Plugins/Stats/Helpers/StatManager.cs +++ b/Plugins/Stats/Helpers/StatManager.cs @@ -243,8 +243,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers } var playerStats = Servers[serverId].PlayerStats; - var statsSvc = ContextThreads[serverId]; var detectionStats = Servers[serverId].PlayerDetections; + var statsSvc = ContextThreads[serverId]; if (playerStats.ContainsKey(pl.ClientId)) { @@ -295,17 +295,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await statsSvc.ClientStatSvc.SaveChangesAsync(); } - // adjusts for adding new hit location - if (clientStats.HitLocations.Count == 19) - { - clientStats.HitLocations.Add(new EFHitLocationCount() - { - Location = IW4Info.HitLocation.shield - }); - - await statsSvc.ClientStatSvc.SaveChangesAsync(); - } - // for stats before rating if (clientStats.EloRating == 0.0) { @@ -382,7 +371,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers if (match.Success) { - // this gives us what time the player is on + // this gives us what team the player is on var attackerStats = Servers[serverId].PlayerStats[attackerClientId]; var victimStats = Servers[serverId].PlayerStats[victimClientId]; IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString()); @@ -507,13 +496,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx); } - await ctx.SaveChangesAsync(); await clientStatsSvc.SaveChangesAsync(); + await ctx.SaveChangesAsync(); } catch (Exception ex) { - + Log.WriteError("AC ERROR"); + Log.WriteDebug(ex.GetExceptionInfo()); } OnProcessingPenalty.Release(1); @@ -529,7 +519,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { break; } - await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player() + attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player() { ClientId = 1, AdministeredPenalties = new List() @@ -587,80 +577,80 @@ namespace IW4MAdmin.Plugins.Stats.Helpers while ((change = clientDetection.Tracker.GetNextChange()) != default(EFACSnapshot)) { - //if (change.HitOriginId == 0) - //{ - // change.HitOriginId = change.HitOrigin.Vector3Id; - // change.HitOrigin = null; - //} - - //if (change.HitDestinationId == 0) - //{ - // change.HitDestinationId = change.HitDestination.Vector3Id; - // change.HitDestination = null; - //} - - //if (change.CurrentViewAngleId == 0) - //{ - // change.CurrentViewAngleId = change.CurrentViewAngle.Vector3Id; - // change.CurrentViewAngle = null; - //} - - //if (change.LastStrainAngleId == 0) - //{ - // change.LastStrainAngleId = change.LastStrainAngle.Vector3Id; - // change.LastStrainAngle = null; - //} - if (change.HitOrigin.Vector3Id > 0) { + change.HitOriginId = change.HitOrigin.Vector3Id; ctx.Attach(change.HitOrigin); } + + else if (change.HitOrigin.Vector3Id == 0) + { + ctx.Add(change.HitOrigin); + } + if (change.HitDestination.Vector3Id > 0) { + change.HitDestinationId = change.HitDestination.Vector3Id; ctx.Attach(change.HitDestination); } + + else if (change.HitDestination.Vector3Id == 0) + { + ctx.Add(change.HitOrigin); + } + if (change.CurrentViewAngle.Vector3Id > 0) { + change.CurrentViewAngleId = change.CurrentViewAngle.Vector3Id; ctx.Attach(change.CurrentViewAngle); } + + else if (change.CurrentViewAngle.Vector3Id == 0) + { + ctx.Add(change.HitOrigin); + } + if (change.LastStrainAngle.Vector3Id > 0) { + change.LastStrainAngleId = change.LastStrainAngle.Vector3Id; ctx.Attach(change.LastStrainAngle); } - ctx.Add(change); + else if (change.LastStrainAngle.Vector3Id == 0) + { + ctx.Add(change.HitOrigin); + } + ctx.Add(change); } } public async Task AddStandardKill(Player attacker, Player victim) { int serverId = attacker.CurrentServer.GetHashCode(); + EFClientStatistics attackerStats = null; - try + if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId)) + { + attackerStats = await AddPlayer(attacker); + } + + else { attackerStats = Servers[serverId].PlayerStats[attacker.ClientId]; } - catch (KeyNotFoundException) + EFClientStatistics victimStats = null; + if (!Servers[serverId].PlayerStats.ContainsKey(victim.ClientId)) { - // happens when the client has disconnected before the last status update - Log.WriteWarning($"[Stats::AddStandardKill] kill attacker ClientId is invalid {attacker.ClientId}-{attacker}"); - return; + victimStats = await AddPlayer(victim); } - EFClientStatistics victimStats = null; - try + else { victimStats = Servers[serverId].PlayerStats[victim.ClientId]; } - catch (KeyNotFoundException) - { - Log.WriteWarning($"[Stats::AddStandardKill] kill victim ClientId is invalid {victim.ClientId}-{victim}"); - return; - } - #if DEBUG Log.WriteDebug("Calculating standard kill"); #endif @@ -691,7 +681,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers StreakMessage.MessageOnStreak(-1, -1); if (streakMessage != string.Empty) - await attacker.Tell(streakMessage); + { + attacker.Tell(streakMessage); + } // fixme: why? if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill)) @@ -711,6 +703,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // update their performance #if !DEBUG if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5) +#else + if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 0.1) #endif { attackerStats.LastStatHistoryUpdate = DateTime.UtcNow; @@ -765,13 +759,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers if (clientHistory.RatingHistoryId == 0) { ctx.Add(clientHistory); - // Log.WriteDebug($"adding first time client history {client.ClientId}"); - await ctx.SaveChangesAsync(); - } - - else - { - //ctx.Update(clientHistory); } #region INDIVIDUAL_SERVER_PERFORMANCE @@ -793,8 +780,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers .First(); ctx.Remove(ratingToRemove); - //Log.WriteDebug($"remove oldest rating {client.ClientId}"); - await ctx.SaveChangesAsync(); } // set the previous newest to false @@ -810,8 +795,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ctx.Update(ratingToUnsetNewest); ctx.Entry(ratingToUnsetNewest).Property(r => r.Newest).IsModified = true; ratingToUnsetNewest.Newest = false; - //Log.WriteDebug($"unsetting previous newest flag {client.ClientId}"); - await ctx.SaveChangesAsync(); } } @@ -829,9 +812,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers // add new rating for current server ctx.Add(newServerRating); - //Log.WriteDebug($"adding new server rating {client.ClientId}"); - await ctx.SaveChangesAsync(); - #endregion #region OVERALL_RATING // select all performance & time played for current client @@ -877,8 +857,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers .First(); ctx.Remove(ratingToRemove); - //Log.WriteDebug($"remove oldest overall rating {client.ClientId}"); - await ctx.SaveChangesAsync(); } // set the previous average newest to false @@ -894,8 +872,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ctx.Update(ratingToUnsetNewest); ctx.Entry(ratingToUnsetNewest).Property(r => r.Newest).IsModified = true; ratingToUnsetNewest.Newest = false; - //Log.WriteDebug($"unsetting overall newest rating {client.ClientId}"); - await ctx.SaveChangesAsync(); } } @@ -913,7 +889,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers ctx.Add(averageRating); #endregion - //Log.WriteDebug($"adding new average rating {client.ClientId}"); + await ctx.SaveChangesAsync(); } } @@ -1148,7 +1124,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers await statsSvc.ServerSvc.SaveChangesAsync(); statsSvc = null; - // this should prevent the gunk for having a long lasting context. + // this should prevent the gunk from having a long lasting context. ContextThreads[serverId] = new ThreadSafeStatsService(); } diff --git a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs index 7a3bea7b2..1863794c4 100644 --- a/Plugins/Stats/Helpers/ThreadSafeStatsService.cs +++ b/Plugins/Stats/Helpers/ThreadSafeStatsService.cs @@ -14,7 +14,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers { get { - return new GenericRepository(); + return new GenericRepository(true); } } public GenericRepository ServerSvc { get; private set; } @@ -30,11 +30,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers public ThreadSafeStatsService() { - //ClientStatSvc = new GenericRepository(); ServerSvc = new GenericRepository(); KillStatsSvc = new GenericRepository(); ServerStatsSvc = new GenericRepository(); - //MessageSvc = new GenericRepository(); } } } diff --git a/Plugins/Stats/Plugin.cs b/Plugins/Stats/Plugin.cs index 7f4eb87fc..6b473c6dc 100644 --- a/Plugins/Stats/Plugin.cs +++ b/Plugins/Stats/Plugin.cs @@ -81,11 +81,15 @@ namespace IW4MAdmin.Plugins.Stats break; case GameEvent.EventType.Kill: if (!E.Owner.CustomCallback) + { await Manager.AddStandardKill(E.Origin, E.Target); + } break; case GameEvent.EventType.Damage: if (!E.Owner.CustomCallback) + { Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode()); + } break; case GameEvent.EventType.ScriptDamage: killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0]; diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index 153dc697c..3506c0814 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -82,17 +82,17 @@ namespace IW4MAdmin.Plugins.Welcome { Player newPlayer = E.Origin; if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked) - await E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer)); + E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer)); - await newPlayer.Tell(ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer)); + newPlayer.Tell(ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer)); if (newPlayer.Level == Player.Permission.Flagged) { var penalty = await new GenericRepository().FindAsync(p => p.OffenderId == newPlayer.ClientId && p.Type == Penalty.PenaltyType.Flag); - await E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penalty.FirstOrDefault()?.Offense}) has joined!"); + E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penalty.FirstOrDefault()?.Offense}) has joined!"); } else - await E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer)); + E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer)); } } diff --git a/SharedLibraryCore/Commands/CommandProcessing.cs b/SharedLibraryCore/Commands/CommandProcessing.cs index a18188a02..9d2166e28 100644 --- a/SharedLibraryCore/Commands/CommandProcessing.cs +++ b/SharedLibraryCore/Commands/CommandProcessing.cs @@ -10,7 +10,7 @@ namespace SharedLibraryCore.Commands { public class CommandProcessing { - public static async Task ValidateCommand(GameEvent E) + public static async Task ValidateCommand(GameEvent E) { var loc = Utilities.CurrentLocalization.LocalizationIndex; var Manager = E.Owner.Manager; @@ -27,7 +27,7 @@ namespace SharedLibraryCore.Commands if (C == null) { - await E.Origin.Tell(loc["COMMAND_UNKNOWN"]); + E.Origin.Tell(loc["COMMAND_UNKNOWN"]); throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\""); } @@ -36,14 +36,14 @@ namespace SharedLibraryCore.Commands if (E.Origin.Level < C.Permission) { - await E.Origin.Tell(loc["COMMAND_NOACCESS"]); + E.Origin.Tell(loc["COMMAND_NOACCESS"]); throw new CommandException($"{E.Origin} does not have access to \"{C.Name}\""); } if (Args.Length < (C.RequiredArgumentCount)) { - await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); - await E.Origin.Tell(C.Syntax); + E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); + E.Origin.Tell(C.Syntax); throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } @@ -82,7 +82,7 @@ namespace SharedLibraryCore.Commands matchingPlayers = E.Owner.GetClientByName(E.Data.Trim()); if (matchingPlayers.Count > 1) { - await E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); + E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } else if (matchingPlayers.Count == 1) @@ -95,8 +95,8 @@ namespace SharedLibraryCore.Commands if (E.Data.Length == 0 && C.RequiredArgumentCount > 1) { - await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); - await E.Origin.Tell(C.Syntax); + E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); + E.Origin.Tell(C.Syntax); throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } } @@ -107,9 +107,11 @@ namespace SharedLibraryCore.Commands matchingPlayers = E.Owner.GetClientByName(Args[0]); if (matchingPlayers.Count > 1) { - await E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); + E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]); foreach (var p in matchingPlayers) - await E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}"); + { + E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}"); + } throw new CommandException($"{E.Origin} had multiple players found for {C.Name}"); } else if (matchingPlayers.Count == 1) @@ -125,8 +127,8 @@ namespace SharedLibraryCore.Commands E.Data == String.Empty) && C.RequiresTarget) { - await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); - await E.Origin.Tell(C.Syntax); + E.Origin.Tell(loc["COMMAND_MISSINGARGS"]); + E.Origin.Tell(C.Syntax); throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\""); } } @@ -134,7 +136,7 @@ namespace SharedLibraryCore.Commands if (E.Target == null && C.RequiresTarget) { - await E.Origin.Tell(loc["COMMAND_TARGET_NOTFOUND"]); + E.Origin.Tell(loc["COMMAND_TARGET_NOTFOUND"]); throw new CommandException($"{E.Origin} specified invalid player for \"{C.Name}\""); } } diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index cb72bacc4..0c7d486de 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -39,13 +39,15 @@ namespace SharedLibraryCore.Commands if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0) { E.Origin.Level = Player.Permission.Owner; - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]); // so setpassword/login works E.Owner.Manager.GetPrivilegedClients().Add(E.Origin.ClientId, E.Origin); await E.Owner.Manager.GetClientService().Update(E.Origin); } else - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_FAIL"]); + { + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_FAIL"]); + } } } @@ -69,22 +71,9 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - if (E.Origin.Level <= E.Target.Level) - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"]} {E.Target.Name}"); - else + if (!await E.Target.Warn(E.Data, E.Origin).WaitAsync()) { - await E.Target.Warn(E.Data, E.Origin); - - var e = new GameEvent() - { - Type = GameEvent.EventType.Warn, - Data = E.Data, - Origin = E.Origin, - Target = E.Target, - Owner = E.Owner - }; - - E.Owner.Manager.GetEventHandler().AddEvent(e); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"]} {E.Target.Name}"); } } } @@ -104,9 +93,10 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - E.Target.Warnings = 0; - String Message = $"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"]} {E.Target.Name}"; - await E.Owner.Broadcast(Message); + if (await E.Target.WarnClear(E.Origin).WaitAsync()) + { + E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"]} {E.Target.Name}"); + } } } @@ -130,24 +120,9 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - if (E.Origin.Level > E.Target.Level) - { - await E.Target.Kick(E.Data, E.Origin); - await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}"); - - 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 - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}"); + var _ = await E.Target.Kick(E.Data, E.Origin).WaitAsync() ? + E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}") : + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}"); } } @@ -164,9 +139,10 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - await E.Owner.Broadcast($"{(E.Owner.GameName == Server.Game.IW4 ? "^:" : "")}{E.Origin.Name} - ^6{E.Data}^7"); + E.Owner.Broadcast($"{(E.Owner.GameName == Server.Game.IW4 ? "^:" : "")}{E.Origin.Name} - ^6{E.Data}^7", E.Origin); + return Task.CompletedTask; } } @@ -200,24 +176,9 @@ namespace SharedLibraryCore.Commands if (length.TotalHours >= 1 && length.TotalHours < 2) Message = E.Data.Replace("1h", "").Replace("1H", ""); - if (E.Origin.Level > E.Target.Level) - { - 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()}"); - - var e = new GameEvent() - { - Type = GameEvent.EventType.TempBan, - Data = E.Data, - Origin = E.Origin, - Target = E.Target, - Owner = E.Owner - }; - - E.Owner.Manager.GetEventHandler().AddEvent(e); - } - else - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}"); + var _ = await E.Target.TempBan(Message, length, E.Origin).WaitAsync() ? + E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") : + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}"); } } @@ -241,13 +202,9 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - if (E.Origin.Level > E.Target.Level) - { - await E.Target.Ban(E.Data, E.Origin); - await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"]}"); - } - else - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"]} {E.Target.Name}"); + var _ = await E.Target.Ban(E.Data, E.Origin).WaitAsync() ? + E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"]}") : + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"]} {E.Target.Name}"); } } @@ -274,12 +231,12 @@ namespace SharedLibraryCore.Commands var penalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId); if (penalties.Where(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan).FirstOrDefault() != null) { - await E.Owner.Unban(E.Data, E.Target, E.Origin); - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"]} {E.Target}"); + await E.Target.Unban(E.Data, E.Origin).WaitAsync(); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"]} {E.Target}"); } else { - await E.Origin.Tell($"{E.Target} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"]}"); + E.Origin.Tell($"{E.Target} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"]}"); } } } @@ -290,10 +247,12 @@ namespace SharedLibraryCore.Commands base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", Player.Permission.User, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString); - await E.Origin.Tell(You); + E.Origin.Tell(You); + + return Task.CompletedTask; } } @@ -303,7 +262,7 @@ namespace SharedLibraryCore.Commands base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", Player.Permission.Moderator, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { StringBuilder playerList = new StringBuilder(); int count = 0; @@ -322,8 +281,7 @@ namespace SharedLibraryCore.Commands if (count == 2 || E.Owner.GetPlayersAsList().Count == 1) { - await E.Origin.Tell(playerList.ToString()); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell(playerList.ToString()); count = 0; playerList = new StringBuilder(); continue; @@ -334,8 +292,12 @@ namespace SharedLibraryCore.Commands if (playerList.Length > 0) { - await E.Origin.Tell(playerList.ToString()); + E.Origin.Tell(playerList.ToString()); } + + // todo: make no players response for webfront + + return Task.CompletedTask; } } @@ -352,7 +314,7 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { String cmd = E.Data.Trim(); @@ -364,15 +326,16 @@ namespace SharedLibraryCore.Commands if (C.Name == cmd.ToLower() || C.Alias == cmd.ToLower()) { - await E.Origin.Tell("[^3" + C.Name + "^7] " + C.Description); - await E.Origin.Tell(C.Syntax); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell($"[^3{C.Name}^7] {C.Description}"); + E.Origin.Tell(C.Syntax); found = true; } } if (!found) - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_NOTFOUND"]); + { + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_NOTFOUND"]); + } } else @@ -388,20 +351,18 @@ namespace SharedLibraryCore.Commands helpResponse.Append(" [^3" + C.Name + "^7] "); if (count >= 4) { - if (E.Message[0] == '@') - await E.Owner.Broadcast(helpResponse.ToString()); - else - await E.Origin.Tell(helpResponse.ToString()); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell(helpResponse.ToString()); helpResponse = new StringBuilder(); count = 0; } count++; } } - await E.Origin.Tell(helpResponse.ToString()); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_MOREINFO"]); + E.Origin.Tell(helpResponse.ToString()); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_MOREINFO"]); } + + return Task.CompletedTask; } } @@ -415,10 +376,9 @@ namespace SharedLibraryCore.Commands { await E.Owner.ExecuteCommandAsync("fast_restart"); - if (!E.Origin.Masked) - await E.Owner.Broadcast($"^5{E.Origin.Name} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_UNMASKED"]}"); - else - await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_MASKED"]); + var _ = !E.Origin.Masked ? + E.Owner.Broadcast($"^5{E.Origin.Name} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_UNMASKED"]}") : + E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_MASKED"]); } } @@ -430,10 +390,10 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - if (!E.Origin.Masked) - await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]"); - else - await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]); + var _ = !E.Origin.Masked ? + E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]", E.Origin) : + E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"], E.Origin); + await Task.Delay(5000); await E.Owner.ExecuteCommandAsync("map_rotate"); } @@ -461,7 +421,7 @@ namespace SharedLibraryCore.Commands { if (E.Target == E.Origin) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SELF"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SELF"]); return; } @@ -471,14 +431,14 @@ namespace SharedLibraryCore.Commands if (newPerm == Player.Permission.Owner && !E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]); return; } if (E.Origin.Level < Player.Permission.Owner && !E.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}"); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}"); return; } @@ -486,7 +446,7 @@ namespace SharedLibraryCore.Commands { if (E.Origin.Level < Player.Permission.Owner) { - await E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString())); + E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString())); return; } } @@ -500,7 +460,7 @@ namespace SharedLibraryCore.Commands { ActiveClient.Level = newPerm; await E.Owner.Manager.GetClientService().Update(ActiveClient); - await ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}"); + ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}"); } else @@ -535,11 +495,13 @@ namespace SharedLibraryCore.Commands E.Owner.Manager.GetEventHandler().AddEvent(e); - await E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}"); + E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}"); } else - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_FAIL"]); + { + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_FAIL"]); + } } } @@ -549,9 +511,10 @@ namespace SharedLibraryCore.Commands base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", Player.Permission.Moderator, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - await E.Origin.Tell($"IW4MAdmin {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"]}" + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB"); + E.Origin.Tell($"IW4MAdmin {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"]} " + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB"); + return Task.CompletedTask; } } @@ -561,11 +524,12 @@ namespace SharedLibraryCore.Commands base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", Player.Permission.Moderator, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { TimeSpan uptime = DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime; var loc = Utilities.CurrentLocalization.LocalizationIndex; - await E.Origin.Tell($"IW4M Admin {loc["COMMANDS_UPTIME_TEXT"]} {uptime.Days} {loc["GLOBAL_TIME_DAYS"]}, {uptime.Hours} {loc["GLOBAL_TIME_HOURS"]}, {uptime.Minutes} {loc["GLOBAL_TIME_MINUTES"]}"); + E.Origin.Tell($"IW4M Admin {loc["COMMANDS_UPTIME_TEXT"]} {uptime.Days} {loc["GLOBAL_TIME_DAYS"]}, {uptime.Hours} {loc["GLOBAL_TIME_HOURS"]}, {uptime.Minutes} {loc["GLOBAL_TIME_MINUTES"]}"); + return Task.CompletedTask; } } @@ -587,15 +551,14 @@ namespace SharedLibraryCore.Commands Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_NONE"]; } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { foreach (string line in OnlineAdmins(E.Owner).Split(Environment.NewLine)) { - var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line); - await t; - - await Task.Delay(FloodProtectionInterval); + var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line); } + + return Task.CompletedTask; } } @@ -619,14 +582,14 @@ namespace SharedLibraryCore.Commands { if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap) { - await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}"); + E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}"); await Task.Delay(5000); await E.Owner.LoadMap(m.Name); return; } } - await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}"); + E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}"); await Task.Delay(5000); await E.Owner.LoadMap(newMap); } @@ -649,7 +612,7 @@ namespace SharedLibraryCore.Commands { if (E.Data.Length < 3) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_MIN"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_MIN"]); return; } @@ -660,7 +623,7 @@ namespace SharedLibraryCore.Commands if (db_players.Count == 0) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_EMPTY"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_EMPTY"]); return; } @@ -671,8 +634,7 @@ namespace SharedLibraryCore.Commands string msg = P.Name.ToLower().Contains(E.Data.ToLower()) ? $"[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}" : $"({P.AliasLink.Children.FirstOrDefault(a => a.Name.ToLower().Contains(E.Data.ToLower()))?.Name})->[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}"; - await E.Origin.Tell(msg); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell(msg); } } } @@ -683,15 +645,14 @@ namespace SharedLibraryCore.Commands base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", Player.Permission.User, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Count < 1 && E.Owner.ServerConfig.Rules?.Count < 1) { - if (E.Message.IsBroadcastCommand()) - await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]); - else - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]); + var _ = E.Message.IsBroadcastCommand() ? + E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]) : + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]); } else @@ -703,11 +664,11 @@ namespace SharedLibraryCore.Commands foreach (string r in rules) { - var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}"); - await t; - await Task.Delay(FloodProtectionInterval); + var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}"); } } + + return Task.CompletedTask; } } @@ -729,10 +690,11 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - await E.Target.Tell($"^1{E.Origin.Name} ^3[PM]^7 - {E.Data}"); - await E.Origin.Tell($"To ^3{E.Target.Name} ^7-> {E.Data}"); + E.Target.Tell($"^1{E.Origin.Name} ^3[PM]^7 - {E.Data}"); + E.Origin.Tell($"To ^3{E.Target.Name} ^7-> {E.Data}"); + return Task.CompletedTask; } } @@ -757,15 +719,17 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - if (E.Target.Level >= E.Origin.Level) + var flagEvent = E.Target.Flag(E.Data, E.Origin); + + + if (E.FailReason == GameEvent.EventFailReason.Permission) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}"); - return; + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}"); } - if (E.Target.Level == Player.Permission.Flagged) + else if (E.FailReason == GameEvent.EventFailReason.Invalid) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}"); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}"); } else @@ -786,17 +750,7 @@ namespace SharedLibraryCore.Commands await E.Owner.Manager.GetPenaltyService().Create(newPenalty); - 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}"); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}"); } } @@ -815,36 +769,27 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - if (E.Target.Level >= E.Origin.Level) + var unflagEvent = E.Target.Unflag(E.Data, E.Origin); + + if (unflagEvent.FailReason == GameEvent.EventFailReason.Permission) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"]} ^5{E.Target.Name}"); - return; + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"]} ^5{E.Target.Name}"); } - if (E.Target.Level == Player.Permission.Flagged) + else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid) { - E.Target.Level = Player.Permission.User; - await E.Owner.Manager.GetClientService().Update(E.Target); - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}"); - - var e = new GameEvent() - { - Data = E.Data, - Origin = E.Origin, - Target = E.Target, - Owner = E.Owner, - Type = GameEvent.EventType.Unflag - }; - - E.Owner.Manager.GetEventHandler().AddEvent(e); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}"); } else { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]); } + + return Task.CompletedTask; + // todo: update immediately? } } @@ -867,61 +812,52 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override async Task ExecuteAsync(GameEvent commandEvent) { - if (E.Data.ToLower().Contains("camp")) + if (commandEvent.Data.ToLower().Contains("camp")) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_CAMP"]); + commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_CAMP"]); return; } - if (E.Owner.Reports.Find(x => (x.Origin == E.Origin && x.Target.NetworkId == E.Target.NetworkId)) != null) + var reportEvent = commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin); + + if (reportEvent.FailReason == GameEvent.EventFailReason.Permission) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]); - return; + commandEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"]} {commandEvent.Target.Name}"); } - if (E.Target == E.Origin) + else if (reportEvent.FailReason == GameEvent.EventFailReason.Invalid) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]); - return; + commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]); } - if (E.Target.Level > E.Origin.Level) + else if (reportEvent.Failed) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"]} {E.Target.Name}"); - return; + commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]); } - E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data)); - - Penalty newReport = new Penalty() + else { - Type = Penalty.PenaltyType.Report, - Expires = DateTime.UtcNow, - Offender = E.Target, - Offense = E.Data, - Punisher = E.Origin, - Active = true, - When = DateTime.UtcNow, - Link = E.Target.AliasLink - }; + commandEvent.Owner.Reports.Add(new Report(commandEvent.Target, commandEvent.Origin, commandEvent.Data)); - await E.Owner.Manager.GetPenaltyService().Create(newReport); + Penalty newReport = new Penalty() + { + Type = Penalty.PenaltyType.Report, + Expires = DateTime.UtcNow, + Offender = commandEvent.Target, + Offense = commandEvent.Data, + Punisher = commandEvent.Origin, + Active = true, + When = DateTime.UtcNow, + Link = commandEvent.Target.AliasLink + }; - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]); + await commandEvent.Owner.Manager.GetPenaltyService().Create(newReport); - 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)); + commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]); + commandEvent.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", commandEvent.Origin.Name, commandEvent.Target.Name, commandEvent.Data)); + } } } @@ -938,26 +874,27 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { if (E.Data != null && E.Data.ToLower().Contains(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_CLEAR"])) { E.Owner.Reports = new List(); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_CLEAR_SUCCESS"]); - return; + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_CLEAR_SUCCESS"]); + return Task.CompletedTask; } if (E.Owner.Reports.Count < 1) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_NONE"]); - return; + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_NONE"]); + return Task.CompletedTask; } foreach (Report R in E.Owner.Reports) { - await E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason)); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason)); } + + return Task.CompletedTask; } } @@ -972,12 +909,12 @@ namespace SharedLibraryCore.Commands if (E.Origin.Masked) { E.Origin.Masked = false; - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_OFF"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_OFF"]); } else { E.Origin.Masked = true; - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_ON"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_ON"]); } await E.Owner.Manager.GetClientService().Update(E.Origin); @@ -1005,16 +942,15 @@ namespace SharedLibraryCore.Commands if (penalty == null) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_NONE"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_NONE"]); return; } string timeRemaining = penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires - DateTime.UtcNow).TimeSpanText()} remaining)" : ""; string success = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_SUCCESS"]; - await E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}"); + E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}"); } - } public class CListAlias : Command @@ -1030,22 +966,24 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { StringBuilder message = new StringBuilder(); var names = new List(E.Target.AliasLink.Children.Select(a => a.Name)); var IPs = new List(E.Target.AliasLink.Children.Select(a => a.IPAddress.ConvertIPtoString()).Distinct()); - await E.Target.Tell($"[^3{E.Target}^7]"); + E.Target.Tell($"[^3{E.Target}^7]"); message.Append($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_ALIASES"]}: "); message.Append(String.Join(" | ", names)); - await E.Origin.Tell(message.ToString()); + E.Origin.Tell(message.ToString()); message.Clear(); message.Append($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_IPS"]}: "); message.Append(String.Join(" | ", IPs)); - await E.Origin.Tell(message.ToString()); + E.Origin.Tell(message.ToString()); + + return Task.CompletedTask; } } @@ -1066,9 +1004,9 @@ namespace SharedLibraryCore.Commands { var Response = await E.Owner.ExecuteCommandAsync(E.Data.Trim()); foreach (string S in Response) - await E.Origin.Tell(S.StripColors()); + E.Origin.Tell(S.StripColors()); if (Response.Length == 0) - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_SUCCESS"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_SUCCESS"]); } } @@ -1078,14 +1016,14 @@ namespace SharedLibraryCore.Commands base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", Player.Permission.Administrator, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_LOADED"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_LOADED"]); foreach (var P in Plugins.PluginImporter.ActivePlugins) { - await E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author)); - await Task.Delay(FloodProtectionInterval); + E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author)); } + return Task.CompletedTask; } } @@ -1095,9 +1033,10 @@ namespace SharedLibraryCore.Commands base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", Player.Permission.User, false) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"]} ^5{E.Origin.IPAddressString}"); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"]} ^5{E.Origin.IPAddressString}"); + return Task.CompletedTask; } } @@ -1129,12 +1068,12 @@ namespace SharedLibraryCore.Commands catch (FormatException) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_FAIL"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_FAIL"]); return; } List inactiveUsers = null; - + // todo: make an event for this // update user roles using (var context = new DatabaseContext()) { @@ -1146,8 +1085,7 @@ namespace SharedLibraryCore.Commands inactiveUsers.ForEach(c => c.Level = Player.Permission.User); await context.SaveChangesAsync(); } - await E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}"); - + E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}"); } } @@ -1167,7 +1105,7 @@ namespace SharedLibraryCore.Commands { if (E.Data.Length < 5) { - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_FAIL"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_FAIL"]); return; } @@ -1181,7 +1119,7 @@ namespace SharedLibraryCore.Commands E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].PasswordSalt = hashedPassword[1]; await E.Owner.Manager.GetClientService().Update(E.Origin); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]); } } @@ -1212,7 +1150,7 @@ namespace SharedLibraryCore.Commands if (currentProcess == null) { - await E.Origin.Tell("Could not find running/stalled instance of IW4x"); + E.Origin.Tell("Could not find running/stalled instance of IW4x"); } else @@ -1230,7 +1168,7 @@ namespace SharedLibraryCore.Commands catch (Exceptions.NetworkException) { - await E.Origin.Tell("Unable to cleanly shutdown server, forcing"); + E.Origin.Tell("Unable to cleanly shutdown server, forcing"); } if (!currentProcess.HasExited) @@ -1238,11 +1176,11 @@ namespace SharedLibraryCore.Commands try { currentProcess.Kill(); - await E.Origin.Tell("Successfully killed server process"); + E.Origin.Tell("Successfully killed server process"); } catch (Exception e) { - await E.Origin.Tell("Could not kill server process"); + E.Origin.Tell("Could not kill server process"); E.Owner.Logger.WriteDebug("Unable to kill process"); E.Owner.Logger.WriteDebug($"Exception: {e.Message}"); return; @@ -1265,22 +1203,24 @@ namespace SharedLibraryCore.Commands }) { } - public override async Task ExecuteAsync(GameEvent E) + public override Task ExecuteAsync(GameEvent E) { if (E.Message.IsBroadcastCommand()) { if (E.Target == null) - await E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms"); + E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms"); else - await E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms"); + E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms"); } else { if (E.Target == null) - await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms"); + E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms"); else - await E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms"); + E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms"); } + + return Task.CompletedTask; } } @@ -1323,7 +1263,7 @@ namespace SharedLibraryCore.Commands ctx.EFMeta.Add(gravatarMeta); await ctx.SaveChangesAsync(); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]); return; } } @@ -1339,7 +1279,7 @@ namespace SharedLibraryCore.Commands } await ctx.SaveChangesAsync(); - await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_UPDATE"]); + E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_UPDATE"]); } } } @@ -1402,7 +1342,7 @@ namespace SharedLibraryCore.Commands public override async Task ExecuteAsync(GameEvent E) { - await E.Origin.Tell(await GetNextMap(E.Owner)); + E.Origin.Tell(await GetNextMap(E.Owner)); } } -} +} \ No newline at end of file diff --git a/SharedLibraryCore/Events/GameEvent.cs b/SharedLibraryCore/Events/GameEvent.cs index 2e34741b6..fa150ef89 100644 --- a/SharedLibraryCore/Events/GameEvent.cs +++ b/SharedLibraryCore/Events/GameEvent.cs @@ -7,6 +7,28 @@ namespace SharedLibraryCore { public class GameEvent { + public enum EventFailReason + { + /// + /// event execution did not fail + /// + None, + /// + /// an internal exception prevented the event + /// from executing + /// + Exception, + /// + /// event origin didn't have the necessary privileges + /// to execute the command + /// + Permission, + /// + /// executing the event would cause an invalid state + /// + Invalid + } + public enum EventType { /// @@ -52,75 +74,83 @@ namespace SharedLibraryCore /// /// a client sent a message /// - Say, + Say = 100, /// /// a client was warned /// - Warn, + Warn = 101, + /// + /// all warnings for a client were cleared + /// + WarnClear = 102, /// /// a client was reported /// - Report, + Report = 103, /// /// a client was flagged /// - Flag, + Flag = 104, /// /// a client was unflagged /// - Unflag, + Unflag = 105, /// /// a client was kicked /// - Kick, + Kick = 106, /// /// a client was tempbanned /// - TempBan, + TempBan = 107, /// /// a client was banned /// - Ban, + Ban = 108, + /// + /// a client was unbanned + /// + Unban = 109, /// /// a client entered a command /// - Command, + Command = 110, /// /// a client's permission was changed /// - ChangePermission, + ChangePermission = 111, // events "generated" by IW4MAdmin /// /// a message is sent to all clients /// - Broadcast, + Broadcast = 200, /// /// a message is sent to a specific client /// - Tell, + Tell = 201, // events "generated" by script/log /// /// AC Damage Log /// - ScriptDamage, + ScriptDamage = 300, /// /// AC Kill Log /// - ScriptKill, + ScriptKill = 301, /// /// damage info printed out by game script /// - Damage, + Damage = 302, /// /// kill info printed out by game script /// - Kill, + Kill = 303, /// /// team info printed out by game script /// - JoinTeam, + JoinTeam = 304, } static long NextEventId; @@ -144,12 +174,18 @@ namespace SharedLibraryCore public ManualResetEventSlim OnProcessed { get; set; } public DateTime Time { get; set; } public long Id { get; private set; } + public EventFailReason FailReason { get; set; } + public bool Failed => FailReason != EventFailReason.None; /// /// asynchronously wait for GameEvent to be processed /// /// waitable task - public Task WaitAsync(int timeOut = int.MaxValue) => Task.FromResult(OnProcessed.Wait(timeOut)); + public Task WaitAsync(int timeOut = int.MaxValue) => Task.Run(() => + { + OnProcessed.Wait(timeOut); + return !Failed; + }); /// /// determine whether an event should be delayed or not diff --git a/SharedLibraryCore/Exceptions/NetworkException.cs b/SharedLibraryCore/Exceptions/NetworkException.cs index b4015d24e..237715560 100644 --- a/SharedLibraryCore/Exceptions/NetworkException.cs +++ b/SharedLibraryCore/Exceptions/NetworkException.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Sockets; using System.Text; using System.Threading.Tasks; @@ -9,5 +10,9 @@ namespace SharedLibraryCore.Exceptions public class NetworkException : ServerException { public NetworkException(string msg) : base(msg) { } + public NetworkException(string msg, Socket s) : base(msg) + { + this.Data.Add("socket", s); + } } } diff --git a/SharedLibraryCore/Migrations/20180922231600_ReaddACSnapshot.cs b/SharedLibraryCore/Migrations/20180922231600_ReaddACSnapshot.cs index 3050d297e..fe7e907f5 100644 --- a/SharedLibraryCore/Migrations/20180922231600_ReaddACSnapshot.cs +++ b/SharedLibraryCore/Migrations/20180922231600_ReaddACSnapshot.cs @@ -91,12 +91,12 @@ namespace SharedLibraryCore.Migrations principalColumn: "SnapshotId", onDelete: ReferentialAction.Restrict); - } + migrationBuilder.CreateIndex( + name: "IX_Vector3_EFACSnapshotSnapshotId", + table: "Vector3", + column: "EFACSnapshotSnapshotId"); - migrationBuilder.CreateIndex( - name: "IX_Vector3_EFACSnapshotSnapshotId", - table: "Vector3", - column: "EFACSnapshotSnapshotId"); + } migrationBuilder.CreateIndex( name: "IX_EFACSnapshot_ClientId", diff --git a/SharedLibraryCore/Objects/Player.cs b/SharedLibraryCore/Objects/Player.cs index ab2bf2df8..e15400c4a 100644 --- a/SharedLibraryCore/Objects/Player.cs +++ b/SharedLibraryCore/Objects/Player.cs @@ -86,53 +86,302 @@ namespace SharedLibraryCore.Objects public override string ToString() => $"{Name}::{NetworkId}"; - public String GetLastConnection() + /// + /// send a message directly to the connected client + /// + /// message content to send to client + public GameEvent Tell(String message) { - return Utilities.GetTimePassed(LastConnection); + var e = new GameEvent() + { + Message = message, + Target = this, + Owner = CurrentServer, + Type = GameEvent.EventType.Tell, + Data = message + }; + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } - public async Task Tell(String Message) + /// + /// warn a client with given reason + /// + /// reason for warn + /// client performing the warn + public GameEvent Warn(String warnReason, Player sender) { - // this is console or remote so send immediately - if (ClientNumber < 0) + var e = new GameEvent() { - await CurrentServer.Tell(Message, this); + Type = GameEvent.EventType.Warn, + Message = warnReason, + Origin = sender, + Target = this, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; } - else + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; + } + + /// + /// report a client for a given reason + /// + /// reason for the report + /// client performing the report + /// + public GameEvent Report(string reportReason, Player sender) + { + var e = new GameEvent() { - var e = new GameEvent() - { - Message = Message, - Target = this, - Owner = CurrentServer, - Type = GameEvent.EventType.Tell, - Data = Message - }; + Type = GameEvent.EventType.Report, + Message = reportReason, + Data = reportReason, + Origin = sender, + Target = this, + Owner = this.CurrentServer + }; - CurrentServer.Manager.GetEventHandler().AddEvent(e); + if (this.Level < sender.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; } + + if (this == sender) + { + e.FailReason = GameEvent.EventFailReason.Invalid; + return e; + } + + if (CurrentServer.Reports.Count(rep => (rep.Origin == sender && + rep.Target.NetworkId == this.NetworkId)) > 0) + { + e.FailReason = GameEvent.EventFailReason.Exception; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } - public async Task Kick(String Message, Player Sender) + /// + /// clear all warnings for a client + /// + /// client performing the warn clear + /// + public GameEvent WarnClear(Player sender) { - await CurrentServer.Kick(Message, this, Sender); + var e = new GameEvent() + { + Type = GameEvent.EventType.WarnClear, + Origin = sender, + Target = this, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + this.Warnings = 0; + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } - public async Task TempBan(String Message, TimeSpan Length, Player Sender) + /// + /// flag a client for a given reason + /// + /// reason for flagging + /// client performing the flag + /// game event for the flag + public GameEvent Flag(string flagReason, Player sender) { - await CurrentServer.TempBan(Message, Length, this, Sender); + var e = new GameEvent() + { + Type = GameEvent.EventType.Flag, + Origin = sender, + Data = flagReason, + Message = flagReason, + Owner = this.CurrentServer + }; + + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + if (this.Level == Player.Permission.Flagged) + { + e.FailReason = GameEvent.EventFailReason.Invalid; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } - public async Task Warn(String Message, Player Sender) + /// + /// unflag a client for a given reason + /// + /// reason to unflag a player for + /// client performing the unflag + /// game event for the un flug + public GameEvent Unflag(string unflagReason, Player sender) { - await CurrentServer.Warn(Message, this, Sender); + var e = new GameEvent() + { + Type = GameEvent.EventType.Unflag, + Origin = sender, + Data = unflagReason, + Message = unflagReason, + Owner = this.CurrentServer + }; + + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + if (this.Level != Player.Permission.Flagged) + { + e.FailReason = GameEvent.EventFailReason.Invalid; + return e; + } + + this.Level = Permission.User; + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } - public async Task Ban(String Message, Player Sender) + /// + /// kick a client for the given reason + /// + /// reason to kick for + /// client performing the kick + public GameEvent Kick(String kickReason, Player sender) { - await CurrentServer.Ban(Message, this, Sender); + var e = new GameEvent() + { + Type = GameEvent.EventType.Kick, + Message = kickReason, + Target = this, + Origin = sender, + Data = kickReason, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; } + + /// + /// temporarily ban a client for the given time span + /// + /// reason for the temp ban + /// how long the temp ban lasts + /// client performing the tempban + public GameEvent TempBan(String tempbanReason, TimeSpan banLength, Player sender) + { + var e = new GameEvent() + { + Type = GameEvent.EventType.TempBan, + Message = tempbanReason, + Origin = sender, + Target = this, + Extra = banLength, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; + } + + /// + /// permanently ban a client + /// + /// reason for the ban + /// client performing the ban + public GameEvent Ban(String banReason, Player sender) + { + var e = new GameEvent() + { + Type = GameEvent.EventType.Ban, + Message = banReason, + Origin = sender, + Target = this, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; + } + + /// + /// unban a client + /// + /// reason for the unban + /// client performing the unban + /// + public GameEvent Unban(String unbanReason, Player sender) + { + var e = new GameEvent() + { + Type = GameEvent.EventType.Unban, + Message = unbanReason, + Data = unbanReason, + Origin = sender, + Target = this, + Owner = this.CurrentServer + }; + + // enforce level restrictions + if (sender.Level <= this.Level) + { + e.FailReason = GameEvent.EventFailReason.Permission; + return e; + } + + CurrentServer.Manager.GetEventHandler().AddEvent(e); + return e; + } + [NotMapped] Dictionary _additionalProperties; public T GetAdditionalProperty(string name) => (T)_additionalProperties[name]; diff --git a/SharedLibraryCore/RCon/Connection.cs b/SharedLibraryCore/RCon/Connection.cs index c476bf9f4..6dc4dd36f 100644 --- a/SharedLibraryCore/RCon/Connection.cs +++ b/SharedLibraryCore/RCon/Connection.cs @@ -13,145 +13,44 @@ namespace SharedLibraryCore.RCon { class ConnectionState { - public Socket Client { get; private set; } - public int BufferSize { get; private set; } - public byte[] Buffer { get; private set; } - - public StringBuilder ResponseString { get; } - - public ConnectionState(Socket cl) - { - BufferSize = 8192; - Buffer = new byte[BufferSize]; - Client = cl; - ResponseString = new StringBuilder(); - } + public int ConnectionAttempts { get; set; } + const int BufferSize = 4096; + public readonly byte[] ReceiveBuffer = new byte[BufferSize]; + public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1); } public class Connection { + static readonly ConcurrentDictionary ActiveQueries = new ConcurrentDictionary(); public IPEndPoint Endpoint { get; private set; } public string RConPassword { get; private set; } ILogger Log; - int FailedSends; - int FailedReceives; - string response; - - ManualResetEvent OnConnected; - ManualResetEvent OnSent; - ManualResetEvent OnReceived; public Connection(string ipAddress, int port, string password, ILogger log) { Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); RConPassword = password; Log = log; - - OnConnected = new ManualResetEvent(false); - OnSent = new ManualResetEvent(false); - OnReceived = new ManualResetEvent(false); - } - - private void OnConnectedCallback(IAsyncResult ar) - { - var serverSocket = (Socket)ar.AsyncState; - - try - { - serverSocket.EndConnect(ar); -#if DEBUG - Log.WriteDebug($"Successfully initialized socket to {serverSocket.RemoteEndPoint}"); -#endif - OnConnected.Set(); - } - - catch (SocketException e) - { - throw new NetworkException($"Could not initialize socket for RCon - {e.Message}"); - } - } - - private void OnSentCallback(IAsyncResult ar) - { - Socket serverConnection = (Socket)ar.AsyncState; - - try - { - int sentByteNum = serverConnection.EndSend(ar); -#if DEBUG - Log.WriteDebug($"Sent {sentByteNum} bytes to {serverConnection.RemoteEndPoint}"); -#endif - // this is where we override our await to make it - OnSent.Set(); - } - - catch (Exception) - { - } - } - - private void OnReceivedCallback(IAsyncResult ar) - { - var connectionState = (ConnectionState)ar.AsyncState; - var serverConnection = connectionState.Client; - - try - { - int bytesRead = serverConnection.EndReceive(ar); - - if (bytesRead > 0) - { -#if DEBUG - Log.WriteDebug($"Received {bytesRead} bytes from {serverConnection.RemoteEndPoint}"); -#endif - FailedReceives = 0; - connectionState.ResponseString.Append(Utilities.EncodingType.GetString(connectionState.Buffer, 0, bytesRead).TrimEnd('\0') + '\n'); - - if (!connectionState.Buffer.Take(4).ToArray().SequenceEqual(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF })) - throw new NetworkException("Unexpected packet received"); - - /* if (FailedReceives == 0 && serverConnection.Available > 0) - { - serverConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0, - new AsyncCallback(OnReceivedCallback), connectionState); - } - else*/ - { - response = connectionState.ResponseString.ToString(); - OnReceived.Set(); - } - } - else - { - response = connectionState.ResponseString.ToString(); - OnReceived.Set(); - } - } - - catch (SocketException) - { - - } - - catch (ObjectDisposedException) - { - // Log.WriteWarning($"Tried to check for more available bytes for disposed socket on {Endpoint}"); - } } public async Task SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true) { - //// will this really prevent flooding? - //if ((DateTime.Now - LastQuery).TotalMilliseconds < 350) - //{ - // Thread.Sleep(350); - // //await Task.Delay(350); - //} + if (!ActiveQueries.ContainsKey(this.Endpoint)) + { + ActiveQueries.TryAdd(this.Endpoint, new ConnectionState()); + } - // LastQuery = DateTime.Now; + var connectionState = ActiveQueries[this.Endpoint]; + +#if DEBUG == true + Log.WriteDebug($"Waiting for semaphore to be released [${this.Endpoint}]"); +#endif + await connectionState.OnComplete.WaitAsync(); + +#if DEBUG == true + Log.WriteDebug($"Semaphore has been released [${this.Endpoint}]"); +#endif - OnSent.Reset(); - OnReceived.Reset(); byte[] payload = null; switch (type) @@ -171,130 +70,103 @@ namespace SharedLibraryCore.RCon break; } - using (var socketConnection = new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp) { ExclusiveAddressUse = true } ) + byte[] response = null; + retrySend: +#if DEBUG == true + Log.WriteDebug($"Sending {payload.Length} bytes to [${this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})"); +#endif + try { - socketConnection.BeginConnect(Endpoint, new AsyncCallback(OnConnectedCallback), socketConnection); - - retrySend: - try - { -#if DEBUG - Console.WriteLine($"Sending Command {parameters}"); -#endif - if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout)) - throw new SocketException((int)SocketError.TimedOut); - - socketConnection.BeginSend(payload, 0, payload.Length, 0, new AsyncCallback(OnSentCallback), socketConnection); - bool success = await Task.FromResult(OnSent.WaitOne(StaticHelpers.SocketTimeout)); - - if (!success) - { - FailedSends++; -#if DEBUG - Log.WriteDebug($"{FailedSends} failed sends to {socketConnection.RemoteEndPoint.ToString()}"); -#endif - if (FailedSends < 4) - goto retrySend; - else if (FailedSends == 4) - Log.WriteError($"Failed to send data to {socketConnection.RemoteEndPoint}"); - } - - else - { - if (FailedSends >= 4) - { - Log.WriteVerbose($"Resumed send RCon connection with {socketConnection.RemoteEndPoint}"); - FailedSends = 0; - } - } - } - - catch (SocketException e) - { - // this result is normal if the server is not listening - if (e.NativeErrorCode != (int)SocketError.ConnectionReset && - e.NativeErrorCode != (int)SocketError.TimedOut) - throw new NetworkException($"Unexpected error while sending data to server - {e.Message}"); - } - - if (!waitForResponse) - return await Task.FromResult(new string[] { "" }); - - var connectionState = new ConnectionState(socketConnection); - - retryReceive: - try - { - socketConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0, - new AsyncCallback(OnReceivedCallback), connectionState); - bool success = await Task.FromResult(OnReceived.WaitOne(StaticHelpers.SocketTimeout)); - - if (!success) - { - - FailedReceives++; -#if DEBUG - Log.WriteDebug($"{FailedReceives} failed receives from {socketConnection.RemoteEndPoint.ToString()}"); -#endif - if (FailedReceives < 4) - goto retrySend; - else if (FailedReceives == 4) - { - // Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries"); - } - - if (FailedReceives >= 4) - { - throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} {socketConnection.RemoteEndPoint.ToString()}"); - } - } - - else - { - if (FailedReceives >= 4) - { - Log.WriteVerbose($"Resumed receive RCon connection from {socketConnection.RemoteEndPoint.ToString()}"); - FailedReceives = 0; - } - } - } - - catch (SocketException e) - { - // this result is normal if the server is not listening - if (e.NativeErrorCode != (int)SocketError.ConnectionReset && - e.NativeErrorCode != (int)SocketError.TimedOut) - throw new NetworkException($"Unexpected error while receiving data from server - {e.Message}"); - else if (FailedReceives < 4) - { - goto retryReceive; - } - - else if (FailedReceives == 4) - { - // Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries"); - } - - if (FailedReceives >= 4) - { - throw new NetworkException(e.Message); - } - } - - string queryResponse = response; - - if (queryResponse.Contains("Invalid password")) - throw new NetworkException("RCON password is invalid"); - if (queryResponse.ToString().Contains("rcon_password")) - throw new NetworkException("RCON password has not been set"); - - string[] splitResponse = queryResponse.Split(new char[] - { - '\n' - }, StringSplitOptions.RemoveEmptyEntries) - .Select(line => line.Trim()).ToArray(); - return splitResponse; + response = await SendPayloadAsync(payload); } + + catch (Exception ex) + { + if(connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails) + { + connectionState.ConnectionAttempts++; + Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})"); + await Task.Delay(StaticHelpers.SocketTimeout.Milliseconds); + goto retrySend; + } + ActiveQueries.TryRemove(this.Endpoint, out _); + Log.WriteDebug(ex.GetExceptionInfo()); + throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]"); + } + + ActiveQueries.TryRemove(this.Endpoint, out _); + + string responseString = Utilities.EncodingType.GetString(response, 0, response.Length).TrimEnd('\0') + '\n'; + + if (responseString.Contains("Invalid password")) + { + // todo: localize this + throw new NetworkException("RCON password is invalid"); + } + + if (responseString.ToString().Contains("rcon_password")) + { + throw new NetworkException("RCON password has not been set"); + } + + string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries) + .Select(line => line.Trim()).ToArray(); + return splitResponse; + } + + private async Task SendPayloadAsync(byte[] payload) + { + var rconSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) + { + DontFragment = true, + Ttl = 42, + ExclusiveAddressUse = true + }; + + var connectionState = ActiveQueries[this.Endpoint]; + + var outgoingDataArgs = new SocketAsyncEventArgs() + { + RemoteEndPoint = this.Endpoint + }; + outgoingDataArgs.SetBuffer(payload); + outgoingDataArgs.Completed += OnDataSent; + + // send the data to the server + bool sendDataPending = rconSocket.SendToAsync(outgoingDataArgs); + + var incomingDataArgs = new SocketAsyncEventArgs() + { + RemoteEndPoint = this.Endpoint + }; + incomingDataArgs.SetBuffer(connectionState.ReceiveBuffer); + incomingDataArgs.Completed += OnDataReceived; + + // get our response back + rconSocket.ReceiveFromAsync(incomingDataArgs); + + if (!await connectionState.OnComplete.WaitAsync(StaticHelpers.SocketTimeout.Milliseconds)) + { + throw new NetworkException("Timed out waiting for response", rconSocket); + } + + byte[] response = connectionState.ReceiveBuffer; + return response; + } + + private void OnDataReceived(object sender, SocketAsyncEventArgs e) + { +#if DEBUG == true + Log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}"); +#endif + ActiveQueries[this.Endpoint].OnComplete.Release(1); + } + + private void OnDataSent(object sender, SocketAsyncEventArgs e) + { +#if DEBUG == true + Log.WriteDebug($"Sent {e.Buffer.Length} bytes to {e.ConnectSocket.RemoteEndPoint.ToString()}"); +#endif } } } diff --git a/SharedLibraryCore/RCon/StaticHelpers.cs b/SharedLibraryCore/RCon/StaticHelpers.cs index 76c2e1625..2d63227d9 100644 --- a/SharedLibraryCore/RCon/StaticHelpers.cs +++ b/SharedLibraryCore/RCon/StaticHelpers.cs @@ -39,10 +39,11 @@ namespace SharedLibraryCore.RCon /// /// timeout in seconds to wait for a socket send or receive before giving up /// - public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 10); + public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 0, 0, 150); /// /// interval in milliseconds to wait before sending the next RCon request /// public static readonly int FloodProtectionInterval = 350; + public static readonly int AllowedConnectionFails = 3; } } diff --git a/SharedLibraryCore/Server.cs b/SharedLibraryCore/Server.cs index 45932e2bd..25f800888 100644 --- a/SharedLibraryCore/Server.cs +++ b/SharedLibraryCore/Server.cs @@ -113,28 +113,28 @@ namespace SharedLibraryCore /// /// Send a message to all players /// - /// Message to be sent to all players - public async Task Broadcast(String Message) + /// Message to be sent to all players + public GameEvent Broadcast(string message, Player sender = null) { #if DEBUG == false - string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}"); + string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}"); #else - Logger.WriteVerbose(Message.StripColors()); + Logger.WriteVerbose(message.StripColors()); #endif var e = new GameEvent() { Type = GameEvent.EventType.Broadcast, #if DEBUG == true - Data = Message, + Data = message, #else Data = formattedMessage, #endif Owner = this, + Origin = sender, }; Manager.GetEventHandler().AddEvent(e); - - await Task.CompletedTask; + return e; } /// @@ -142,7 +142,7 @@ namespace SharedLibraryCore /// /// Message to send /// Player to send message to - public async Task Tell(String Message, Player Target) + protected async Task Tell(String Message, Player Target) { #if !DEBUG string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}"); @@ -179,11 +179,11 @@ namespace SharedLibraryCore /// Send a message to all admins on the server /// /// Message to send out - public async Task ToAdmins(String message) + public void ToAdmins(String message) { foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged)) { - await client.Tell(message); + client.Tell(message); } } diff --git a/SharedLibraryCore/Services/GenericRepository.cs b/SharedLibraryCore/Services/GenericRepository.cs index 60abe2bce..6ae7727bc 100644 --- a/SharedLibraryCore/Services/GenericRepository.cs +++ b/SharedLibraryCore/Services/GenericRepository.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Text; using System.Threading.Tasks; namespace SharedLibraryCore.Services @@ -14,6 +13,14 @@ namespace SharedLibraryCore.Services { private DatabaseContext _context; private DbSet _dbSet; + private readonly bool ShouldTrack; + + public GenericRepository(bool shouldTrack) + { + this.ShouldTrack = shouldTrack; + } + + public GenericRepository() { } protected DbContext Context { @@ -21,7 +28,7 @@ namespace SharedLibraryCore.Services { if (_context == null) { - _context = new DatabaseContext(true); + _context = new DatabaseContext(ShouldTrack); } return _context;