csrd_name_filter.sp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /**
  2. * [CSRD] Name Filter
  3. *
  4. * An in-house plugin that tidies up names.
  5. * Made in response to a few script kiddies that are (ab)using newlines and other non-printing
  6. * characters in their name.
  7. *
  8. * If their existing name has no printable characters, their name will be changed to
  9. * "unprintable" plus their userid and their name will be locked.
  10. */
  11. #pragma semicolon 1
  12. #include <sourcemod>
  13. #include <sdktools_functions>
  14. #pragma newdecls required
  15. #define PLUGIN_VERSION "1.0.2"
  16. public Plugin myinfo = {
  17. name = "[CSRD] Name Filter",
  18. author = "nosoop",
  19. description = "Filters out rather unscrupulous characters from names and locks the name if "
  20. ... "they are using a completely non-printable name.",
  21. version = PLUGIN_VERSION,
  22. url = "https://pika.nom-nom-nom.us/git/"
  23. }
  24. #define UNPRINTABLE_NAME_USER_FORMAT "unprintable (%d)"
  25. int g_FilteredMultiByteWhitespace[] = {
  26. 0xe2a080, // braille pattern blank U+2800
  27. 0xe2819f, // medium mathematical space U+205F
  28. /* invisible math operators, &c. (char & 0xe281a0) */
  29. 0xe281a0, // word joiner U+2060
  30. 0xe281a1, // function application U+2061
  31. 0xe281a2, // invisible times U+2062
  32. 0xe281a3, // invisible separator U+2063
  33. 0xe281a4, // invisible plus U+2064
  34. 0xe281aa, // inhibit symmetric swapping U+206A
  35. 0xe281ab, // activate symmetric swapping U+206B
  36. 0xe281ac, // inhibit arabic form shaping U+206C
  37. 0xe281ad, // activate arabic form shaping U+206D
  38. 0xe281ae, // national digit shapes U+206E
  39. 0xe281af, // nominal digit shapes U+206F
  40. 0
  41. };
  42. public void OnPluginStart() {
  43. for (int i = 1; i <= MaxClients; i++) {
  44. if (IsClientConnected(i)) {
  45. OnNameUpdated(i);
  46. }
  47. }
  48. }
  49. public void OnClientConnected(int client) {
  50. OnNameUpdated(client);
  51. }
  52. public void OnClientSettingsChanged(int client) {
  53. OnNameUpdated(client);
  54. }
  55. void OnNameUpdated(int client) {
  56. char currentName[MAX_NAME_LENGTH], newNameBuffer[MAX_NAME_LENGTH];
  57. GetClientName(client, currentName, sizeof(currentName));
  58. bool bNameChanged;
  59. /**
  60. * Copies valid characters into another buffer (while also dealing with Unicode).
  61. * The buffer is used if any characters are filtered.
  62. */
  63. for (int i = 0; i < strlen(currentName); /* ... */) {
  64. int fullChar, nCharBytes;
  65. fullChar = GetMultiByteCharacterAt(currentName, i, nCharBytes);
  66. bool bCharFiltered;
  67. // Filter out non-printing characters that can't be handled elsewhere.
  68. switch (nCharBytes) {
  69. case 3: {
  70. // filter unicode invisible characters
  71. bCharFiltered |= IsUnicodeCharInvisible(fullChar);
  72. }
  73. case 1: {
  74. // filter out newlines
  75. bCharFiltered |= (fullChar == 0x0d || fullChar == 0x0a);
  76. }
  77. default: {
  78. // unhandled
  79. }
  80. }
  81. if (!bCharFiltered) {
  82. // it's a character we're not filtering
  83. StrCatMultiByteChar(newNameBuffer, sizeof(newNameBuffer), fullChar, nCharBytes);
  84. } else {
  85. // we filtered this one
  86. // we could be as rude as they are in using unprintables, but we're nice
  87. // TODO check if the last character in new buffer is a space to prevent multiples
  88. StrCat(newNameBuffer, sizeof(newNameBuffer), " ");
  89. }
  90. bNameChanged |= bCharFiltered;
  91. i += nCharBytes;
  92. }
  93. if (bNameChanged) {
  94. // Remove surrounding whitespace
  95. TrimString(newNameBuffer);
  96. // If there's no printable characters in the buffer, the player is a total dongbus.
  97. if (strlen(newNameBuffer) == 0) {
  98. Format(newNameBuffer, sizeof(newNameBuffer), UNPRINTABLE_NAME_USER_FORMAT,
  99. GetClientUserId(client));
  100. SetClientNameLock(client);
  101. }
  102. SetClientName(client, newNameBuffer);
  103. }
  104. // TODO else not name changed, but check if we can do other non-filter stuff? (trim)
  105. }
  106. bool IsUnicodeCharInvisible(int uchar) {
  107. int c;
  108. do {
  109. if (uchar == g_FilteredMultiByteWhitespace[c]) {
  110. return true;
  111. }
  112. } while (g_FilteredMultiByteWhitespace[++c] != 0);
  113. return false;
  114. }
  115. /**
  116. * Attempts to lock the specified client's name with the existing `namelockid` command.
  117. */
  118. stock bool SetClientNameLock(int client, bool locked = true) {
  119. if (CommandExists("namelockid")) {
  120. ServerCommand("namelockid %d %d", GetClientUserId(client), locked ? 0 : 1);
  121. return true;
  122. }
  123. return false;
  124. }
  125. /**
  126. * Gets a possibly multi-byte character at the specified position in a string as an integer.
  127. *
  128. * If the character is an ASCII character, the integer representation of the character will be
  129. * returned, and the number of characters read will be 1.
  130. *
  131. * This stock is made specifically for the purpose of handling strings with multi-byte
  132. * characters.
  133. */
  134. stock int GetMultiByteCharacterAt(const char[] text, int pos, int &read = 0) {
  135. int nCharBytes, charOutput;
  136. nCharBytes = GetCharBytes(text[pos]);
  137. for (int b = 0; b < nCharBytes; b++) {
  138. charOutput = charOutput << 8;
  139. // read in the current byte
  140. charOutput |= text[pos + b] & 0xFF;
  141. read++;
  142. }
  143. return charOutput;
  144. }
  145. /**
  146. * Appends a multi-byte character to a string.
  147. */
  148. stock void StrCatMultiByteChar(char[] buffer, int maxlen, int character, int size) {
  149. char[] appended = new char[size];
  150. MultiByteCharacterToCharArray(appended, size, character);
  151. StrCat(buffer, maxlen, appended);
  152. }
  153. /**
  154. * Converts a multi-byte character to a char array.
  155. */
  156. stock void MultiByteCharacterToCharArray(char[] buffer, int charsize, int character) {
  157. for (int i = 0; i < charsize; i++) {
  158. buffer[i] = (character >> ((charsize - i - 1) * 8 )) & 0xFF;
  159. }
  160. }