adding IW5m parsers

reduce status polling rate
adding preliminary russian localization
small rcon tweak to attempt to send custom encoded messages
removed exception handling in ConvertLong
throttled servers will still attempt to execute events
This commit is contained in:
RaidMax 2018-04-23 00:43:48 -05:00
parent 96d6b03cc5
commit 02ef5a0bf8
14 changed files with 438 additions and 63 deletions

View File

@ -58,6 +58,9 @@
<None Update="Localization\IW4MAdmin.en-US.json"> <None Update="Localization\IW4MAdmin.en-US.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Localization\IW4MAdmin.ru-RU.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using SharedLibraryCore; using SharedLibraryCore;
@ -9,7 +10,7 @@ namespace Application.EventParsers
{ {
class IW4EventParser : IEventParser class IW4EventParser : IEventParser
{ {
public GameEvent GetEvent(Server server, string logLine) public virtual GameEvent GetEvent(Server server, string logLine)
{ {
string[] lineSplit = logLine.Split(';'); string[] lineSplit = logLine.Split(';');
string cleanedEventLine = Regex.Replace(lineSplit[0], @"[0-9]+:[0-9]+\ ", "").Trim(); string cleanedEventLine = Regex.Replace(lineSplit[0], @"[0-9]+:[0-9]+\ ", "").Trim();
@ -73,6 +74,13 @@ namespace Application.EventParsers
if (cleanedEventLine.Contains("InitGame")) if (cleanedEventLine.Contains("InitGame"))
{ {
string dump = cleanedEventLine.Replace("InitGame: ", "");
string[] values = dump.Split('\\', StringSplitOptions.RemoveEmptyEntries);
var dict = new Dictionary<string, string>();
for (int i = 0; i < values.Length; i += 2)
dict.Add(values[i], values[i + 1]);
return new GameEvent() return new GameEvent()
{ {
Type = GameEvent.EventType.MapChange, Type = GameEvent.EventType.MapChange,
@ -85,7 +93,8 @@ namespace Application.EventParsers
{ {
ClientId = 1 ClientId = 1
}, },
Owner = server Owner = server,
Extra = dict
}; };
} }

View File

@ -1,12 +1,53 @@
using SharedLibraryCore.Interfaces; using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
namespace Application.EventParsers namespace Application.EventParsers
{ {
class IW5EventParser : IW4EventParser class IW5EventParser : IW4EventParser
{ {
public override string GetGameDir() => "rzodemo"; public override string GetGameDir() => "logs";
public override GameEvent GetEvent(Server server, string logLine)
{
string cleanedEventLine = Regex.Replace(logLine, @"[0-9]+:[0-9]+\ ", "").Trim();
if (cleanedEventLine.Contains("J;"))
{
string[] lineSplit = cleanedEventLine.Split(';');
int clientNum = Int32.Parse(lineSplit[2]);
var player = new Player()
{
NetworkId = lineSplit[1].ConvertLong(),
ClientNumber = clientNum,
Name = lineSplit[3]
};
return new GameEvent()
{
Type = GameEvent.EventType.Connect,
Origin = new Player()
{
ClientId = 1
},
Target = new Player()
{
ClientId = 1
},
Owner = server,
Extra = player
};
}
else
return base.GetEvent(server, logLine);
}
} }
} }

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -12,15 +13,15 @@ namespace Application.EventParsers
{ {
public GameEvent GetEvent(Server server, string logLine) public GameEvent GetEvent(Server server, string logLine)
{ {
string cleanedLogLine = Regex.Replace(logLine, @"^ *[0-9]+:[0-9]+ *", ""); string cleanedEventLine = Regex.Replace(logLine, @"^ *[0-9]+:[0-9]+ *", "").Trim();
string[] lineSplit = cleanedLogLine.Split(';'); string[] lineSplit = cleanedEventLine.Split(';');
if (lineSplit[0][0] == 'K') if (lineSplit[0][0] == 'K')
{ {
return new GameEvent() return new GameEvent()
{ {
Type = GameEvent.EventType.Script, Type = GameEvent.EventType.Script,
Data = cleanedLogLine, Data = cleanedEventLine,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)), Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)),
Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)), Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
Owner = server Owner = server
@ -32,7 +33,7 @@ namespace Application.EventParsers
return new GameEvent() return new GameEvent()
{ {
Type = GameEvent.EventType.Damage, Type = GameEvent.EventType.Damage,
Data = cleanedLogLine, Data = cleanedEventLine,
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)), Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)),
Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)), Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
Owner = server Owner = server
@ -69,13 +70,15 @@ namespace Application.EventParsers
}; };
} }
/*if (lineSplit[0].Contains("ShutdownGame"))
{
}*/
if (lineSplit[0].Contains("InitGame")) if (lineSplit[0].Contains("InitGame"))
{ {
string dump = cleanedEventLine.Replace("InitGame: ", "");
string[] values = dump.Split('\\', StringSplitOptions.RemoveEmptyEntries);
var dict = new Dictionary<string, string>();
for (int i = 0; i < values.Length; i += 2)
dict.Add(values[i], values[i + 1]);
return new GameEvent() return new GameEvent()
{ {
Type = GameEvent.EventType.MapChange, Type = GameEvent.EventType.MapChange,
@ -88,7 +91,8 @@ namespace Application.EventParsers
{ {
ClientId = 1 ClientId = 1
}, },
Owner = server Owner = server,
Extra = dict
}; };
} }

View File

@ -12,19 +12,21 @@ namespace IW4MAdmin.Application.Localization
public static void Initialize() public static void Initialize()
{ {
string currentLocal = CultureInfo.CurrentCulture.Name; string currentLocal = CultureInfo.CurrentCulture.Name;
#if DEBUG
currentLocal = "ru-RU";
#endif
string localizationFile = $"Localization{Path.DirectorySeparatorChar}IW4MAdmin.{currentLocal}.json"; string localizationFile = $"Localization{Path.DirectorySeparatorChar}IW4MAdmin.{currentLocal}.json";
string localizationContents; string localizationContents;
if (File.Exists(localizationFile)) if (File.Exists(localizationFile))
{ {
localizationContents = File.ReadAllText(localizationFile); localizationContents = File.ReadAllText(localizationFile, Encoding.UTF8);
} }
else else
{ {
localizationFile = $"Localization{Path.DirectorySeparatorChar}IW4MAdmin.en-US.json"; localizationFile = $"Localization{Path.DirectorySeparatorChar}IW4MAdmin.en-US.json";
localizationContents = File.ReadAllText(localizationFile); localizationContents = File.ReadAllText(localizationFile, Encoding.UTF8);
} }
Utilities.CurrentLocalization = Newtonsoft.Json.JsonConvert.DeserializeObject<SharedLibraryCore.Localization.Layout>(localizationContents); Utilities.CurrentLocalization = Newtonsoft.Json.JsonConvert.DeserializeObject<SharedLibraryCore.Localization.Layout>(localizationContents);

View File

@ -0,0 +1,109 @@
{
"LocalizationName": "ru-RU",
"LocalizationSet": {
"MANAGER_VERSION_FAIL": "Не удалось получить последнюю версию IW4MAdmin",
"MANAGER_VERSION_UPDATE": "имеет обновление. Последняя версия",
"MANAGER_VERSION_CURRENT": "Ваша версия",
"MANAGER_VERSION_SUCCESS": "IW4MAdmin обновлен",
"MANAGER_INIT_FAIL": "Неустранимая ошибка при инициализации",
"MANAGER_EXIT": "Нажмите любую клавишу чтобы выйти ...",
"SETUP_ENABLE_WEBFRONT": "Включить веб-интерфейс",
"SETUP_ENABLE_MULTIOWN": "Включить поддержку нескольких владельцев",
"SETUP_ENABLE_STEPPEDPRIV": "Включить последовательную иерархию прав",
"SETUP_ENABLE_CUSTOMSAY": "Включить серверное имя для чата",
"SETUP_SAY_NAME": "Введите серверное имя для чата",
"SETUP_USE_CUSTOMENCODING": "Использовать иную кодировку текста",
"SETUP_ENCODING_STRING": "Введите желаемую кодировку",
"SETUP_ENABLE_VPNS": "Разрешить игрокам подключаться с VPN",
"SETUP_IPHUB_KEY": "Введите iphub.info api-ключ",
"SETUP_DISPLAY_DISCORD": "Отображать ссылку на Discord в веб-интерфейсе",
"SETUP_DISCORD_INVITE": "Введите ссылку-приглашение в Discord",
"SETUP_SERVER_USET6M": "Использовать T6M парсер для Black Ops 2",
"SETUP_SERVER_IP": "Введите IP-адрес сервера",
"SETUP_SERVER_PORT": "введите порт сервера",
"SETUP_SERVER_RCON": "Введите RCon пароль сервера",
"SETUP_SERVER_SAVE": "Конфигурация сохранена, добавить еще?",
"SERVER_KICK_VPNS_NOTALLOWED": "Использование VPN не разрешено на этом сервере",
"SERVER_KICK_TEXT": "Вы исключены",
"SERVER_KICK_MINNAME": "Ваше имя должно содержать хотя бы 3 символа",
"SERVER_KICK_NAME_INUSE": "Ваше имя используется кем-то другим",
"SERVER_KICK_GENERICNAME": "Пожалуйста, смените ваше имя, используя /name",
"SERVER_KICK_CONTROLCHARS": "Ваше имя не должно содержать спецсимволы",
"SERVER_TB_TEXT": "Вы временно забанены",
"SERVER_TB_REMAIN": "Вы временно забанены",
"SERVER_BAN_TEXT": "Вы забанены",
"SERVER_BAN_PREV": "Ранее забанены за",
"SERVER_BAN_APPEAL": "оспорить:",
"SERVER_REPORT_COUNT": "Имеется ^5{0} ^7жалоб за последнее время",
"SERVER_WARNLIMT_REACHED": "Слишком много предупреждений",
"SERVER_WARNING": "предупреждение",
"SERVER_WEBSITE_GENERIC": "веб-сайт этого сервера",
"BROADCAST_ONLINE": "^5IW4MADMIN ^7сейчас ^2ОНЛАЙН",
"BROADCAST_OFFLINE": "IW4MAdmin отключается",
"COMMAND_HELP_SYNTAX": "синтаксис:",
"COMMAND_HELP_OPTIONAL": "опционально",
"COMMAND_UNKNOWN": "Вы ввели неизвестную команду",
"COMMAND_NOACCESS": "У вас нет доступа к этой команде",
"COMMAND_NOTAUTHORIZED": "У вас нет разрешения выполнить эту команду",
"COMMAND_MISSINGARGS": "Приведено недостаточно аргументов",
"COMMAND_TARGET_MULTI": "Это имя использует не один игрок",
"COMMAND_TARGET_NOTFOUND": "Невозможно найти указанного игрока",
"PLUGIN_IMPORTER_NOTFOUND": "Нет загружаемых плагинов",
"PLUGIN_IMPORTER_REGISTERCMD": "Зарегистрированная команда",
"COMMANDS_OWNER_SUCCESS": "Поздравляем, Вы стали владельцем этого сервера!",
"COMMANDS_OWNER_FAIL": "Этот сервер уже имеет владельца",
"COMMANDS_WARN_FAIL": "У вас недостаточно прав чтобы предупреждать!",
"COMMANDS_WARNCLEAR_SUCCESS": "Все предупреждения очищены за",
"COMMANDS_KICK_SUCCESS": "был исключен",
"COMMANDS_KICK_FAIL": "У вас недостаточно прав чтобы исключать!",
"COMMANDS_TEMPBAN_SUCCESS": "был временно забанен за",
"COMMANDS_TEMPBAN_FAIL": "Вы не можете временно банить!",
"COMMANDS_BAN_SUCCESS": "был забанен навсегда",
"COMMANDS_BAN_FAIL": "Вы не можете банить!",
"COMMANDS_UNBAN_SUCCESS": "Успешно разбанен",
"COMMANDS_UNBAN_FAIL": "не забанен",
"COMMANDS_HELP_NOTFOUND": "Не удалось найти эту команду",
"COMMANDS_HELP_MOREINFO": "Введите !help <имя команды>, чтобы узнать синтаксис команды",
"COMMANDS_FASTRESTART_UNMASKED": "перезапуск карты",
"COMMANDS_FASTRESTART_MASKED": "Карта перезапущена",
"COMMANDS_MAPROTATE": "Смена карты через ^55 ^7секунд",
"COMMANDS_SETLEVEL_SELF": "Вы не можете изменить свой уровень",
"COMMANDS_SETLEVEL_OWNER": "Возможен только 1 владелец. Включите возможность нескольких владельцев!",
"COMMANDS_SETLEVEL_STEPPEDDISABLED": "Этот сервер не разрешает вам повыситься",
"COMMANDS_SETLEVEL_LEVELTOOHIGH": "Вы только можете повысить ^5{0} ^7до ^5{1} ^7или понизиться в правах",
"COMMANDS_SETLEVEL_SUCCESS_TARGET": "Поздравляем! Вы были повышены до",
"COMMANDS_SETLEVEL_SUCCESS": "был успешно повышен",
"COMMANDS_SETLEVEL_FAIL": "Указана неверная группа",
"COMMANDS_ADMINS_NONE": "Нет администраторов в сети",
"COMMANDS_MAP_SUCCESS": "Смена карты на",
"COMMANDS_MAP_UKN": "Попытка сменить на неизвестную карту",
"COMMANDS_FIND_MIN": "Пожалуйста, введите хотя бы 3 символа",
"COMMANDS_FIND_EMPTY": "Не найдено игроков",
"COMMANDS_RULES_NONE": "Владелец сервера не установил никаких правил",
"COMMANDS_FLAG_SUCCESS": "Вы были отмечены",
"COMMANDS_FLAG_UNFLAG": "С вас сняли отметку",
"COMMANDS_FLAG_FAIL": "Вы не можете ставить отметки",
"COMMANDS_REPORT_FAIL_CAMP": "Вы не можете пожаловаться на игрока за кемперство",
"COMMANDS_REPORT_FAIL_DUPLICATE": "Вы уже пожаловались на этого игрока",
"COMMANDS_REPORT_FAIL_SELF": "Вы не можете пожаловаться на самого себя",
"COMMANDS_REPORT_FAIL": "Вы не можете пожаловаться",
"COMMANDS_REPORT_SUCCESS": "Спасибо за вашу жалобу, администратор оповещен",
"COMMANDS_REPORTS_CLEAR_SUCCESS": "Жалобы полностью очищены",
"COMMANDS_REPORTS_NONE": "Пока нет жалоб на игроков",
"COMMANDS_MASK_ON": "Вы замаскированы",
"COMMANDS_MASK_OFF": "Маскировка снята",
"COMMANDS_BANINFO_NONE": "Нет активного запрета для этого игрока",
"COMMANDS_BANINO_SUCCESS": "был забанен ^5{0} ^7на:",
"COMMANDS_ALIAS_ALIASES": "Псевдонимы",
"COMMANDS_ALIAS_IPS": "IP",
"COMMANDS_RCON_SUCCESS": " RCon команда успешно отправлена",
"COMMANDS_PLUGINS_LOADED": "Загруженные плагины",
"COMMANDS_IP_SUCCESS": "Ваш внешний IP-адрес",
"COMMANDS_PRUNE_FAIL": "Недопустимое количество неактивных дней",
"COMMANDS_PRUNE_SUCCESS": "неактивные привилегированные пользователи были разжалованы",
"COMMANDS_PASSWORD_FAIL": "Ваш пароль должен содержать не менее 5 символов",
"COMMANDS_PASSWORD_SUCCESS": "Ваш пароль успешно установлен",
"COMMANDS_PING_TARGET": "пинг",
"COMMANDS_PING_SELF": "Ваш пинг"
}
}

View File

@ -6,6 +6,8 @@ using System.Reflection;
using SharedLibraryCore; using SharedLibraryCore;
using SharedLibraryCore.Objects; using SharedLibraryCore.Objects;
using SharedLibraryCore.Database; using SharedLibraryCore.Database;
using System.Text;
using System.Threading;
namespace IW4MAdmin.Application namespace IW4MAdmin.Application
{ {
@ -21,8 +23,10 @@ namespace IW4MAdmin.Application
System.Diagnostics.Process.GetCurrentProcess().PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal; System.Diagnostics.Process.GetCurrentProcess().PriorityClass = System.Diagnostics.ProcessPriorityClass.BelowNormal;
Localization.Configure.Initialize(); Localization.Configure.Initialize();
var loc = Utilities.CurrentLocalization.LocalizationSet; var loc = Utilities.CurrentLocalization.LocalizationSet;
Console.OutputEncoding = Encoding.UTF8;
Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f; Version = Assembly.GetExecutingAssembly().GetName().Version.Major + Assembly.GetExecutingAssembly().GetName().Version.Minor / 10.0f;
Version = Math.Round(Version, 2);
Console.WriteLine("====================================================="); Console.WriteLine("=====================================================");
Console.WriteLine(" IW4M ADMIN"); Console.WriteLine(" IW4M ADMIN");

View File

@ -0,0 +1,166 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using SharedLibraryCore.RCon;
using SharedLibraryCore.Exceptions;
using System.Text;
using System.Linq;
using System.Net.Http;
namespace Application.RconParsers
{
public class IW5MRConParser : IRConParser
{
private static CommandPrefix Prefixes = new CommandPrefix()
{
Tell = "tell {0} {1}",
Say = "say {0}",
Kick = "dropClient {0} \"{1}\"",
Ban = "dropClient {0} \"{1}\"",
TempBan = "dropClient {0} \"{1}\""
};
public CommandPrefix GetCommandPrefixes() => Prefixes;
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
{
await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command, false);
return new string[] { "Command Executed" };
}
public async Task<Dvar<T>> GetDvarAsync<T>(Connection connection, string dvarName)
{
// why can't this be real :(
if (dvarName == "version")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("IW5 MP 1.9 build 461 Fri Sep 14 00:04:28 2012 win-x86", typeof(T))
};
if (dvarName == "shortversion")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("1.9", typeof(T))
};
if (dvarName == "mapname")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("Unknown", typeof(T))
};
if (dvarName == "g_gametype")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("Unknown", typeof(T))
};
if (dvarName == "fs_game")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("", typeof(T))
};
if (dvarName == "g_logsync")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType(1, typeof(T))
};
if (dvarName == "fs_basepath")
return new Dvar<T>(dvarName)
{
Value = (T)Convert.ChangeType("", typeof(T))
};
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
if (LineSplit.Length < 4)
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' });
if (ValueSplit.Length == 0)
{
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string DvarName = dvarName;
string DvarCurrentValue = Regex.Replace(ValueSplit[3].StripColors(), @"\^[0-9]", "");
return new Dvar<T>(DvarName)
{
Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T))
};
}
public async Task<List<Player>> 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)
{
// T6M doesn't respond with anything when a value is set, so we can only hope for the best :c
await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, $"set {dvarName} {dvarValue}", false);
return true;
}
private List<Player> ClientsFromStatus(string[] status)
{
List<Player> StatusPlayers = new List<Player>();
foreach (string statusLine in status)
{
String responseLine = statusLine;
if (Regex.Matches(responseLine, @"^ *\d+", RegexOptions.IgnoreCase).Count > 0) // its a client line!
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
// this happens when the client is in a zombie state
if (playerInfo.Length < 5)
continue;
int clientId = -1;
int Ping = -1;
Int32.TryParse(playerInfo[2], out Ping);
string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(responseLine.Substring(23, 15).StripColors().Trim())));
long networkId = playerInfo[4].ConvertLong();
int.TryParse(playerInfo[0], out clientId);
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
int ipAddress = regex.Value.Split(':')[0].ConvertToIP();
regex = Regex.Match(responseLine, @" +(\d+ +){3}");
int score = Int32.Parse(regex.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries)[0]);
StatusPlayers.Add(new Player()
{
Name = name,
NetworkId = networkId,
ClientNumber = clientId,
IPAddress = ipAddress,
Ping = Ping,
Score = score,
IsBot = networkId < 1
});
}
}
return StatusPlayers;
}
}
}

View File

@ -149,7 +149,6 @@ namespace Application.RconParsers
} }
} }
private List<Player> ClientsFromStatus(string[] status) private List<Player> ClientsFromStatus(string[] status)
{ {
List<Player> StatusPlayers = new List<Player>(); List<Player> StatusPlayers = new List<Player>();

View File

@ -51,8 +51,8 @@ namespace IW4MAdmin
override public async Task<bool> AddPlayer(Player polledPlayer) override public async Task<bool> AddPlayer(Player polledPlayer)
{ {
if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot)|| if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot) ||
polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) || polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) ||
polledPlayer.ClientNumber < 0) polledPlayer.ClientNumber < 0)
{ {
//Logger.WriteDebug($"Skipping client not in connected state {P}"); //Logger.WriteDebug($"Skipping client not in connected state {P}");
@ -360,8 +360,8 @@ namespace IW4MAdmin
public override async Task ExecuteEvent(GameEvent E) public override async Task ExecuteEvent(GameEvent E)
{ {
if (Throttled) //if (Throttled)
return; // return;
await ProcessEvent(E); await ProcessEvent(E);
Manager.GetEventApi().OnServerEvent(this, E); Manager.GetEventApi().OnServerEvent(this, E);
@ -422,7 +422,9 @@ namespace IW4MAdmin
for (int i = 0; i < CurrentPlayers.Count; i++) for (int i = 0; i < CurrentPlayers.Count; i++)
{ {
await AddPlayer(CurrentPlayers[i]); // todo: wait til GUID is included in status to fix this
if (GameName != Game.IW5)
await AddPlayer(CurrentPlayers[i]);
} }
return CurrentPlayers.Count; return CurrentPlayers.Count;
@ -457,15 +459,19 @@ namespace IW4MAdmin
try try
{ {
int polledPlayerCount = await PollPlayersAsync(); // trying to reduce the polling rate as every 450ms is unnecessary
if ((DateTime.Now - LastPoll).TotalSeconds >= 10)
if (ConnectionErrors > 0)
{ {
Logger.WriteVerbose($"Connection has been reestablished with {IP}:{Port}"); int polledPlayerCount = await PollPlayersAsync();
Throttled = false;
if (ConnectionErrors > 0)
{
Logger.WriteVerbose($"Connection has been reestablished with {IP}:{Port}");
Throttled = false;
}
ConnectionErrors = 0;
LastPoll = DateTime.Now;
} }
ConnectionErrors = 0;
LastPoll = DateTime.Now;
} }
catch (NetworkException e) catch (NetworkException e)
@ -587,6 +593,8 @@ namespace IW4MAdmin
public async Task Initialize() public async Task Initialize()
{ {
RconParser = ServerConfig.UseT6MParser ? (IRConParser)new T6MRConParser() : new IW4RConParser(); RconParser = ServerConfig.UseT6MParser ? (IRConParser)new T6MRConParser() : new IW4RConParser();
if (ServerConfig.UseIW5MParser)
RconParser = new IW5MRConParser();
var version = await this.GetDvarAsync<string>("version"); var version = await this.GetDvarAsync<string>("version");
GameName = Utilities.GetGame(version.Value); GameName = Utilities.GetGame(version.Value);
@ -632,9 +640,9 @@ namespace IW4MAdmin
this.CurrentMap = Maps.Find(m => m.Name == mapname.Value) ?? new Map() { Alias = mapname.Value, Name = mapname.Value }; this.CurrentMap = Maps.Find(m => m.Name == mapname.Value) ?? new Map() { Alias = mapname.Value, Name = mapname.Value };
this.MaxClients = maxplayers.Value; this.MaxClients = maxplayers.Value;
this.FSGame = game.Value; this.FSGame = game.Value;
this.Gametype = (await this.GetDvarAsync<string>("g_gametype")).Value; this.Gametype = gametype.Value;
await this.SetDvarAsync("sv_kickbantime", 60); //wait this.SetDvarAsync("sv_kickbantime", 60);
if (logsync.Value == 0 || logfile.Value == string.Empty) if (logsync.Value == 0 || logfile.Value == string.Empty)
{ {
@ -650,11 +658,19 @@ namespace IW4MAdmin
CustomCallback = await ScriptLoaded(); CustomCallback = await ScriptLoaded();
string mainPath = EventParser.GetGameDir(); string mainPath = EventParser.GetGameDir();
#if DEBUG #if DEBUG
basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II"; // basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II";
#endif #endif
string logPath = game.Value == string.Empty ? string logPath;
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" : if (GameName == Game.IW5)
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Value.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}"; {
logPath = ServerConfig.ManualLogPath;
}
else
{
logPath = game.Value == string.Empty ?
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" :
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{game.Value.Replace('/', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{logfile.Value}";
}
// hopefully fix wine drive name mangling // hopefully fix wine drive name mangling
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -676,7 +692,7 @@ namespace IW4MAdmin
Logger.WriteInfo($"Log file is {logPath}"); Logger.WriteInfo($"Log file is {logPath}");
#if DEBUG #if DEBUG
// LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php"); // LogFile = new RemoteFile("https://raidmax.org/IW4MAdmin/getlog.php");
#else #else
await Broadcast(loc["BROADCAST_ONLINE"]); await Broadcast(loc["BROADCAST_ONLINE"]);
#endif #endif
@ -687,15 +703,33 @@ namespace IW4MAdmin
{ {
if (E.Type == GameEvent.EventType.Connect) if (E.Type == GameEvent.EventType.Connect)
{ {
ChatHistory.Add(new ChatInfo() // special case for IW5 when connect is from the log
if (E.Extra != null)
{ {
Name = E.Origin.Name, var logClient = (Player)E.Extra;
Message = "CONNECTED", var client = (await this.GetStatusAsync())
Time = DateTime.UtcNow .Single(c => c.ClientNumber == logClient.ClientNumber &&
}); c.Name == logClient.Name);
client.NetworkId = logClient.NetworkId;
if (E.Origin.Level > Player.Permission.Moderator) await AddPlayer(client);
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
// hack: to prevent plugins from registering it as a real connect
E.Type = GameEvent.EventType.Unknown;
}
else
{
ChatHistory.Add(new ChatInfo()
{
Name = E.Origin.Name,
Message = "CONNECTED",
Time = DateTime.UtcNow
});
if (E.Origin.Level > Player.Permission.Moderator)
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
}
} }
else if (E.Type == GameEvent.EventType.Disconnect) else if (E.Type == GameEvent.EventType.Disconnect)
@ -790,11 +824,11 @@ namespace IW4MAdmin
{ {
Logger.WriteInfo($"New map loaded - {ClientNum} active players"); Logger.WriteInfo($"New map loaded - {ClientNum} active players");
Gametype = (await this.GetDvarAsync<string>("g_gametype")).Value.StripColors(); var dict = (Dictionary<string, string>)E.Extra;
Hostname = (await this.GetDvarAsync<string>("sv_hostname")).Value.StripColors(); Gametype = dict["g_gametype"].StripColors();
FSGame = (await this.GetDvarAsync<string>("fs_game")).Value.StripColors(); Hostname = dict["sv_hostname"].StripColors();
string mapname = this.GetDvarAsync<string>("mapname").Result.Value; string mapname = dict["mapname"].StripColors();
CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname }; CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map() { Alias = mapname, Name = mapname };
} }

View File

@ -11,10 +11,17 @@ namespace SharedLibraryCore.Configuration
public List<string> Rules { get; set; } public List<string> Rules { get; set; }
public List<string> AutoMessages { get; set; } public List<string> AutoMessages { get; set; }
public bool UseT6MParser { get; set; } public bool UseT6MParser { get; set; }
public bool UseIW5MParser { get; set; }
public string ManualLogPath { get; set; }
public IBaseConfiguration Generate() public IBaseConfiguration Generate()
{ {
UseT6MParser = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationSet["SETUP_SERVER_USET6M"]); UseT6MParser = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationSet["SETUP_SERVER_USET6M"]);
if (!UseT6MParser)
UseIW5MParser = Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationSet["SETUP_SERVER_USEIW5M"]);
if (UseIW5MParser)
ManualLogPath = Utilities.PromptString(Utilities.CurrentLocalization.LocalizationSet["SETUP_SERVER_MANUALLOG"]);
return this; return this;
} }

View File

@ -172,19 +172,23 @@ namespace SharedLibraryCore.RCon
OnSent.Reset(); OnSent.Reset();
OnReceived.Reset(); OnReceived.Reset();
string queryString = ""; string queryString = "";
byte[] payload = null;
switch (type) switch (type)
{ {
case StaticHelpers.QueryType.DVAR: case StaticHelpers.QueryType.DVAR:
case StaticHelpers.QueryType.COMMAND: case StaticHelpers.QueryType.COMMAND:
queryString = $"ÿÿÿÿrcon {RConPassword} {parameters}"; var header = "ÿÿÿÿrcon ".Select(Convert.ToByte).ToList();
byte[] p = Utilities.EncodingType.GetBytes($"{RConPassword} {parameters}");
header.AddRange(p);
payload = header.ToArray();
break; break;
case StaticHelpers.QueryType.GET_STATUS: case StaticHelpers.QueryType.GET_STATUS:
queryString = "ÿÿÿÿgetstatus"; payload = "ÿÿÿÿgetstatus".Select(Convert.ToByte).ToArray();
break; break;
} }
byte[] payload = queryString.Select(Convert.ToByte).ToArray(); // byte[] payload = Utilities.EncodingType.GetBytes(queryString); // queryString.Select(Convert.ToByte).ToArray();
retrySend: retrySend:
try try

View File

@ -290,8 +290,8 @@ namespace SharedLibraryCore
// Info // Info
public string Hostname { get; protected set; } public string Hostname { get; protected set; }
public string Website { get; protected set; } public string Website { get; protected set; }
public string Gametype { get; protected set; } public string Gametype { get; set; }
public Map CurrentMap { get; protected set; } public Map CurrentMap { get; set; }
public int ClientNum public int ClientNum
{ {
get get

View File

@ -9,6 +9,7 @@ using static SharedLibraryCore.Server;
using System.Reflection; using System.Reflection;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Globalization;
namespace SharedLibraryCore namespace SharedLibraryCore
{ {
@ -181,16 +182,8 @@ namespace SharedLibraryCore
public static long ConvertLong(this string str) public static long ConvertLong(this string str)
{ {
try Int64.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long id);
{ return id;
return Int64.Parse(str, System.Globalization.NumberStyles.HexNumber);
}
catch (FormatException)
{
return -1;
}
} }
public static int ConvertToIP(this string str) public static int ConvertToIP(this string str)