improve CS:GO compatibility

This commit is contained in:
RaidMax 2021-06-11 11:52:30 -05:00
parent 3a1e8359c2
commit e628ac0e9e
8 changed files with 183 additions and 57 deletions

View File

@ -1042,8 +1042,8 @@ namespace IW4MAdmin
EventParser = Manager.AdditionalEventParsers EventParser = Manager.AdditionalEventParsers
.FirstOrDefault(_parser => _parser.Version == ServerConfig.EventParserVersion); .FirstOrDefault(_parser => _parser.Version == ServerConfig.EventParserVersion);
RconParser = RconParser ?? Manager.AdditionalRConParsers[0]; RconParser ??= Manager.AdditionalRConParsers[0];
EventParser = EventParser ?? Manager.AdditionalEventParsers[0]; EventParser ??= Manager.AdditionalEventParsers[0];
RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine); RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine);
RemoteConnection.SetConfiguration(RconParser); RemoteConnection.SetConfiguration(RconParser);

View File

@ -378,8 +378,6 @@ Global
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x64.Build.0 = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x64.Build.0 = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x86.ActiveCfg = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x86.ActiveCfg = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x86.Build.0 = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Debug|x86.Build.0 = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|x64.ActiveCfg = Debug|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|x64.ActiveCfg = Debug|Any CPU
@ -394,6 +392,8 @@ Global
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x64.Build.0 = Release|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x64.Build.0 = Release|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x86.ActiveCfg = Release|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x86.ActiveCfg = Release|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x86.Build.0 = Release|Any CPU {A9348433-58C1-4B9C-8BB7-088B02529D9D}.Release|x86.Build.0 = Release|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
{A9348433-58C1-4B9C-8BB7-088B02529D9D}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
@ -402,8 +402,6 @@ Global
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x64.Build.0 = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x64.Build.0 = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x86.ActiveCfg = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x86.ActiveCfg = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x86.Build.0 = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Debug|x86.Build.0 = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Any CPU.Build.0 = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|x64.ActiveCfg = Debug|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|x64.ActiveCfg = Debug|Any CPU
@ -418,6 +416,8 @@ Global
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x64.Build.0 = Release|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x64.Build.0 = Release|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x86.ActiveCfg = Release|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x86.ActiveCfg = Release|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x86.Build.0 = Release|Any CPU {9512295B-3045-40E0-9B7E-2409F2173E9D}.Release|x86.Build.0 = Release|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
{9512295B-3045-40E0-9B7E-2409F2173E9D}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -4,6 +4,12 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Integrations.Cod</AssemblyName> <AssemblyName>Integrations.Cod</AssemblyName>
<RootNamespace>Integrations.Cod</RootNamespace> <RootNamespace>Integrations.Cod</RootNamespace>
<Configurations>Debug;Release;Prerelease</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
<Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,48 @@
using System.Text;
namespace Integrations.Source.Extensions
{
public static class SourceExtensions
{
public static string ReplaceUnfriendlyCharacters(this string source)
{
var result = new StringBuilder();
var quoteStart = false;
var quoteIndex = 0;
var index = 0;
foreach (var character in source)
{
if (character == '%')
{
result.Append('‰');
}
else if ((character == '"' || character == '\'') && index + 1 != source.Length)
{
if (quoteIndex > 0)
{
result.Append(!quoteStart ? "«" : "»");
quoteStart = !quoteStart;
}
else
{
result.Append('"');
}
quoteIndex++;
}
else
{
result.Append(character);
}
index++;
}
return result.ToString();
}
}
}

View File

@ -4,6 +4,12 @@
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<AssemblyName>Integrations.Source</AssemblyName> <AssemblyName>Integrations.Source</AssemblyName>
<RootNamespace>Integrations.Source</RootNamespace> <RootNamespace>Integrations.Source</RootNamespace>
<Configurations>Debug;Release;Prerelease</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
<Optimize>true</Optimize>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,6 +1,9 @@
using System.Linq; using System;
using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Integrations.Source.Extensions;
using Integrations.Source.Interfaces; using Integrations.Source.Interfaces;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using RconSharp; using RconSharp;
@ -20,28 +23,57 @@ namespace Integrations.Source
private readonly string _hostname; private readonly string _hostname;
private readonly int _port; private readonly int _port;
private readonly IRConClientFactory _rconClientFactory; private readonly IRConClientFactory _rconClientFactory;
private readonly SemaphoreSlim _activeQuery;
private static readonly TimeSpan FloodDelay = TimeSpan.FromMilliseconds(250);
private DateTime _lastQuery = DateTime.Now;
private RconClient _rconClient; private RconClient _rconClient;
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory, public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
string hostname, int port, string password) string hostname, int port, string password)
{ {
_rconClient = rconClientFactory.CreateClient(hostname, port);
_rconClientFactory = rconClientFactory; _rconClientFactory = rconClientFactory;
_password = password; _password = password;
_hostname = hostname; _hostname = hostname;
_port = port; _port = port;
_logger = logger; _logger = logger;
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
_activeQuery = new SemaphoreSlim(1, 1);
}
~SourceRConConnection()
{
_activeQuery.Dispose();
} }
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "") public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
{ {
try
{
await _activeQuery.WaitAsync();
var diff = DateTime.Now - _lastQuery;
if (diff < FloodDelay)
{
await Task.Delay(FloodDelay - diff);
}
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogDebug("Connecting to RCon socket");
}
await _rconClient.ConnectAsync(); await _rconClient.ConnectAsync();
bool authenticated; bool authenticated;
try try
{ {
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogDebug("Authenticating to RCon socket");
}
authenticated = await _rconClient.AuthenticateAsync(_password); authenticated = await _rconClient.AuthenticateAsync(_password);
} }
catch (SocketException ex) catch (SocketException ex)
@ -56,15 +88,24 @@ namespace Integrations.Source
"Server appears to resumed from hibernation, so we are using a new socket"); "Server appears to resumed from hibernation, so we are using a new socket");
} }
try
{
_rconClient.Disconnect();
}
catch
{
// ignored
}
_rconClient = _rconClientFactory.CreateClient(_hostname, _port); _rconClient = _rconClientFactory.CreateClient(_hostname, _port);
} }
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{ {
_logger.LogError("Could not login to server"); _logger.LogError(ex, "Error occurred authenticating with server");
} }
throw new NetworkException("Could not authenticate with server"); throw new NetworkException("Error occurred authenticating with server");
} }
if (!authenticated) if (!authenticated)
@ -82,12 +123,15 @@ namespace Integrations.Source
parameters = "status"; parameters = "status";
} }
parameters = parameters.ReplaceUnfriendlyCharacters();
parameters = parameters.StripColors();
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}")) using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{ {
_logger.LogDebug("Sending query {Type} with parameters {Parameters}", type, parameters); _logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
} }
var response = await _rconClient.ExecuteCommandAsync(parameters.StripColors(), true); var response = await _rconClient.ExecuteCommandAsync(parameters, true);
using (LogContext.PushProperty("Server", $"{_rconClient.Host}:{_rconClient.Port}")) using (LogContext.PushProperty("Server", $"{_rconClient.Host}:{_rconClient.Port}"))
{ {
@ -98,6 +142,28 @@ namespace Integrations.Source
return split.Take(split.Length - 1).ToArray(); return split.Take(split.Length - 1).ToArray();
} }
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
ex.GetType() != typeof(ServerException))
{
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
{
_logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
}
throw new NetworkException("Unable to communicate with server");
}
finally
{
if (_activeQuery.CurrentCount == 0)
{
_activeQuery.Release();
}
_lastQuery = DateTime.Now;
}
}
public void SetConfiguration(IRConParser config) public void SetConfiguration(IRConParser config)
{ {
} }

View File

@ -3,7 +3,7 @@ let eventParser;
const plugin = { const plugin = {
author: 'RaidMax', author: 'RaidMax',
version: 0.1, version: 0.2,
name: 'CS:GO Parser', name: 'CS:GO Parser',
engine: 'Source', engine: 'Source',
isParser: true, isParser: true,
@ -12,8 +12,8 @@ const plugin = {
}, },
onLoadAsync: function (manager) { onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser(this.engine); rconParser = manager.GenerateDynamicRConParser(this.name);
eventParser = manager.GenerateDynamicEventParser(this.engine); eventParser = manager.GenerateDynamicEventParser(this.name);
rconParser.RConEngine = this.engine; rconParser.RConEngine = this.engine;
rconParser.Configuration.StatusHeader.Pattern = 'userid +name +uniqueid +connected +ping +loss +state +rate +adr'; rconParser.Configuration.StatusHeader.Pattern = 'userid +name +uniqueid +connected +ping +loss +state +rate +adr';
@ -24,16 +24,16 @@ const plugin = {
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$'; rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
rconParser.Configuration.MapStatus.AddMapping(113, 1); rconParser.Configuration.MapStatus.AddMapping(113, 1);
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d humans, \\d bots \\((\\d+).+'; rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
rconParser.Configuration.MapStatus.AddMapping(114, 1); rconParser.Configuration.MapStatus.AddMapping(114, 1);
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = (?:"(.+)" (?:\\( def\\. "(.*)" \\))|"(.+)" +(.+)) +- (.*)$'; rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
rconParser.Configuration.Dvar.AddMapping(106, 1); rconParser.Configuration.Dvar.AddMapping(106, 1);
rconParser.Configuration.Dvar.AddMapping(107, 2); rconParser.Configuration.Dvar.AddMapping(107, 2);
rconParser.Configuration.Dvar.AddMapping(108, 3); rconParser.Configuration.Dvar.AddMapping(108, 3);
rconParser.Configuration.Dvar.AddMapping(109, 3); rconParser.Configuration.Dvar.AddMapping(109, 3);
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) (\\d+:\\d+) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$'; rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
rconParser.Configuration.Status.AddMapping(100, 2); rconParser.Configuration.Status.AddMapping(100, 2);
rconParser.Configuration.Status.AddMapping(101, 7); rconParser.Configuration.Status.AddMapping(101, 7);
rconParser.Configuration.Status.AddMapping(102, 6); rconParser.Configuration.Status.AddMapping(102, 6);

View File

@ -3,7 +3,7 @@ let eventParser;
const plugin = { const plugin = {
author: 'RaidMax', author: 'RaidMax',
version: 0.1, version: 0.2,
name: 'CS:GO (SourceMod) Parser', name: 'CS:GO (SourceMod) Parser',
engine: 'Source', engine: 'Source',
isParser: true, isParser: true,
@ -12,8 +12,8 @@ const plugin = {
}, },
onLoadAsync: function (manager) { onLoadAsync: function (manager) {
rconParser = manager.GenerateDynamicRConParser(this.engine); rconParser = manager.GenerateDynamicRConParser(this.name);
eventParser = manager.GenerateDynamicEventParser(this.engine); eventParser = manager.GenerateDynamicEventParser(this.name);
rconParser.RConEngine = this.engine; rconParser.RConEngine = this.engine;
rconParser.Configuration.StatusHeader.Pattern = 'userid +name +uniqueid +connected +ping +loss +state +rate +adr'; rconParser.Configuration.StatusHeader.Pattern = 'userid +name +uniqueid +connected +ping +loss +state +rate +adr';
@ -24,16 +24,16 @@ const plugin = {
rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$'; rconParser.Configuration.HostnameStatus.Pattern = '^hostname: +(.+)$';
rconParser.Configuration.MapStatus.AddMapping(113, 1); rconParser.Configuration.MapStatus.AddMapping(113, 1);
rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d humans, \\d bots \\((\\d+).+'; rconParser.Configuration.MaxPlayersStatus.Pattern = '^players *: +\\d+ humans, \\d+ bots \\((\\d+).+';
rconParser.Configuration.MapStatus.AddMapping(114, 1); rconParser.Configuration.MapStatus.AddMapping(114, 1);
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = (?:"(.+)" (?:\\( def\\. "(.*)" \\))|"(.+)" +(.+)) +- (.*)$'; rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
rconParser.Configuration.Dvar.AddMapping(106, 1); rconParser.Configuration.Dvar.AddMapping(106, 1);
rconParser.Configuration.Dvar.AddMapping(107, 2); rconParser.Configuration.Dvar.AddMapping(107, 2);
rconParser.Configuration.Dvar.AddMapping(108, 3); rconParser.Configuration.Dvar.AddMapping(108, 3);
rconParser.Configuration.Dvar.AddMapping(109, 3); rconParser.Configuration.Dvar.AddMapping(109, 3);
rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) (\\d+:\\d+) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$'; rconParser.Configuration.Status.Pattern = '^#\\s*(\\d+) (\\d+) "(.+)" (\\S+) +(\\d+:\\d+(?::\\d+)?) (\\d+) (\\S+) (\\S+) (\\d+) (\\d+\\.\\d+\\.\\d+\\.\\d+:\\d+)$';
rconParser.Configuration.Status.AddMapping(100, 2); rconParser.Configuration.Status.AddMapping(100, 2);
rconParser.Configuration.Status.AddMapping(101, 7); rconParser.Configuration.Status.AddMapping(101, 7);
rconParser.Configuration.Status.AddMapping(102, 6); rconParser.Configuration.Status.AddMapping(102, 6);
@ -64,7 +64,7 @@ const plugin = {
rconParser.Configuration.CommandPrefixes.Ban = 'sm_kick #{0} {1}'; rconParser.Configuration.CommandPrefixes.Ban = 'sm_kick #{0} {1}';
rconParser.Configuration.CommandPrefixes.TempBan = 'sm_kick #{0} {1}'; rconParser.Configuration.CommandPrefixes.TempBan = 'sm_kick #{0} {1}';
rconParser.Configuration.CommandPrefixes.Say = 'sm_say {0}'; rconParser.Configuration.CommandPrefixes.Say = 'sm_say {0}';
rconParser.Configuration.CommandPrefixes.Tell = 'sm_psay #{0} {1}'; rconParser.Configuration.CommandPrefixes.Tell = 'sm_psay #{0} "{1}"';
eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" say "(.*)"$'; eventParser.Configuration.Say.Pattern = '^"(.+)<(\\d+)><(.+)><(.*?)>" say "(.*)"$';
eventParser.Configuration.Say.AddMapping(5, 1); eventParser.Configuration.Say.AddMapping(5, 1);