improve CS:GO compatibility
This commit is contained in:
parent
3a1e8359c2
commit
e628ac0e9e
@ -1042,8 +1042,8 @@ namespace IW4MAdmin
|
||||
EventParser = Manager.AdditionalEventParsers
|
||||
.FirstOrDefault(_parser => _parser.Version == ServerConfig.EventParserVersion);
|
||||
|
||||
RconParser = RconParser ?? Manager.AdditionalRConParsers[0];
|
||||
EventParser = EventParser ?? Manager.AdditionalEventParsers[0];
|
||||
RconParser ??= Manager.AdditionalRConParsers[0];
|
||||
EventParser ??= Manager.AdditionalEventParsers[0];
|
||||
|
||||
RemoteConnection = RConConnectionFactory.CreateConnection(IP, Port, Password, RconParser.RConEngine);
|
||||
RemoteConnection.SetConfiguration(RconParser);
|
||||
|
@ -378,8 +378,6 @@ Global
|
||||
{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.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.Build.0 = 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|x86.ActiveCfg = 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.Build.0 = 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|x86.ActiveCfg = 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.Build.0 = 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|x86.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -4,6 +4,12 @@
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AssemblyName>Integrations.Cod</AssemblyName>
|
||||
<RootNamespace>Integrations.Cod</RootNamespace>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
48
Integrations/Source/Extensions/SourceExtensions.cs
Normal file
48
Integrations/Source/Extensions/SourceExtensions.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,12 @@
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AssemblyName>Integrations.Source</AssemblyName>
|
||||
<RootNamespace>Integrations.Source</RootNamespace>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Prerelease' ">
|
||||
<Optimize>true</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Integrations.Source.Extensions;
|
||||
using Integrations.Source.Interfaces;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RconSharp;
|
||||
@ -20,82 +23,145 @@ namespace Integrations.Source
|
||||
private readonly string _hostname;
|
||||
private readonly int _port;
|
||||
private readonly IRConClientFactory _rconClientFactory;
|
||||
private readonly SemaphoreSlim _activeQuery;
|
||||
|
||||
private static readonly TimeSpan FloodDelay = TimeSpan.FromMilliseconds(250);
|
||||
|
||||
private DateTime _lastQuery = DateTime.Now;
|
||||
private RconClient _rconClient;
|
||||
|
||||
public SourceRConConnection(ILogger<SourceRConConnection> logger, IRConClientFactory rconClientFactory,
|
||||
string hostname, int port, string password)
|
||||
{
|
||||
_rconClient = rconClientFactory.CreateClient(hostname, port);
|
||||
_rconClientFactory = rconClientFactory;
|
||||
_password = password;
|
||||
_hostname = hostname;
|
||||
_port = port;
|
||||
_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 = "")
|
||||
{
|
||||
await _rconClient.ConnectAsync();
|
||||
|
||||
bool authenticated;
|
||||
|
||||
try
|
||||
{
|
||||
authenticated = await _rconClient.AuthenticateAsync(_password);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
// occurs when the server comes back from hibernation
|
||||
// this is probably a bug in the library
|
||||
if (ex.ErrorCode == 10053 || ex.ErrorCode == 10054)
|
||||
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();
|
||||
|
||||
bool authenticated;
|
||||
|
||||
try
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogWarning(ex,
|
||||
"Server appears to resumed from hibernation, so we are using a new socket");
|
||||
_logger.LogDebug("Authenticating to RCon socket");
|
||||
}
|
||||
|
||||
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
||||
authenticated = await _rconClient.AuthenticateAsync(_password);
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
// occurs when the server comes back from hibernation
|
||||
// this is probably a bug in the library
|
||||
if (ex.ErrorCode == 10053 || ex.ErrorCode == 10054)
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogWarning(ex,
|
||||
"Server appears to resumed from hibernation, so we are using a new socket");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_rconClient.Disconnect();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
_rconClient = _rconClientFactory.CreateClient(_hostname, _port);
|
||||
}
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogError(ex, "Error occurred authenticating with server");
|
||||
}
|
||||
|
||||
throw new NetworkException("Error occurred authenticating with server");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
if (type == StaticHelpers.QueryType.COMMAND_STATUS)
|
||||
{
|
||||
parameters = "status";
|
||||
}
|
||||
|
||||
parameters = parameters.ReplaceUnfriendlyCharacters();
|
||||
parameters = parameters.StripColors();
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogError("Could not login to server");
|
||||
_logger.LogDebug("Sending query {Type} with parameters \"{Parameters}\"", type, parameters);
|
||||
}
|
||||
|
||||
throw new NetworkException("Could not authenticate with server");
|
||||
var response = await _rconClient.ExecuteCommandAsync(parameters, true);
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_rconClient.Host}:{_rconClient.Port}"))
|
||||
{
|
||||
_logger.LogDebug("Received RCon response {Response}", response);
|
||||
}
|
||||
|
||||
var split = response.TrimEnd('\n').Split('\n');
|
||||
return split.Take(split.Length - 1).ToArray();
|
||||
}
|
||||
|
||||
if (!authenticated)
|
||||
catch (Exception ex) when (ex.GetType() != typeof(NetworkException) &&
|
||||
ex.GetType() != typeof(ServerException))
|
||||
{
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogError("Could not login to server");
|
||||
_logger.LogError(ex, "Could not execute RCon query {Parameters}", parameters);
|
||||
}
|
||||
|
||||
throw new ServerException("Could not authenticate to server with provided password");
|
||||
throw new NetworkException("Unable to communicate with server");
|
||||
}
|
||||
|
||||
if (type == StaticHelpers.QueryType.COMMAND_STATUS)
|
||||
finally
|
||||
{
|
||||
parameters = "status";
|
||||
if (_activeQuery.CurrentCount == 0)
|
||||
{
|
||||
_activeQuery.Release();
|
||||
}
|
||||
|
||||
_lastQuery = DateTime.Now;
|
||||
}
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_hostname}:{_port}"))
|
||||
{
|
||||
_logger.LogDebug("Sending query {Type} with parameters {Parameters}", type, parameters);
|
||||
}
|
||||
|
||||
var response = await _rconClient.ExecuteCommandAsync(parameters.StripColors(), true);
|
||||
|
||||
using (LogContext.PushProperty("Server", $"{_rconClient.Host}:{_rconClient.Port}"))
|
||||
{
|
||||
_logger.LogDebug("Received RCon response {Response}", response);
|
||||
}
|
||||
|
||||
var split = response.TrimEnd('\n').Split('\n');
|
||||
return split.Take(split.Length - 1).ToArray();
|
||||
}
|
||||
|
||||
public void SetConfiguration(IRConParser config)
|
||||
|
@ -3,7 +3,7 @@ let eventParser;
|
||||
|
||||
const plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.1,
|
||||
version: 0.2,
|
||||
name: 'CS:GO Parser',
|
||||
engine: 'Source',
|
||||
isParser: true,
|
||||
@ -12,8 +12,8 @@ const plugin = {
|
||||
},
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
rconParser = manager.GenerateDynamicRConParser(this.engine);
|
||||
eventParser = manager.GenerateDynamicEventParser(this.engine);
|
||||
rconParser = manager.GenerateDynamicRConParser(this.name);
|
||||
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||
rconParser.RConEngine = this.engine;
|
||||
|
||||
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.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.Dvar.Pattern = '^"(.+)" = (?:"(.+)" (?:\\( def\\. "(.*)" \\))|"(.+)" +(.+)) +- (.*)$';
|
||||
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
||||
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
||||
rconParser.Configuration.Dvar.AddMapping(108, 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(101, 7);
|
||||
rconParser.Configuration.Status.AddMapping(102, 6);
|
||||
|
@ -3,7 +3,7 @@ let eventParser;
|
||||
|
||||
const plugin = {
|
||||
author: 'RaidMax',
|
||||
version: 0.1,
|
||||
version: 0.2,
|
||||
name: 'CS:GO (SourceMod) Parser',
|
||||
engine: 'Source',
|
||||
isParser: true,
|
||||
@ -12,8 +12,8 @@ const plugin = {
|
||||
},
|
||||
|
||||
onLoadAsync: function (manager) {
|
||||
rconParser = manager.GenerateDynamicRConParser(this.engine);
|
||||
eventParser = manager.GenerateDynamicEventParser(this.engine);
|
||||
rconParser = manager.GenerateDynamicRConParser(this.name);
|
||||
eventParser = manager.GenerateDynamicEventParser(this.name);
|
||||
rconParser.RConEngine = this.engine;
|
||||
|
||||
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.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.Dvar.Pattern = '^"(.+)" = (?:"(.+)" (?:\\( def\\. "(.*)" \\))|"(.+)" +(.+)) +- (.*)$';
|
||||
rconParser.Configuration.Dvar.Pattern = '^"(.+)" = "(.+)" (?:\\( def. "(.*)" \\))?(?: |\\w)+- (.+)$';
|
||||
rconParser.Configuration.Dvar.AddMapping(106, 1);
|
||||
rconParser.Configuration.Dvar.AddMapping(107, 2);
|
||||
rconParser.Configuration.Dvar.AddMapping(108, 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(101, 7);
|
||||
rconParser.Configuration.Status.AddMapping(102, 6);
|
||||
@ -64,7 +64,7 @@ const plugin = {
|
||||
rconParser.Configuration.CommandPrefixes.Ban = 'sm_kick #{0} {1}';
|
||||
rconParser.Configuration.CommandPrefixes.TempBan = 'sm_kick #{0} {1}';
|
||||
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.AddMapping(5, 1);
|
||||
|
Loading…
Reference in New Issue
Block a user