IW4M-Admin/Integrations/Source/SourceRConConnection.cs

187 lines
6.2 KiB
C#
Raw Normal View History

2021-06-11 12:52:30 -04:00
using System;
using System.Linq;
2021-06-03 11:51:03 -04:00
using System.Net.Sockets;
2021-06-11 12:52:30 -04:00
using System.Threading;
2021-06-03 11:51:03 -04:00
using System.Threading.Tasks;
2021-06-11 12:52:30 -04:00
using Integrations.Source.Extensions;
2021-06-03 11:51:03 -04:00
using Integrations.Source.Interfaces;
using Microsoft.Extensions.Logging;
using RconSharp;
using Serilog.Context;
using SharedLibraryCore;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.RCon;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace Integrations.Source
{
public class SourceRConConnection : IRConConnection
{
private readonly ILogger _logger;
private readonly string _password;
private readonly string _hostname;
private readonly int _port;
private readonly IRConClientFactory _rconClientFactory;
2021-06-11 12:52:30 -04:00
private readonly SemaphoreSlim _activeQuery;
2021-06-03 11:51:03 -04:00
2021-06-11 12:52:30 -04:00
private static readonly TimeSpan FloodDelay = TimeSpan.FromMilliseconds(250);
private static readonly TimeSpan ConnectionTimeout = TimeSpan.FromSeconds(30);
2021-06-11 12:52:30 -04:00
private DateTime _lastQuery = DateTime.Now;
2021-06-03 11:51:03 -04:00
private RconClient _rconClient;
private bool _authenticated;
private bool _needNewSocket = true;
2021-06-03 11:51:03 -04:00
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
string hostname, int port, string password)
{
_rconClientFactory = rconClientFactory;
_password = password;
_hostname = hostname;
_port = port;
_logger = logger;
2021-06-11 12:52:30 -04:00
_activeQuery = new SemaphoreSlim(1, 1);
2021-06-03 11:51:03 -04:00
}
2021-06-11 12:52:30 -04:00
~SourceRConConnection()
2021-06-03 11:51:03 -04:00
{
2021-06-11 12:52:30 -04:00
_activeQuery.Dispose();
}
2021-06-03 11:51:03 -04:00
2021-06-11 12:52:30 -04:00
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
{
2021-06-03 11:51:03 -04:00
try
{
2021-06-11 12:52:30 -04:00
await _activeQuery.WaitAsync();
await WaitForAvailable();
2021-06-11 12:52:30 -04:00
if (_needNewSocket)
2021-06-11 12:52:30 -04:00
{
try
2021-06-11 12:52:30 -04:00
{
_rconClient?.Disconnect();
2021-06-11 12:52:30 -04:00
}
catch
2021-06-11 12:52:30 -04:00
{
// ignored
2021-06-11 12:52:30 -04:00
}
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
_authenticated = false;
_needNewSocket = false;
2021-06-11 12:52:30 -04:00
}
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
2021-06-03 11:51:03 -04:00
{
_logger.LogDebug("Connecting to RCon socket");
2021-06-03 11:51:03 -04:00
}
await TryConnectAndAuthenticate().WithTimeout(ConnectionTimeout);
var multiPacket = false;
2021-06-11 12:52:30 -04:00
if (type == StaticHelpers.QueryType.COMMAND_STATUS)
{
parameters = "status";
multiPacket = true;
2021-06-11 12:52:30 -04:00
}
parameters = parameters.ReplaceUnfriendlyCharacters();
parameters = parameters.StripColors();
2021-06-03 11:51:03 -04:00
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
2021-06-11 12:52:30 -04:00
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
2021-06-03 11:51:03 -04:00
}
var response = await _rconClient.ExecuteCommandAsync(parameters, multiPacket)
.WithTimeout(ConnectionTimeout);
2021-06-03 11:51:03 -04:00
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
2021-06-11 12:52:30 -04:00
{
_logger.LogDebug("Received RCon response {Response}", response);
}
var split = response.TrimEnd('\n').Split('\n');
return split.Take(split.Length - 1).ToArray();
2021-06-03 11:51:03 -04:00
}
catch (TaskCanceledException)
{
_needNewSocket = true;
throw new NetworkException("Timeout while attempting to communicate with server");
}
catch (SocketException ex)
{
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogError(ex, "Socket exception encountered while attempting to communicate with server");
}
_needNewSocket = true;
throw new NetworkException("Socket exception encountered while attempting to communicate with server");
}
2021-06-11 12:52:30 -04:00
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
ex.GetType() != typeof(ServerException))
2021-06-03 11:51:03 -04:00
{
2021-06-11 12:52:30 -04:00
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
}
2021-06-03 11:51:03 -04:00
2021-06-11 12:52:30 -04:00
throw new NetworkException("Unable to communicate with server");
}
2021-06-03 11:51:03 -04:00
2021-06-11 12:52:30 -04:00
finally
2021-06-03 11:51:03 -04:00
{
2021-06-11 12:52:30 -04:00
if (_activeQuery.CurrentCount == 0)
{
_activeQuery.Release();
}
2021-06-03 11:51:03 -04:00
2021-06-11 12:52:30 -04:00
_lastQuery = DateTime.Now;
}
2021-06-03 11:51:03 -04:00
}
private async Task WaitForAvailable()
{
var diff = DateTime.Now - _lastQuery;
if (diff < FloodDelay)
{
await Task.Delay(FloodDelay - diff);
}
}
private async Task TryConnectAndAuthenticate()
{
if (!_authenticated)
{
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogDebug("Authenticating to RCon socket");
}
await _rconClient.ConnectAsync().WithTimeout(ConnectionTimeout);
_authenticated = await _rconClient.AuthenticateAsync(_password).WithTimeout(ConnectionTimeout);
if (!_authenticated)
{
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogError("Could not login to server");
}
throw new ServerException("Could not authenticate to server with provided password");
}
}
}
2021-06-03 11:51:03 -04:00
public void SetConfiguration(IRConParser config)
{
}
}
}