fix intermittent issue with game interface during connection loss with servers
This commit is contained in:
parent
dd8c4f438f
commit
1f13f9122c
@ -24,7 +24,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jint" Version="3.0.0-beta-2037" />
|
<PackageReference Include="Jint" Version="3.0.0-beta-2038" />
|
||||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
@ -792,8 +792,16 @@ namespace IW4MAdmin
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
async Task<List<EFClient>[]> PollPlayersAsync()
|
async Task<List<EFClient>[]> PollPlayersAsync()
|
||||||
{
|
{
|
||||||
|
var tokenSource = new CancellationTokenSource();
|
||||||
|
tokenSource.CancelAfter(TimeSpan.FromSeconds(5));
|
||||||
var currentClients = GetClientsAsList();
|
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();
|
var polledClients = statusResponse.Clients.AsEnumerable();
|
||||||
|
|
||||||
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
if (Manager.GetApplicationSettings().Configuration().IgnoreBots)
|
||||||
@ -930,6 +938,11 @@ namespace IW4MAdmin
|
|||||||
|
|
||||||
var polledClients = await PollPlayersAsync();
|
var polledClients = await PollPlayersAsync();
|
||||||
|
|
||||||
|
if (polledClients is null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var disconnectingClient in polledClients[1]
|
foreach (var disconnectingClient in polledClients[1]
|
||||||
.Where(client => !client.IsZombieClient /* ignores "fake" zombie clients */))
|
.Where(client => !client.IsZombieClient /* ignores "fake" zombie clients */))
|
||||||
{
|
{
|
||||||
|
12
Application/Misc/AsyncResult.cs
Normal file
12
Application/Misc/AsyncResult.cs
Normal file
@ -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; }
|
||||||
|
}
|
@ -276,8 +276,8 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
{
|
{
|
||||||
_logger.LogDebug("OnLoad executing for {Name}", Name);
|
_logger.LogDebug("OnLoad executing for {Name}", Name);
|
||||||
_scriptEngine.SetValue("_manager", manager);
|
_scriptEngine.SetValue("_manager", manager);
|
||||||
_scriptEngine.SetValue("getDvar", GetDvarAsync);
|
_scriptEngine.SetValue("getDvar", BeginGetDvar);
|
||||||
_scriptEngine.SetValue("setDvar", SetDvarAsync);
|
_scriptEngine.SetValue("setDvar", BeginSetDvar);
|
||||||
_scriptEngine.Evaluate("plugin.onLoadAsync(_manager)");
|
_scriptEngine.Evaluate("plugin.onLoadAsync(_manager)");
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@ -343,7 +343,8 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
/// <param name="commands">commands value from jint parser</param>
|
/// <param name="commands">commands value from jint parser</param>
|
||||||
/// <param name="scriptCommandFactory">factory to create the command from</param>
|
/// <param name="scriptCommandFactory">factory to create the command from</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private IEnumerable<IManagerCommand> GenerateScriptCommands(JsValue commands, IScriptCommandFactory scriptCommandFactory)
|
private IEnumerable<IManagerCommand> GenerateScriptCommands(JsValue commands,
|
||||||
|
IScriptCommandFactory scriptCommandFactory)
|
||||||
{
|
{
|
||||||
var commandList = new List<IManagerCommand>();
|
var commandList = new List<IManagerCommand>();
|
||||||
|
|
||||||
@ -410,7 +411,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
|
|
||||||
_scriptEngine.SetValue("_event", gameEvent);
|
_scriptEngine.SetValue("_event", gameEvent);
|
||||||
var jsEventObject = _scriptEngine.Evaluate("_event");
|
var jsEventObject = _scriptEngine.Evaluate("_event");
|
||||||
|
|
||||||
dynamicCommand.execute.Target.Invoke(_scriptEngine, jsEventObject);
|
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");
|
throw new PluginException("A runtime error occured while executing action for script plugin");
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
using (LogContext.PushProperty("Server", gameEvent.Owner?.ToString()))
|
using (LogContext.PushProperty("Server", gameEvent.Owner?.ToString()))
|
||||||
@ -454,83 +455,71 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
return commandList;
|
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();
|
var shouldRelease = false;
|
||||||
tokenSource.CancelAfter(TimeSpan.FromSeconds(5));
|
|
||||||
string result = null;
|
|
||||||
var success = true;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = server.GetDvarAsync<string>(dvarName, token: tokenSource.Token).GetAwaiter().GetResult().Value;
|
_onProcessing.Wait(tokenSource.Token);
|
||||||
}
|
shouldRelease = true;
|
||||||
catch
|
var (success, value) = (ValueTuple<bool, string>)result.AsyncState;
|
||||||
{
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_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,
|
onCompleted.DynamicInvoke(JsValue.Undefined,
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
JsValue.FromObject(_scriptEngine, server),
|
JsValue.FromObject(_scriptEngine, server),
|
||||||
JsValue.FromObject(_scriptEngine, dvarName),
|
JsValue.FromObject(_scriptEngine, dvarName),
|
||||||
JsValue.FromObject(_scriptEngine, dvarValue),
|
JsValue.FromObject(_scriptEngine, value),
|
||||||
JsValue.FromObject(_scriptEngine, success)
|
JsValue.FromObject(_scriptEngine, success)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (_onProcessing.CurrentCount == 0)
|
if (_onProcessing.CurrentCount == 0 && shouldRelease)
|
||||||
{
|
{
|
||||||
_onProcessing.Release();
|
_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
|
using IW4MAdmin.Application.Misc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using static SharedLibraryCore.Server;
|
using static SharedLibraryCore.Server;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
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<string>(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<IStatusResponse> GetStatusAsync(IRConConnection connection, CancellationToken token = default)
|
public virtual async Task<IStatusResponse> GetStatusAsync(IRConConnection connection, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS, "status", token);
|
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;
|
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<EFClient> ClientsFromStatus(string[] Status)
|
private List<EFClient> ClientsFromStatus(string[] Status)
|
||||||
{
|
{
|
||||||
List<EFClient> StatusPlayers = new List<EFClient>();
|
List<EFClient> StatusPlayers = new List<EFClient>();
|
||||||
|
@ -55,6 +55,8 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default);
|
Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default);
|
||||||
|
|
||||||
|
void BeginGetDvar(IRConConnection connection, string dvarName, AsyncCallback callback, CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// set value of DVAR by name
|
/// set value of DVAR by name
|
||||||
@ -65,6 +67,8 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue, CancellationToken token = default);
|
Task<bool> SetDvarAsync(IRConConnection connection, string dvarName, object dvarValue, CancellationToken token = default);
|
||||||
|
|
||||||
|
void BeginSetDvar(IRConConnection connection, string dvarName, object dvarValue, AsyncCallback callback, CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// executes a console command on the server
|
/// executes a console command on the server
|
||||||
|
@ -773,6 +773,11 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
return await server.RconParser.GetDvarAsync(server.RemoteConnection, dvarName, fallbackValue, token);
|
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<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName,
|
public static async Task<Dvar<T>> GetDvarAsync<T>(this Server server, string dvarName,
|
||||||
T fallbackValue = default)
|
T fallbackValue = default)
|
||||||
@ -808,6 +813,12 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
await server.RconParser.SetDvarAsync(server.RemoteConnection, dvarName, dvarValue, token);
|
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)
|
public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
|
||||||
{
|
{
|
||||||
@ -824,9 +835,17 @@ namespace SharedLibraryCore
|
|||||||
return await ExecuteCommandAsync(server, commandName, default);
|
return await ExecuteCommandAsync(server, commandName, default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Task<IStatusResponse> GetStatusAsync(this Server server, CancellationToken token)
|
public static async Task<IStatusResponse> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user