diff --git a/Application/Application.csproj b/Application/Application.csproj index 9a2f117d..ee381b80 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -24,7 +24,7 @@ - + all diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 57fc7381..0eda0f1c 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -792,8 +792,16 @@ namespace IW4MAdmin /// async Task[]> PollPlayersAsync() { + var tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(TimeSpan.FromSeconds(5)); var currentClients = GetClientsAsList(); - var statusResponse = await this.GetStatusAsync(Manager.CancellationToken); + var statusResponse = await this.GetStatusAsync(tokenSource.Token); + + if (statusResponse is null) + { + return null; + } + var polledClients = statusResponse.Clients.AsEnumerable(); if (Manager.GetApplicationSettings().Configuration().IgnoreBots) @@ -930,6 +938,11 @@ namespace IW4MAdmin var polledClients = await PollPlayersAsync(); + if (polledClients is null) + { + return true; + } + foreach (var disconnectingClient in polledClients[1] .Where(client => !client.IsZombieClient /* ignores "fake" zombie clients */)) { diff --git a/Application/Misc/AsyncResult.cs b/Application/Misc/AsyncResult.cs new file mode 100644 index 00000000..7fc1ec33 --- /dev/null +++ b/Application/Misc/AsyncResult.cs @@ -0,0 +1,12 @@ +using System; +using System.Threading; + +namespace IW4MAdmin.Application.Misc; + +public class AsyncResult : IAsyncResult +{ + public object AsyncState { get; set; } + public WaitHandle AsyncWaitHandle { get; set; } + public bool CompletedSynchronously { get; set; } + public bool IsCompleted { get; set; } +} diff --git a/Application/Misc/ScriptPlugin.cs b/Application/Misc/ScriptPlugin.cs index 6e5c6233..053d0e2d 100644 --- a/Application/Misc/ScriptPlugin.cs +++ b/Application/Misc/ScriptPlugin.cs @@ -276,8 +276,8 @@ namespace IW4MAdmin.Application.Misc { _logger.LogDebug("OnLoad executing for {Name}", Name); _scriptEngine.SetValue("_manager", manager); - _scriptEngine.SetValue("getDvar", GetDvarAsync); - _scriptEngine.SetValue("setDvar", SetDvarAsync); + _scriptEngine.SetValue("getDvar", BeginGetDvar); + _scriptEngine.SetValue("setDvar", BeginSetDvar); _scriptEngine.Evaluate("plugin.onLoadAsync(_manager)"); return Task.CompletedTask; @@ -343,7 +343,8 @@ namespace IW4MAdmin.Application.Misc /// commands value from jint parser /// factory to create the command from /// - private IEnumerable GenerateScriptCommands(JsValue commands, IScriptCommandFactory scriptCommandFactory) + private IEnumerable GenerateScriptCommands(JsValue commands, + IScriptCommandFactory scriptCommandFactory) { var commandList = new List(); @@ -410,7 +411,7 @@ namespace IW4MAdmin.Application.Misc _scriptEngine.SetValue("_event", gameEvent); var jsEventObject = _scriptEngine.Evaluate("_event"); - + dynamicCommand.execute.Target.Invoke(_scriptEngine, jsEventObject); } @@ -424,7 +425,7 @@ namespace IW4MAdmin.Application.Misc throw new PluginException("A runtime error occured while executing action for script plugin"); } - + catch (Exception ex) { using (LogContext.PushProperty("Server", gameEvent.Owner?.ToString())) @@ -454,83 +455,71 @@ namespace IW4MAdmin.Application.Misc return commandList; } - private void GetDvarAsync(Server server, string dvarName, Delegate onCompleted) + private void BeginGetDvar(Server server, string dvarName, Delegate onCompleted) { - Task.Run(() => + var tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(TimeSpan.FromSeconds(15)); + + server.BeginGetDvar(dvarName, result => { - var tokenSource = new CancellationTokenSource(); - tokenSource.CancelAfter(TimeSpan.FromSeconds(5)); - string result = null; - var success = true; + var shouldRelease = false; try { - result = server.GetDvarAsync(dvarName, token: tokenSource.Token).GetAwaiter().GetResult().Value; - } - catch - { - success = false; - } + _onProcessing.Wait(tokenSource.Token); + shouldRelease = true; + var (success, value) = (ValueTuple)result.AsyncState; - _onProcessing.Wait(); - try - { - onCompleted.DynamicInvoke(JsValue.Undefined, - new[] - { - JsValue.FromObject(_scriptEngine, server), - JsValue.FromObject(_scriptEngine, dvarName), - JsValue.FromObject(_scriptEngine, result), - JsValue.FromObject(_scriptEngine, success), - }); - } - - finally - { - if (_onProcessing.CurrentCount == 0) - { - _onProcessing.Release(); - } - } - }); - } - private void SetDvarAsync(Server server, string dvarName, string dvarValue, Delegate onCompleted) - { - Task.Run(() => - { - var tokenSource = new CancellationTokenSource(); - tokenSource.CancelAfter(TimeSpan.FromSeconds(5)); - var success = true; - - try - { - server.SetDvarAsync(dvarName, dvarValue, tokenSource.Token).GetAwaiter().GetResult(); - } - catch - { - success = false; - } - - _onProcessing.Wait(); - try - { onCompleted.DynamicInvoke(JsValue.Undefined, new[] { JsValue.FromObject(_scriptEngine, server), - JsValue.FromObject(_scriptEngine, dvarName), - JsValue.FromObject(_scriptEngine, dvarValue), + JsValue.FromObject(_scriptEngine, dvarName), + JsValue.FromObject(_scriptEngine, value), JsValue.FromObject(_scriptEngine, success) }); } finally { - if (_onProcessing.CurrentCount == 0) + if (_onProcessing.CurrentCount == 0 && shouldRelease) { _onProcessing.Release(); } } - }); + }, tokenSource.Token); + } + + private void BeginSetDvar(Server server, string dvarName, string dvarValue, Delegate onCompleted) + { + var tokenSource = new CancellationTokenSource(); + tokenSource.CancelAfter(TimeSpan.FromSeconds(15)); + + server.BeginSetDvar(dvarName, dvarValue, result => + { + var shouldRelease = false; + try + { + _onProcessing.Wait(tokenSource.Token); + shouldRelease = true; + var success = (bool)result.AsyncState; + + onCompleted.DynamicInvoke(JsValue.Undefined, + new[] + { + JsValue.FromObject(_scriptEngine, server), + JsValue.FromObject(_scriptEngine, dvarName), + JsValue.FromObject(_scriptEngine, dvarValue), + JsValue.FromObject(_scriptEngine, success) + }); + } + finally + { + if (_onProcessing.CurrentCount == 0 && shouldRelease) + { + _onProcessing.Release(); + } + } + }, tokenSource.Token); } } diff --git a/Application/RConParsers/BaseRConParser.cs b/Application/RConParsers/BaseRConParser.cs index 238ebb23..b98f66d2 100644 --- a/Application/RConParsers/BaseRConParser.cs +++ b/Application/RConParsers/BaseRConParser.cs @@ -10,6 +10,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Data.Models; +using IW4MAdmin.Application.Misc; using Microsoft.Extensions.Logging; using static SharedLibraryCore.Server; using ILogger = Microsoft.Extensions.Logging.ILogger; @@ -141,6 +142,30 @@ namespace IW4MAdmin.Application.RConParsers }; } + public void BeginGetDvar(IRConConnection connection, string dvarName, AsyncCallback callback, CancellationToken token = default) + { + GetDvarAsync(connection, dvarName, token: token).ContinueWith(action => + { + if (action.Exception is null) + { + callback?.Invoke(new AsyncResult + { + IsCompleted = true, + AsyncState = (true, action.Result.Value) + }); + } + + else + { + callback?.Invoke(new AsyncResult + { + IsCompleted = true, + AsyncState = (false, (string)null) + }); + } + }, token); + } + public virtual async Task GetStatusAsync(IRConConnection connection, CancellationToken token = default) { var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS, "status", token); @@ -196,6 +221,31 @@ namespace IW4MAdmin.Application.RConParsers return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, dvarString, token)).Length > 0; } + public void BeginSetDvar(IRConConnection connection, string dvarName, object dvarValue, AsyncCallback callback, + CancellationToken token = default) + { + SetDvarAsync(connection, dvarName, dvarValue, token).ContinueWith(action => + { + if (action.Exception is null) + { + callback?.Invoke(new AsyncResult + { + IsCompleted = true, + AsyncState = true + }); + } + + else + { + callback?.Invoke(new AsyncResult + { + IsCompleted = true, + AsyncState = false + }); + } + }, token); + } + private List ClientsFromStatus(string[] Status) { List StatusPlayers = new List(); diff --git a/SharedLibraryCore/Interfaces/IRConParser.cs b/SharedLibraryCore/Interfaces/IRConParser.cs index e879c5de..b080b2c4 100644 --- a/SharedLibraryCore/Interfaces/IRConParser.cs +++ b/SharedLibraryCore/Interfaces/IRConParser.cs @@ -55,6 +55,8 @@ namespace SharedLibraryCore.Interfaces /// /// Task> GetDvarAsync(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default); + + void BeginGetDvar(IRConConnection connection, string dvarName, AsyncCallback callback, CancellationToken token = default); /// /// set value of DVAR by name @@ -65,6 +67,8 @@ namespace SharedLibraryCore.Interfaces /// /// Task SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue, CancellationToken token = default); + + void BeginSetDvar(IRConConnection connection, string dvarName, object dvarValue, AsyncCallback callback, CancellationToken token = default); /// /// executes a console command on the server diff --git a/SharedLibraryCore/Utilities.cs b/SharedLibraryCore/Utilities.cs index 40ada560..b95e9565 100644 --- a/SharedLibraryCore/Utilities.cs +++ b/SharedLibraryCore/Utilities.cs @@ -773,6 +773,11 @@ namespace SharedLibraryCore { return await server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName, fallbackValue, token); } + + public static void BeginGetDvar(this Server server, string dvarName, AsyncCallback callback, CancellationToken token = default) + { + server.RconParser.BeginGetDvar(server.RemoteConnection, dvarName, callback, token); + } public static async Task> GetDvarAsync(this Server server, string dvarName, T fallbackValue = default) @@ -808,6 +813,12 @@ namespace SharedLibraryCore { await server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue, token); } + + public static void BeginSetDvar(this Server server, string dvarName, object dvarValue, + AsyncCallback callback, CancellationToken token = default) + { + server.RconParser.BeginSetDvar(server.RemoteConnection, dvarName, dvarValue, callback, token); + } public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue) { @@ -824,9 +835,17 @@ namespace SharedLibraryCore return await ExecuteCommandAsync(server, commandName, default); } - public static Task GetStatusAsync(this Server server, CancellationToken token) + public static async Task GetStatusAsync(this Server server, CancellationToken token) { - return server.RconParser.GetStatusAsync(server.RemoteConnection, token); + try + { + return await server.RconParser.GetStatusAsync(server.RemoteConnection, token); + } + + catch (TaskCanceledException) + { + return null; + } } ///