dea5b3f954
fix alternative encoding character converting allow more paths for game log server add localization for unknown ips in welcome plugin add gamelog server uri to support game log server on games that must supply manual log path misc fixes
181 lines
8.2 KiB
C#
181 lines
8.2 KiB
C#
using SharedLibraryCore;
|
|
using SharedLibraryCore.Database.Models;
|
|
using SharedLibraryCore.Exceptions;
|
|
using SharedLibraryCore.Interfaces;
|
|
using SharedLibraryCore.RCon;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using static SharedLibraryCore.Server;
|
|
|
|
namespace IW4MAdmin.Application.RconParsers
|
|
{
|
|
class BaseRConParser : IRConParser
|
|
{
|
|
public BaseRConParser()
|
|
{
|
|
Configuration = new DynamicRConParserConfiguration()
|
|
{
|
|
CommandPrefixes = new CommandPrefix()
|
|
{
|
|
Tell = "tell {0} {1}",
|
|
Say = "say {0}",
|
|
Kick = "clientkick {0} \"{1}\"",
|
|
Ban = "clientkick {0} \"{1}\"",
|
|
TempBan = "tempbanclient {0} \"{1}\"",
|
|
RConCommand = "ÿÿÿÿrcon {0} {1}",
|
|
RConGetDvar = "ÿÿÿÿrcon {0} {1}",
|
|
RConSetDvar = "ÿÿÿÿrcon {0} set {1}",
|
|
RConGetStatus = "ÿÿÿÿgetstatus",
|
|
RConGetInfo = "ÿÿÿÿgetinfo",
|
|
RConResponse = "ÿÿÿÿprint",
|
|
},
|
|
};
|
|
|
|
Configuration.Status.Pattern = @"^ *([0-9]+) +-?([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) +(.{0,32}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:-*\d{1,5}|0+.0+:-*\d{1,5}|loopback) +(-*[0-9]+) +([0-9]+) *$";
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConClientNumber, 1);
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConScore, 2);
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConPing, 3);
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConNetworkId, 4);
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConName, 5);
|
|
Configuration.Status.AddMapping(ParserRegex.GroupType.RConIpAddress, 7);
|
|
|
|
Configuration.Dvar.Pattern = "^\"(.+)\" is: \"(.+)?\" default: \"(.+)?\"\n(?:latched: \"(.+)?\"\n)? *(.+)$";
|
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarName, 1);
|
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarValue, 2);
|
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDefaultValue, 3);
|
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarLatchedValue, 4);
|
|
Configuration.Dvar.AddMapping(ParserRegex.GroupType.RConDvarDomain, 5);
|
|
}
|
|
|
|
public IRConParserConfiguration Configuration { get; set; }
|
|
|
|
public string Version { get; set; } = "CoD";
|
|
public Game GameName { get; set; } = Game.COD;
|
|
public bool CanGenerateLogPath { get; set; } = true;
|
|
|
|
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
|
|
{
|
|
var response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command);
|
|
return response.Skip(1).ToArray();
|
|
}
|
|
|
|
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
|
|
{
|
|
string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.GET_DVAR, dvarName);
|
|
string response = string.Join('\n', lineSplit.Skip(1));
|
|
|
|
if (!lineSplit[0].Contains(Configuration.CommandPrefixes.RConResponse))
|
|
{
|
|
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
|
|
}
|
|
|
|
if (response.Contains("Unknown command"))
|
|
{
|
|
throw new DvarException($"DVAR \"{dvarName}\" does not exist");
|
|
}
|
|
|
|
var match = Regex.Match(response, Configuration.Dvar.Pattern);
|
|
|
|
if (!match.Success)
|
|
{
|
|
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
|
|
}
|
|
|
|
string value = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarValue]].Value.StripColors();
|
|
string defaultValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDefaultValue]].Value.StripColors();
|
|
string latchedValue = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarLatchedValue]].Value.StripColors();
|
|
|
|
return new Dvar<T>()
|
|
{
|
|
Name = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarName]].Value.StripColors(),
|
|
Value = string.IsNullOrEmpty(value) ? default(T) : (T)Convert.ChangeType(value, typeof(T)),
|
|
DefaultValue = string.IsNullOrEmpty(defaultValue) ? default(T) : (T)Convert.ChangeType(defaultValue, typeof(T)),
|
|
LatchedValue = string.IsNullOrEmpty(latchedValue) ? default(T) : (T)Convert.ChangeType(latchedValue, typeof(T)),
|
|
Domain = match.Groups[Configuration.Dvar.GroupMapping[ParserRegex.GroupType.RConDvarDomain]].Value.StripColors()
|
|
};
|
|
}
|
|
|
|
public async Task<List<EFClient>> GetStatusAsync(Connection connection)
|
|
{
|
|
string[] response = await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND_STATUS);
|
|
return ClientsFromStatus(response);
|
|
}
|
|
|
|
public async Task<bool> SetDvarAsync(Connection connection, string dvarName, object dvarValue)
|
|
{
|
|
return (await connection.SendQueryAsync(StaticHelpers.QueryType.SET_DVAR, $"{dvarName} {dvarValue}")).Length > 0;
|
|
}
|
|
|
|
private List<EFClient> ClientsFromStatus(string[] Status)
|
|
{
|
|
List<EFClient> StatusPlayers = new List<EFClient>();
|
|
|
|
if (Status.Length < 4)
|
|
{
|
|
throw new ServerException("Unexpected status response received");
|
|
}
|
|
|
|
int validMatches = 0;
|
|
foreach (string S in Status)
|
|
{
|
|
string responseLine = S.Trim();
|
|
|
|
var regex = Regex.Match(responseLine, Configuration.Status.Pattern, RegexOptions.IgnoreCase);
|
|
|
|
if (regex.Success)
|
|
{
|
|
validMatches++;
|
|
int clientNumber = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConClientNumber]].Value);
|
|
int score = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConScore]].Value);
|
|
|
|
int ping = 999;
|
|
|
|
// their state can be CNCT, ZMBI etc
|
|
if (regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Value.Length <= 3)
|
|
{
|
|
ping = int.Parse(regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConPing]].Value);
|
|
}
|
|
|
|
long networkId = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConNetworkId]].Value.ConvertLong();
|
|
string name = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConName]].Value.StripColors().Trim();
|
|
int? ip = regex.Groups[Configuration.Status.GroupMapping[ParserRegex.GroupType.RConIpAddress]].Value.Split(':')[0].ConvertToIP();
|
|
|
|
var client = new EFClient()
|
|
{
|
|
CurrentAlias = new EFAlias()
|
|
{
|
|
Name = name
|
|
},
|
|
NetworkId = networkId,
|
|
ClientNumber = clientNumber,
|
|
IPAddress = ip,
|
|
Ping = ping,
|
|
Score = score,
|
|
IsBot = ip == null,
|
|
State = EFClient.ClientState.Connecting
|
|
};
|
|
|
|
// they've not fully connected yet
|
|
if (!client.IsBot && ping == 999)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
StatusPlayers.Add(client);
|
|
}
|
|
}
|
|
|
|
// this happens if status is requested while map is rotating
|
|
if (Status.Length > 5 && validMatches == 0)
|
|
{
|
|
throw new ServerException("Server is rotating map");
|
|
}
|
|
|
|
return StatusPlayers;
|
|
}
|
|
}
|
|
}
|