hopefully fix some issues with rcon socket
This commit is contained in:
parent
e6e56d8d14
commit
ef70496546
@ -50,37 +50,10 @@ namespace Integrations.Cod
|
|||||||
|
|
||||||
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "",
|
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "",
|
||||||
CancellationToken token = default)
|
CancellationToken token = default)
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
return await SendQueryAsyncInternal(type, parameters, token);
|
return await SendQueryAsyncInternal(type, parameters, token);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
_log.LogWarning("Timed out waiting for RCon response");
|
|
||||||
throw new RConException("Did not received RCon response in allocated time frame");
|
|
||||||
}
|
|
||||||
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
var state = ActiveQueries[Endpoint];
|
|
||||||
if (state.OnComplete.CurrentCount == 0)
|
|
||||||
{
|
|
||||||
state.OnComplete.Release(1);
|
|
||||||
state.ConnectionAttempts = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.OnReceivedData.CurrentCount == 0)
|
|
||||||
{
|
|
||||||
state.OnReceivedData.Release(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.OnSentData.CurrentCount == 0)
|
|
||||||
{
|
|
||||||
state.OnSentData.Release(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private async Task<string[]> SendQueryAsyncInternal(StaticHelpers.QueryType type, string parameters = "", CancellationToken token = default)
|
private async Task<string[]> SendQueryAsyncInternal(StaticHelpers.QueryType type, string parameters = "", CancellationToken token = default)
|
||||||
{
|
{
|
||||||
if (!ActiveQueries.ContainsKey(this.Endpoint))
|
if (!ActiveQueries.ContainsKey(this.Endpoint))
|
||||||
@ -93,14 +66,36 @@ namespace Integrations.Cod
|
|||||||
_log.LogDebug("Waiting for semaphore to be released [{Endpoint}]", Endpoint);
|
_log.LogDebug("Waiting for semaphore to be released [{Endpoint}]", Endpoint);
|
||||||
|
|
||||||
// enter the semaphore so only one query is sent at a time per server.
|
// enter the semaphore so only one query is sent at a time per server.
|
||||||
|
try
|
||||||
|
{
|
||||||
await connectionState.OnComplete.WaitAsync(token);
|
await connectionState.OnComplete.WaitAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw new RConException("Timed out waiting for access to rcon socket");
|
||||||
|
}
|
||||||
|
|
||||||
var timeSinceLastQuery = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
|
var timeSinceLastQuery = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
|
||||||
|
|
||||||
if (timeSinceLastQuery < config.FloodProtectInterval)
|
if (timeSinceLastQuery < config.FloodProtectInterval)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(config.FloodProtectInterval - (int)timeSinceLastQuery, token);
|
await Task.Delay(config.FloodProtectInterval - (int)timeSinceLastQuery, token);
|
||||||
}
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw new RConException("Timed out waiting for flood protect to expire");
|
||||||
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (connectionState.OnComplete.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
connectionState.OnComplete.Release(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectionState.LastQuery = DateTime.Now;
|
connectionState.LastQuery = DateTime.Now;
|
||||||
|
|
||||||
@ -125,13 +120,19 @@ namespace Integrations.Cod
|
|||||||
{
|
{
|
||||||
case StaticHelpers.QueryType.GET_DVAR:
|
case StaticHelpers.QueryType.GET_DVAR:
|
||||||
waitForResponse |= true;
|
waitForResponse |= true;
|
||||||
payload = string.Format(config.CommandPrefixes.RConGetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
payload = string
|
||||||
|
.Format(config.CommandPrefixes.RConGetDvar, convertedRConPassword,
|
||||||
|
convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.SET_DVAR:
|
case StaticHelpers.QueryType.SET_DVAR:
|
||||||
payload = string.Format(config.CommandPrefixes.RConSetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
payload = string
|
||||||
|
.Format(config.CommandPrefixes.RConSetDvar, convertedRConPassword,
|
||||||
|
convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.COMMAND:
|
case StaticHelpers.QueryType.COMMAND:
|
||||||
payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
payload = string
|
||||||
|
.Format(config.CommandPrefixes.RConCommand, convertedRConPassword,
|
||||||
|
convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.GET_STATUS:
|
case StaticHelpers.QueryType.GET_STATUS:
|
||||||
waitForResponse |= true;
|
waitForResponse |= true;
|
||||||
@ -143,7 +144,8 @@ namespace Integrations.Cod
|
|||||||
break;
|
break;
|
||||||
case StaticHelpers.QueryType.COMMAND_STATUS:
|
case StaticHelpers.QueryType.COMMAND_STATUS:
|
||||||
waitForResponse |= true;
|
waitForResponse |= true;
|
||||||
payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray();
|
payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0")
|
||||||
|
.Select(Convert.ToByte).ToArray();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +154,7 @@ namespace Integrations.Cod
|
|||||||
// e.g: emoji -> windows-1252
|
// e.g: emoji -> windows-1252
|
||||||
catch (OverflowException ex)
|
catch (OverflowException ex)
|
||||||
{
|
{
|
||||||
connectionState.OnComplete.Release(1);
|
|
||||||
using (LogContext.PushProperty("Server", Endpoint.ToString()))
|
using (LogContext.PushProperty("Server", Endpoint.ToString()))
|
||||||
{
|
{
|
||||||
_log.LogError(ex, "Could not convert RCon data payload to desired encoding {encoding} {params}",
|
_log.LogError(ex, "Could not convert RCon data payload to desired encoding {encoding} {params}",
|
||||||
@ -162,6 +164,14 @@ namespace Integrations.Cod
|
|||||||
throw new RConException($"Invalid character encountered when converting encodings");
|
throw new RConException($"Invalid character encountered when converting encodings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (connectionState.OnComplete.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
connectionState.OnComplete.Release(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byte[][] response = null;
|
byte[][] response = null;
|
||||||
|
|
||||||
retrySend:
|
retrySend:
|
||||||
@ -170,7 +180,7 @@ namespace Integrations.Cod
|
|||||||
using (LogContext.PushProperty("Server", Endpoint.ToString()))
|
using (LogContext.PushProperty("Server", Endpoint.ToString()))
|
||||||
{
|
{
|
||||||
_log.LogInformation(
|
_log.LogInformation(
|
||||||
"Retrying RCon message ({ConnectionAttempts}/{AllowedConnectionFailures} attempts) with parameters {payload}",
|
"Retrying RCon message ({ConnectionAttempts}/{AllowedConnectionFailures} attempts) with parameters {Payload}",
|
||||||
connectionState.ConnectionAttempts,
|
connectionState.ConnectionAttempts,
|
||||||
_retryAttempts, parameters);
|
_retryAttempts, parameters);
|
||||||
}
|
}
|
||||||
@ -184,8 +194,46 @@ namespace Integrations.Cod
|
|||||||
{
|
{
|
||||||
connectionState.SendEventArgs.UserToken = socket;
|
connectionState.SendEventArgs.UserToken = socket;
|
||||||
connectionState.ConnectionAttempts++;
|
connectionState.ConnectionAttempts++;
|
||||||
|
|
||||||
|
// wait for send to complete
|
||||||
|
try
|
||||||
|
{
|
||||||
await connectionState.OnSentData.WaitAsync(token);
|
await connectionState.OnSentData.WaitAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw new RConException("Timed out waiting for access to RCon send socket");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (connectionState.OnComplete.CurrentCount == 0 )
|
||||||
|
{
|
||||||
|
connectionState.OnComplete.Release(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for receive to complete
|
||||||
|
try
|
||||||
|
{
|
||||||
await connectionState.OnReceivedData.WaitAsync(token);
|
await connectionState.OnReceivedData.WaitAsync(token);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw new RConException("Timed out waiting for access to RCon receive socket");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (connectionState.OnComplete.CurrentCount == 0 )
|
||||||
|
{
|
||||||
|
connectionState.OnComplete.Release(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connectionState.OnSentData.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
connectionState.OnSentData.Release(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectionState.BytesReadPerSegment.Clear();
|
connectionState.BytesReadPerSegment.Clear();
|
||||||
var exceptionCaught = false;
|
var exceptionCaught = false;
|
||||||
|
|
||||||
@ -218,7 +266,16 @@ namespace Integrations.Cod
|
|||||||
if (connectionState.ConnectionAttempts < _retryAttempts)
|
if (connectionState.ConnectionAttempts < _retryAttempts)
|
||||||
{
|
{
|
||||||
exceptionCaught = true;
|
exceptionCaught = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
await Task.Delay(StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts), token);
|
await Task.Delay(StaticHelpers.SocketTimeout(connectionState.ConnectionAttempts), token);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
throw new RConException("Timed out waiting on delay retry");
|
||||||
|
}
|
||||||
|
|
||||||
goto retrySend;
|
goto retrySend;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +349,7 @@ namespace Integrations.Cod
|
|||||||
throw new RConException("Unexpected response header from server");
|
throw new RConException("Unexpected response header from server");
|
||||||
}
|
}
|
||||||
|
|
||||||
var splitResponse = headerSplit.Last().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
var splitResponse = headerSplit.Last().Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
return splitResponse;
|
return splitResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +453,14 @@ namespace Integrations.Cod
|
|||||||
_log.LogWarning("Socket timed out while sending RCon data on attempt {Attempt}",
|
_log.LogWarning("Socket timed out while sending RCon data on attempt {Attempt}",
|
||||||
connectionState.ConnectionAttempts);
|
connectionState.ConnectionAttempts);
|
||||||
}
|
}
|
||||||
|
|
||||||
rconSocket.Close();
|
rconSocket.Close();
|
||||||
|
|
||||||
|
if (connectionState.OnSentData.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
connectionState.OnSentData.Release(1);
|
||||||
|
}
|
||||||
|
|
||||||
throw new NetworkException("Timed out sending RCon data", rconSocket);
|
throw new NetworkException("Timed out sending RCon data", rconSocket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,6 +508,11 @@ namespace Integrations.Cod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (connectionState.OnReceivedData.CurrentCount == 0)
|
||||||
|
{
|
||||||
|
connectionState.OnReceivedData.Release(1);
|
||||||
|
}
|
||||||
|
|
||||||
rconSocket.Close();
|
rconSocket.Close();
|
||||||
throw new NetworkException("Timed out receiving RCon response", rconSocket);
|
throw new NetworkException("Timed out receiving RCon response", rconSocket);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user