Profanity deterrent kick players with offensive names

status parsing with Regex in IW4 is much cleaner
fixed tempban not always kicking
made plugin event tasks parallel
This commit is contained in:
RaidMax 2018-04-29 15:44:04 -05:00
parent 35e7f57156
commit 3a463be7f8
8 changed files with 94 additions and 51 deletions

View File

@ -192,6 +192,9 @@
"GLOBAL_ERROR": "Error",
"GLOBAL_WARNING": "Warning",
"GLOBAL_INFO": "Info",
"GLOBAL_VERBOSE": "Verbose"
"GLOBAL_VERBOSE": "Verbose",
"MANAGER_CONSOLE_NOSERV": "No servers are currently being monitored",
"SERVER_PLUGIN_ERROR": "A plugin generated an error"
}
}

View File

@ -40,7 +40,7 @@
"SERVER_WARNLIMT_REACHED": "Слишком много предупреждений",
"SERVER_WARNING": "Предупреждение",
"SERVER_WEBSITE_GENERIC": "веб-сайт этого сервера",
"BROADCAST_ONLINE": "^5IW4MADMIN ^7сейчас ^В СЕТИ",
"BROADCAST_ONLINE": "^5IW4MADMIN ^7сейчас СЕТИ",
"BROADCAST_OFFLINE": "IW4MAdmin отключается",
"COMMAND_HELP_SYNTAX": "синтаксис:",
"COMMAND_HELP_OPTIONAL": "опционально",

View File

@ -23,6 +23,11 @@ namespace Application.Misc
string response = await RequestClient.GetStringAsync($"http://v2.api.iphub.info/ip/{ip}");
var responseJson = JsonConvert.DeserializeObject<JObject>(response);
int blockType = Convert.ToInt32(responseJson["block"]);
if (responseJson.ContainsKey("isp"))
{
if (responseJson["isp"].ToString() == "TSF-IP-CORE")
return true;
}
return blockType == 1;
}
}

View File

@ -22,7 +22,9 @@ namespace Application.RconParsers
Ban = "clientkick {0} \"{1}\"",
TempBan = "tempbanclient {0} \"{1}\""
};
private static string StatusRegex = @"^( *[0-9]+) +([0-9]+) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){16}|bot[0-9]+) +(.{0,20}) +([0-9]+) +(\d+\.\d+\.\d+.\d+\:\d{1,5}|0+.0+:\d{1,5}) +(-*[0-9]+) +([0-9]+) *$";
public async Task<string[]> ExecuteCommandAsync(Connection connection, string command)
{
return (await connection.SendQueryAsync(StaticHelpers.QueryType.COMMAND, command)).Skip(1).ToArray();
@ -82,30 +84,33 @@ namespace Application.RconParsers
{
String responseLine = S.Trim();
if (Regex.Matches(responseLine, @" *^\d+", RegexOptions.IgnoreCase).Count > 0)
var regex = Regex.Match(responseLine, StatusRegex, RegexOptions.IgnoreCase);
if (regex.Success)
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (playerInfo.Length < 4)
continue;
int cID = -1;
int Ping = -1;
Int32.TryParse(playerInfo[2], out Ping);
String cName = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(responseLine.Substring(46, 18).StripColors().Trim())));
long npID = Regex.Match(responseLine, @"([a-z]|[0-9]){16}|bot[0-9]+", RegexOptions.IgnoreCase).Value.ConvertLong();
int.TryParse(playerInfo[0], out cID);
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
int cIP = regex.Value.Split(':')[0].ConvertToIP();
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
int score = Int32.Parse(regex.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]);
int clientNumber = int.Parse(regex.Groups[1].Value);
int score = int.Parse(regex.Groups[2].Value);
int ping = 999;
// their state can be CNCT, ZMBI etc
if (regex.Groups[3].Value.Length <= 3)
{
ping = int.Parse(regex.Groups[3].Value);
}
long networkId = regex.Groups[4].Value.ConvertLong();
string name = regex.Groups[5].Value.StripColors().Trim();
int ip = regex.Groups[7].Value.Split(':')[0].ConvertToIP();
Player P = new Player()
{
Name = cName,
NetworkId = npID,
ClientNumber = cID,
IPAddress = cIP,
Ping = Ping,
Name = name,
NetworkId = networkId,
ClientNumber = clientNumber,
IPAddress = ip,
Ping = ping,
Score = score,
IsBot = cIP == 0
IsBot = ip == 0
};
StatusPlayers.Add(P);

View File

@ -178,6 +178,9 @@ namespace IW4MAdmin
if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban)
await player.Ban($"{loc["SERVER_BAN_PREV"]} {currentBan.Offense}", autoKickClient);
// they didn't fully connect so empty their slot
Players[player.ClientNumber] = null;
return true;
}
@ -186,7 +189,7 @@ namespace IW4MAdmin
var e = new GameEvent(GameEvent.EventType.Connect, "", player, null, this);
Manager.GetEventHandler().AddEvent(e);
// e.OnProcessed.Wait();
e.OnProcessed.Wait();
if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs &&
await VPNCheck.UsingVPN(player.IPAddressString, Manager.GetApplicationSettings().Configuration().IPHubAPIKey))
@ -362,14 +365,15 @@ namespace IW4MAdmin
await ProcessEvent(E);
Manager.GetEventApi().OnServerEvent(this, E);
foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
// this allows us to catch exceptions but still run it parallel
async Task pluginHandlingAsync(Task onEvent, string pluginName)
{
try
{
if (cts.IsCancellationRequested)
break;
return;
await P.OnEventAsync(E, this);
await onEvent;
}
// this happens if a plugin (login) wants to stop commands from executing
@ -381,7 +385,7 @@ namespace IW4MAdmin
catch (Exception Except)
{
Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
Logger.WriteError($"{loc["SERVER_PLUGIN_ERROR"]} [{pluginName}]");
Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
while (Except.InnerException != null)
@ -389,11 +393,15 @@ namespace IW4MAdmin
Except = Except.InnerException;
Logger.WriteDebug($"Inner exception: {Except.Message}");
}
continue;
}
}
var pluginTasks = SharedLibraryCore.Plugins.PluginImporter.ActivePlugins.
Select(p => pluginHandlingAsync(p.OnEventAsync(E, this), p.Name));
// execute all the plugin updates simultaneously
await Task.WhenAll(pluginTasks);
// hack: this prevents commands from getting executing that 'shouldn't' be
if (E.Type == GameEvent.EventType.Command &&
E.Extra != null &&
@ -402,7 +410,6 @@ namespace IW4MAdmin
{
await (((Command)E.Extra).ExecuteAsync(E));
}
}
/// <summary>
@ -599,27 +606,35 @@ namespace IW4MAdmin
override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
{
// this isn't really used anymore
this.cts = cts;
try
{
if (Manager.ShutdownRequested())
{
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
await plugin.OnUnloadAsync();
for (int i = 0; i < Players.Count; i++)
await RemovePlayer(i);
}
// only check every 2 minutes if the server doesn't seem to be responding
if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
return true;
try
{
// trying to reduce the polling rate as every 450ms is unnecessary
if ((DateTime.Now - LastPoll).TotalSeconds >= 10)
{
int polledPlayerCount = await PollPlayersAsync();
int polledPlayerCount = await PollPlayersAsync();
if (ConnectionErrors > 0)
{
Logger.WriteVerbose($"{loc["MANAGER_CONNECTION_REST"]} {IP}:{Port}");
Throttled = false;
}
ConnectionErrors = 0;
LastPoll = DateTime.Now;
if (ConnectionErrors > 0)
{
Logger.WriteVerbose($"{loc["MANAGER_CONNECTION_REST"]} {IP}:{Port}");
Throttled = false;
}
ConnectionErrors = 0;
LastPoll = DateTime.Now;
}
catch (NetworkException e)
@ -637,6 +652,8 @@ namespace IW4MAdmin
LastMessage = DateTime.Now - start;
lastCount = DateTime.Now;
// todo: re-enable on tick
/*
if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000)
{
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
@ -647,8 +664,9 @@ namespace IW4MAdmin
await Plugin.OnTickAsync(this);
}
tickTime = DateTime.Now;
}
}*/
// update the player history
if ((lastCount - playerCountStart).TotalMinutes >= SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval)
{
while (PlayerHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
@ -657,6 +675,7 @@ namespace IW4MAdmin
playerCountStart = DateTime.Now;
}
// send out broadcast messages
if (LastMessage.TotalSeconds > Manager.GetApplicationSettings().Configuration().AutoMessagePeriod
&& BroadcastMessages.Count > 0
&& ClientNum > 0)
@ -666,14 +685,6 @@ namespace IW4MAdmin
start = DateTime.Now;
}
if (Manager.ShutdownRequested())
{
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
await plugin.OnUnloadAsync();
for (int i = 0; i < Players.Count; i++)
await RemovePlayer(i);
}
return true;
}

View File

@ -33,6 +33,16 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
S.Logger.WriteWarning("Could not add client to profanity tracking");
}
var objectionalWords = Settings.Configuration().OffensiveWords;
bool containsObjectionalWord = objectionalWords.FirstOrDefault(w => E.Origin.Name.ToLower().Contains(w)) != null;
if (containsObjectionalWord)
{
await E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
{
ClientId = 1
});
};
}
if (E.Type == GameEvent.EventType.Disconnect)

View File

@ -467,6 +467,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// calculate the new weight against average times the weight against play time
clientStats.SPM = (killSPM * SPMAgainstPlayWeight) + (clientStats.SPM * (1 - SPMAgainstPlayWeight));
if (clientStats.SPM < 0)
{
Log.WriteWarning("[StatManager:UpdateStats] clientStats SPM < 0");
Log.WriteDebug($"{scoreDifference}-{clientStats.RoundScore} - {clientStats.LastScore} - {clientStats.SessionScore}");
clientStats.SPM = 0;
}
clientStats.SPM = Math.Round(clientStats.SPM, 3);
clientStats.Skill = Math.Round((clientStats.SPM * KDRWeight), 3);

View File

@ -37,6 +37,7 @@ namespace WebfrontCore.Controllers
CurrentServer = server,
Name = Client.Name
};
var remoteEvent = new GameEvent()
{
Type = GameEvent.EventType.Say,