Finish dynamic dvar parsing for IW4x

This commit is contained in:
RaidMax 2019-02-01 19:49:25 -06:00
parent f1dd4f7c7f
commit 59e0072744
13 changed files with 87 additions and 50 deletions

View File

@ -36,12 +36,8 @@ namespace IW4MAdmin.Application
public DateTime StartTime { get; private set; }
public string Version => Assembly.GetEntryAssembly().GetName().Version.ToString();
public IList<IRConParser> AdditionalRConParsers => _additionalRConParsers.ToList<IRConParser>();
public IList<IEventParser> AdditionalEventParsers => _additionalEventParsers.ToList<IEventParser>();
private readonly IList<DynamicRConParser> _additionalRConParsers;
private readonly IList<DynamicEventParser> _additionalEventParsers;
public IList<IRConParser> AdditionalRConParsers { get; }
public IList<IEventParser> AdditionalEventParsers { get; }
static ApplicationManager Instance;
readonly List<AsyncStatus> TaskStatuses;
@ -70,8 +66,8 @@ namespace IW4MAdmin.Application
StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
PageList = new PageList();
_additionalEventParsers = new List<DynamicEventParser>();
_additionalRConParsers = new List<DynamicRConParser>();
AdditionalEventParsers = new List<IEventParser>();
AdditionalRConParsers = new List<IRConParser>();
OnServerEvent += OnGameEvent;
OnServerEvent += EventApi.OnGameEvent;
}

View File

@ -665,13 +665,17 @@ namespace IW4MAdmin
public async Task Initialize()
{
//RemoteConnection.SetConfiguration(Manager.AdditionalRConParsers.First().Configuration);
RconParser = ServerConfig.UseT6MParser ?
(IRConParser)new T6MRConParser() :
new IW3RConParser();
new IW4RConParser();
RemoteConnection.SetConfiguration(RconParser.Configuration);
var version = await this.GetDvarAsync<string>("version");
Version = version.Value;
GameName = Utilities.GetGame(version.Value);
GameName = Utilities.GetGame(version?.Value);
if (GameName == Game.IW4)
{
@ -702,7 +706,7 @@ namespace IW4MAdmin
RconParser = Manager.AdditionalRConParsers.FirstOrDefault(_parser => (_parser as DynamicRConParser).Version == version.Value) ?? RconParser;
}
var infoResponse = await this.GetInfoAsync();
var infoResponse = RconParser.Configuration.CommandPrefixes.RConGetInfo != null ? await this.GetInfoAsync() : null;
// this is normally slow, but I'm only doing it because different games have different prefixes
var hostname = infoResponse == null ?
(await this.GetDvarAsync<string>("sv_hostname")).Value :

View File

@ -9,5 +9,6 @@ namespace IW4MAdmin.Application.RconParsers
public CommandPrefix CommandPrefixes { get; set; }
public Server.Game GameName { get; set; }
public ParserRegex Status { get; set; } = new ParserRegex();
public ParserRegex Dvar { get; set; } = new ParserRegex();
}
}

View File

@ -23,7 +23,11 @@ namespace IW4MAdmin.Application.RconParsers
Say = "sayraw {0}",
Kick = "clientkick {0} \"{1}\"",
Ban = "clientkick {0} \"{1}\"",
TempBan = "tempbanclient {0} \"{1}\""
TempBan = "tempbanclient {0} \"{1}\"",
RConQuery = "ÿÿÿÿrcon {0} {1}",
RConGetStatus = "ÿÿÿÿgetstatus",
RConGetInfo = "ÿÿÿÿgetinfo",
RConResponse = "ÿÿÿÿprint",
},
GameName = Server.Game.IW4
};
@ -35,6 +39,13 @@ namespace IW4MAdmin.Application.RconParsers
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConNetworkId, 4);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConName, 5);
Configuration.Status.GroupMapping.Add(ParserRegex.GroupType.RConIpAddress, 7);
Configuration.Dvar.Pattern = "^\"(.+)\" is: \"(.+)\" default: \"(.+)\"\n(?:latched: \"(.+)\"\n)? *(.+)$";
Configuration.Dvar.GroupMapping.Add(ParserRegex.GroupType.RConDvarName, 1);
Configuration.Dvar.GroupMapping.Add(ParserRegex.GroupType.RConDvarValue, 2);
Configuration.Dvar.GroupMapping.Add(ParserRegex.GroupType.RConDvarDefaultValue, 3);
Configuration.Dvar.GroupMapping.Add(ParserRegex.GroupType.RConDvarLatchedValue, 4);
Configuration.Dvar.GroupMapping.Add(ParserRegex.GroupType.RConDvarDomain, 5);
}
public IRConParserConfiguration Configuration { get; set; }
@ -47,32 +58,37 @@ namespace IW4MAdmin.Application.RconParsers
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
{
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
string[] lineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
string response = string.Join('\n', lineSplit.Skip(1));
if (LineSplit.Length < 3)
if (lineSplit[0] != Configuration.CommandPrefixes.RConResponse)
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
throw new DvarException($"Could not retrieve DVAR \"{dvarName}\"");
}
// todo: can this be made more portable and modifiable from plugin
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
if (ValueSplit.Length < 5)
if (response.Contains("Unknown command"))
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
throw new DvarException($"DVAR \"{dvarName}\" does not exist");
}
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
var match = Regex.Match(response, Configuration.Dvar.Pattern);
return new Dvar<T>(DvarName)
if (!match.Success)
{
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
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()
};
}

View File

@ -60,9 +60,10 @@ namespace IW4MAdmin.Application.RconParsers
string DvarName = dvarName;
string DvarCurrentValue = Regex.Replace(ValueSplit[1], @"\^[0-9]", "");
return new Dvar<T>(DvarName)
return new Dvar<T>()
{
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)),
Name = DvarName
};
}

View File

@ -2,12 +2,10 @@
{
public class Dvar<T>
{
public string Name { get; private set; }
public T Value;
public Dvar(string name)
{
Name = name;
}
public string Name { get; set; }
public T Value { get; set; }
public T DefaultValue { get; set; }
public T LatchedValue { get; set; }
public string Domain { get; set; }
}
}

View File

@ -28,6 +28,11 @@ namespace SharedLibraryCore.Interfaces
RConNetworkId = 103,
RConName = 104,
RConIpAddress = 105,
RConDvarName = 106,
RConDvarValue = 107,
RConDvarDefaultValue = 108,
RConDvarLatchedValue = 109,
RConDvarDomain = 110,
AdditionalGroup = 200
}
public string Pattern { get; set; }

View File

@ -7,5 +7,6 @@ namespace SharedLibraryCore.Interfaces
CommandPrefix CommandPrefixes { get; set; }
Server.Game GameName { get; set; }
ParserRegex Status { get; set; }
ParserRegex Dvar { get; set; }
}
}

View File

@ -433,9 +433,9 @@ namespace SharedLibraryCore.Database.Models
}
// reserved slots stuff
// todo: is this broken on T6?
// todo: bots don't seem to honor party_maxplayers/sv_maxclients
if (CurrentServer.MaxClients - (CurrentServer.GetClientsAsList().Count(_client => !_client.IsPrivileged())) < CurrentServer.ServerConfig.ReservedSlotNumber &&
!this.IsPrivileged() && CurrentServer.GameName != Server.Game.T6M /* HACK: temporary */)
!this.IsPrivileged())
{
CurrentServer.Logger.WriteDebug($"Kicking {this} their spot is reserved");
Kick(loc["SERVER_KICK_SLOT_IS_RESERVED"], Utilities.IW4MAdminClient(CurrentServer));
@ -461,9 +461,10 @@ namespace SharedLibraryCore.Database.Models
if (ipAddress != null)
{
if (IPAddressString == "66.150.121.184")
// todo: remove this in a few weeks because it's just temporary for server forwarding
if (IPAddressString == "66.150.121.184" || IPAddressString == "62.210.178.177")
{
Kick("Your favorite servers are outdated. Please re-add the server.", autoKickClient);
Kick($"Your favorite servers are outdated. Please remove and re-add this server. ({CurrentServer.Hostname})", autoKickClient);
return false;
}
await CurrentServer.Manager.GetClientService().UpdateAlias(this);

View File

@ -13,5 +13,9 @@ namespace SharedLibraryCore.RCon
public string Ban { get; set; }
public string Unban { get; set; }
public string TempBan { get; set; }
public string RConQuery { get; set; }
public string RConGetStatus { get; set; }
public string RConGetInfo { get; set; }
public string RConResponse { get; set; }
}
}

View File

@ -30,12 +30,19 @@ namespace SharedLibraryCore.RCon
public string RConPassword { get; private set; }
private readonly ILogger Log;
private IRConParserConfiguration Config;
public Connection(string ipAddress, int port, string password, ILogger log)
public Connection(string ipAddress, int port, string password, ILogger log, IRConParserConfiguration config)
{
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
RConPassword = password;
Log = log;
Config = config;
}
public void SetConfiguration(IRConParserConfiguration config)
{
Config = config;
}
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
@ -73,16 +80,14 @@ namespace SharedLibraryCore.RCon
{
case StaticHelpers.QueryType.DVAR:
case StaticHelpers.QueryType.COMMAND:
var header = "ÿÿÿÿrcon ".Select(Convert.ToByte).ToList();
byte[] p = Utilities.EncodingType.GetBytes($"{RConPassword} {parameters}");
header.AddRange(p);
payload = header.ToArray();
payload = Utilities.EncodingType
.GetBytes(string.Format(Config.CommandPrefixes.RConQuery, RConPassword, parameters + '\0'));
break;
case StaticHelpers.QueryType.GET_STATUS:
payload = "ÿÿÿÿgetstatus".Select(Convert.ToByte).ToArray();
payload = (Config.CommandPrefixes.RConGetStatus + '\0').Select(Convert.ToByte).ToArray();
break;
case StaticHelpers.QueryType.GET_INFO:
payload = "ÿÿÿÿgetinfo".Select(Convert.ToByte).ToArray();
payload = (Config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray();
break;
}

View File

@ -36,7 +36,7 @@ namespace SharedLibraryCore
Logger = Manager.GetLogger(this.EndPoint);
Logger.WriteInfo(this.ToString());
ServerConfig = config;
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger);
RemoteConnection = new RCon.Connection(IP, Port, Password, Logger, null);
Clients = new List<EFClient>(new EFClient[18]);
Reports = new List<Report>();

View File

@ -347,6 +347,11 @@ namespace SharedLibraryCore
public static Game GetGame(string gameName)
{
if (string.IsNullOrEmpty(gameName))
{
return Game.UKN;
}
if (gameName.Contains("IW4"))
{
return Game.IW4;