123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- /**
- * [CSRD] Name Filter
- *
- * An in-house plugin that tidies up names.
- * Made in response to a few script kiddies that are (ab)using newlines and other non-printing
- * characters in their name.
- *
- * If their existing name has no printable characters, their name will be changed to
- * "unprintable" plus their userid and their name will be locked.
- */
- #pragma semicolon 1
- #include <sourcemod>
- #include <sdktools_functions>
- #pragma newdecls required
- #define PLUGIN_VERSION "1.0.5"
- public Plugin myinfo = {
- name = "[CSRD] Name Filter",
- author = "nosoop",
- description = "Filters out rather unscrupulous characters from names and locks the name if "
- ... "they are using a completely non-printable name.",
- version = PLUGIN_VERSION,
- url = "https://pika.nom-nom-nom.us/git/"
- }
- #define UNPRINTABLE_NAME_USER_FORMAT "unprintable (%d)"
- int g_MultiByteNonPrinting[] = {
- 0xe2a080, // braille pattern blank U+2800
- 0
- };
- public void OnPluginStart() {
- for (int i = 1; i <= MaxClients; i++) {
- if (IsClientConnected(i)) {
- OnNameUpdated(i);
- }
- }
- }
- public void OnClientConnected(int client) {
- OnNameUpdated(client);
- }
- public void OnClientSettingsChanged(int client) {
- OnNameUpdated(client);
- }
- void OnNameUpdated(int client) {
- char currentName[MAX_NAME_LENGTH], newNameBuffer[MAX_NAME_LENGTH];
-
- GetClientName(client, currentName, sizeof(currentName));
-
- bool bNameChanged;
-
- /**
- * Copies valid characters into another buffer (while also dealing with Unicode).
- * The buffer is used if any characters are filtered.
- */
- for (int i = 0; i < strlen(currentName); /* ... */) {
- int fullChar, nCharBytes;
- fullChar = GetMultiByteCharacterAt(currentName, i, nCharBytes);
-
- bool bCharFiltered;
-
- /**
- * Filter out characters.
- *
- * Note that while Steam may filter out whitespace characters in its interface, other
- * external applications may be used to bypass this restriction.
- *
- * (We use `sm_rename` to test the filtering process.)
- */
- switch (nCharBytes) {
- case 3: {
- // filter unicode invisible characters
- bCharFiltered |= IsUnicodeCharInvisible(fullChar);
- }
- case 1: {
- // filter out newlines
- bCharFiltered |= (fullChar == 0x0d || fullChar == 0x0a);
- }
- default: {
- // unhandled
- }
- }
-
- if (!bCharFiltered) {
- // it's a character we're not filtering
- StrCatMultiByteChar(newNameBuffer, sizeof(newNameBuffer), fullChar, nCharBytes);
- }
-
- bNameChanged |= bCharFiltered;
-
- i += nCharBytes;
- }
-
- if (bNameChanged) {
- // Remove surrounding whitespace
- TrimString(newNameBuffer);
-
- // TODO remove duplicate whitespace
-
- // If there's no printable characters in the buffer, the player is a total dongbus.
- if (strlen(newNameBuffer) == 0) {
- Format(newNameBuffer, sizeof(newNameBuffer), UNPRINTABLE_NAME_USER_FORMAT,
- GetClientUserId(client));
-
- SetClientNameLock(client);
- }
-
- SetClientName(client, newNameBuffer);
- }
- // TODO else not name changed, but check if we can do other non-filter stuff? (trim)
- }
- /**
- * Determines if the specified character is non-printing (whitespace or not supported).
- *
- * References:
- * http://www.unicode.org/faq/unsup_char.html
- * https://en.wikipedia.org/wiki/Whitespace_character
- */
- bool IsUnicodeCharInvisible(int uchar) {
- // handle invisible math operators, &c. (U+205F ... U+206F)
- if (uchar >= 0xe2819f && uchar <= 0xe281af) {
- return true;
- }
-
- // handle other invisible characters
- int c;
- do {
- if (uchar == g_MultiByteNonPrinting[c]) {
- return true;
- }
- } while (g_MultiByteNonPrinting[++c] != 0);
-
- return false;
- }
- /**
- * Attempts to lock the specified client's name with the existing `namelockid` command.
- */
- stock bool SetClientNameLock(int client, bool locked = true) {
- if (CommandExists("namelockid")) {
- ServerCommand("namelockid %d %d", GetClientUserId(client), locked ? 0 : 1);
- return true;
- }
- return false;
- }
- /**
- * Gets a possibly multi-byte character at the specified position in a string as an integer.
- *
- * If the character is an ASCII character, the integer representation of the character will be
- * returned, and the number of characters read will be 1.
- *
- * This stock is made specifically for the purpose of handling strings with multi-byte
- * characters.
- */
- stock int GetMultiByteCharacterAt(const char[] text, int pos, int &read = 0) {
- int nCharBytes, charOutput;
-
- nCharBytes = GetCharBytes(text[pos]);
-
- for (int b = 0; b < nCharBytes; b++) {
- charOutput = charOutput << 8;
-
- // read in the current byte
- charOutput |= text[pos + b] & 0xFF;
-
- read++;
- }
-
- return charOutput;
- }
- /**
- * Appends a multi-byte character to a string.
- */
- stock void StrCatMultiByteChar(char[] buffer, int maxlen, int character, int size) {
- char[] appended = new char[size];
-
- MultiByteCharacterToCharArray(appended, size, character);
-
- StrCat(buffer, maxlen, appended);
- }
- /**
- * Converts a multi-byte character to a char array.
- */
- stock void MultiByteCharacterToCharArray(char[] buffer, int charsize, int character) {
- for (int i = 0; i < charsize; i++) {
- buffer[i] = (character >> ((charsize - i - 1) * 8 )) & 0xFF;
- }
- }
|