hopefully fix some issues with rcon socket

This commit is contained in:
RaidMax 2022-03-02 18:21:08 -06:00
parent e6e56d8d14
commit ef70496546

View File

@ -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);
} }