fix: Fixes and optimizes NameVerification and ProfanityProtection (#2153)
### Summary Significantly improves the performance of NameVerification & ProfanityProtection: ```cs | Method | Mean | Error | StdDev | |--------------- |----------:|----------:|----------:| | ValidateName | 728.34 ns | 13.244 ns | 11.059 ns | | ValidateNameSV | 26.62 ns | 0.233 ns | 0.207 ns | ```
This commit is contained in:
parent
0ce2a62a76
commit
a94814a7ef
9 changed files with 624 additions and 440 deletions
|
|
@ -1,140 +1,147 @@
|
|||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace Server.Misc
|
||||
namespace Server.Misc;
|
||||
|
||||
public enum ProfanityAction
|
||||
{
|
||||
public enum ProfanityAction
|
||||
None, // no action taken
|
||||
Disallow, // speech is not displayed
|
||||
Criminal, // makes the player criminal, not killable by guards
|
||||
CriminalAction, // makes the player criminal, can be killed by guards
|
||||
Disconnect, // player is kicked
|
||||
Other // some other implementation
|
||||
}
|
||||
|
||||
public static class ProfanityProtection
|
||||
{
|
||||
private static bool Enabled;
|
||||
private static ProfanityAction Action;
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
None, // no action taken
|
||||
Disallow, // speech is not displayed
|
||||
Criminal, // makes the player criminal, not killable by guards
|
||||
CriminalAction, // makes the player criminal, can be killed by guards
|
||||
Disconnect, // player is kicked
|
||||
Other // some other implementation
|
||||
Enabled = ServerConfiguration.GetSetting("profanityProtection.enabled", false);
|
||||
Action = ServerConfiguration.GetSetting("profanityProtection.action", ProfanityAction.Disallow);
|
||||
|
||||
if (Enabled)
|
||||
{
|
||||
EventSink.Speech += EventSink_Speech;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProfanityProtection
|
||||
// Used by the guild system
|
||||
public static readonly SearchValues<char> Exceptions = SearchValues.Create(
|
||||
' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']',
|
||||
'{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@'
|
||||
);
|
||||
|
||||
public static readonly string[] Disallowed =
|
||||
[
|
||||
"jigaboo",
|
||||
"chigaboo",
|
||||
"wop",
|
||||
"kyke",
|
||||
"kike",
|
||||
"tit",
|
||||
"spic",
|
||||
"prick",
|
||||
"piss",
|
||||
"lezbo",
|
||||
"lesbo",
|
||||
"felatio",
|
||||
"dyke",
|
||||
"dildo",
|
||||
"chinc",
|
||||
"chink",
|
||||
"cunnilingus",
|
||||
"cum",
|
||||
"cocksucker",
|
||||
"cock",
|
||||
"clitoris",
|
||||
"clit",
|
||||
"ass",
|
||||
"hitler",
|
||||
"penis",
|
||||
"nigga",
|
||||
"nigger",
|
||||
"klit",
|
||||
"kunt",
|
||||
"jiz",
|
||||
"jism",
|
||||
"jerkoff",
|
||||
"jackoff",
|
||||
"goddamn",
|
||||
"fag",
|
||||
"blowjob",
|
||||
"bitch",
|
||||
"asshole",
|
||||
"dick",
|
||||
"pussy",
|
||||
"snatch",
|
||||
"cunt",
|
||||
"twat",
|
||||
"shit",
|
||||
"fuck",
|
||||
];
|
||||
|
||||
public static readonly SearchValues<string> DisallowedSearchValues = SearchValues.Create(
|
||||
Disallowed,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
|
||||
private static bool OnProfanityDetected(Mobile from, string speech)
|
||||
{
|
||||
// TODO: Move this to configuration
|
||||
private static readonly bool Enabled = false;
|
||||
|
||||
private static readonly ProfanityAction
|
||||
Action = ProfanityAction.Disallow; // change here what to do when profanity is detected
|
||||
|
||||
public static char[] Exceptions { get; } =
|
||||
switch (Action)
|
||||
{
|
||||
' ', '-', '.', '\'', '"', ',', '_', '+', '=', '~', '`', '!', '^', '*', '\\', '/', ';', ':', '<', '>', '[', ']',
|
||||
'{', '}', '?', '|', '(', ')', '%', '$', '&', '#', '@'
|
||||
};
|
||||
case ProfanityAction.None: return true;
|
||||
case ProfanityAction.Disallow: return false;
|
||||
case ProfanityAction.Criminal:
|
||||
from.Criminal = true;
|
||||
return true;
|
||||
case ProfanityAction.CriminalAction:
|
||||
from.CriminalAction(false);
|
||||
return true;
|
||||
case ProfanityAction.Disconnect:
|
||||
{
|
||||
from.NetState?.Disconnect("Using profanity.");
|
||||
|
||||
public static string[] StartDisallowed { get; } = Array.Empty<string>();
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string[] Disallowed { get; } =
|
||||
public static bool ContainsProfanity(ReadOnlySpan<char> speech) =>
|
||||
speech.Length > 0 &&
|
||||
!NameVerification.Validate(
|
||||
speech,
|
||||
1,
|
||||
int.MaxValue,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
int.MaxValue, // allow all non-alphanumeric characters
|
||||
null,
|
||||
Disallowed,
|
||||
DisallowedSearchValues
|
||||
);
|
||||
|
||||
private static void EventSink_Speech(SpeechEventArgs e)
|
||||
{
|
||||
var from = e.Mobile;
|
||||
|
||||
if (from.AccessLevel > AccessLevel.Player)
|
||||
{
|
||||
"jigaboo",
|
||||
"chigaboo",
|
||||
"wop",
|
||||
"kyke",
|
||||
"kike",
|
||||
"tit",
|
||||
"spic",
|
||||
"prick",
|
||||
"piss",
|
||||
"lezbo",
|
||||
"lesbo",
|
||||
"felatio",
|
||||
"dyke",
|
||||
"dildo",
|
||||
"chinc",
|
||||
"chink",
|
||||
"cunnilingus",
|
||||
"cum",
|
||||
"cocksucker",
|
||||
"cock",
|
||||
"clitoris",
|
||||
"clit",
|
||||
"ass",
|
||||
"hitler",
|
||||
"penis",
|
||||
"nigga",
|
||||
"nigger",
|
||||
"klit",
|
||||
"kunt",
|
||||
"jiz",
|
||||
"jism",
|
||||
"jerkoff",
|
||||
"jackoff",
|
||||
"goddamn",
|
||||
"fag",
|
||||
"blowjob",
|
||||
"bitch",
|
||||
"asshole",
|
||||
"dick",
|
||||
"pussy",
|
||||
"snatch",
|
||||
"cunt",
|
||||
"twat",
|
||||
"shit",
|
||||
"fuck"
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
if (Enabled)
|
||||
{
|
||||
EventSink.Speech += EventSink_Speech;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private static bool OnProfanityDetected(Mobile from, string speech)
|
||||
if (ContainsProfanity(e.Speech))
|
||||
{
|
||||
switch (Action)
|
||||
{
|
||||
case ProfanityAction.None: return true;
|
||||
case ProfanityAction.Disallow: return false;
|
||||
case ProfanityAction.Criminal:
|
||||
from.Criminal = true;
|
||||
return true;
|
||||
case ProfanityAction.CriminalAction:
|
||||
from.CriminalAction(false);
|
||||
return true;
|
||||
case ProfanityAction.Disconnect:
|
||||
{
|
||||
from.NetState?.Disconnect("Using profanity.");
|
||||
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
case ProfanityAction.Other: // TODO: Provide custom implementation if this is chosen
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void EventSink_Speech(SpeechEventArgs e)
|
||||
{
|
||||
var from = e.Mobile;
|
||||
|
||||
if (from.AccessLevel > AccessLevel.Player)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NameVerification.Validate(
|
||||
e.Speech,
|
||||
0,
|
||||
int.MaxValue,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
int.MaxValue,
|
||||
Exceptions,
|
||||
Disallowed,
|
||||
StartDisallowed
|
||||
))
|
||||
{
|
||||
e.Blocked = !OnProfanityDetected(from, e.Speech);
|
||||
}
|
||||
e.Blocked = !OnProfanityDetected(from, e.Speech);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue