csrd_name_filter.sp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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.0"
  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. bool g_bNameLockSupported;
  26. int g_FilteredWhitespace[] = {
  27. 0xe2a080, // braille pattern blank U+2800
  28. 0x0a, // linefeed
  29. 0x0d, // carriage return
  30. 0
  31. };
  32. public void OnPluginStart() {
  33. for (int i = 1; i <= MaxClients; i++) {
  34. if (IsClientConnected(i)) {
  35. OnNameUpdated(i);
  36. }
  37. }
  38. g_bNameLockSupported = CommandExists("namelockid");
  39. }
  40. public void OnClientConnected(int client) {
  41. OnNameUpdated(client);
  42. }
  43. public void OnClientSettingsChanged(int client) {
  44. OnNameUpdated(client);
  45. }
  46. void OnNameUpdated(int client) {
  47. char currentName[MAX_NAME_LENGTH], newNameBuffer[MAX_NAME_LENGTH];
  48. GetClientName(client, currentName, sizeof(currentName));
  49. bool bNameChanged;
  50. /**
  51. * Copies valid characters into another buffer (while also dealing with Unicode).
  52. * The buffer is used if any characters are filtered.
  53. */
  54. for (int i = 0; i < strlen(currentName); /* ... */) {
  55. int fullChar, nCharBytes;
  56. fullChar = GetMultiByteCharacterAt(currentName, i, nCharBytes);
  57. bool bCharFiltered;
  58. // Filter out whitespace
  59. int c;
  60. do {
  61. bCharFiltered |= (fullChar == g_FilteredWhitespace[c]);
  62. } while (g_FilteredWhitespace[++c] != 0 && !bCharFiltered);
  63. if (!bCharFiltered) {
  64. // it's a character that we don't really care about
  65. StrCatMultiByteChar(newNameBuffer, sizeof(newNameBuffer), fullChar, nCharBytes);
  66. } else {
  67. StrCat(newNameBuffer, sizeof(newNameBuffer), " ");
  68. }
  69. bNameChanged |= bCharFiltered;
  70. i += nCharBytes;
  71. }
  72. if (bNameChanged) {
  73. // Remove surrounding whitespace
  74. TrimString(newNameBuffer);
  75. // If there's no printable characters in the buffer, the player is a total dongbus.
  76. if (strlen(newNameBuffer) == 0) {
  77. Format(newNameBuffer, sizeof(newNameBuffer), UNPRINTABLE_NAME_USER_FORMAT,
  78. GetClientUserId(client));
  79. SetClientNameLock(client);
  80. }
  81. SetClientName(client, newNameBuffer);
  82. }
  83. // TODO else not name changed, but check if we can do other non-filter stuff? (trim)
  84. }
  85. /**
  86. * Attempts to lock the specified client's name.
  87. */
  88. stock void SetClientNameLock(int client, bool locked = true) {
  89. if (g_bNameLockSupported) {
  90. ServerCommand("namelockid %d %d", GetClientUserId(client), locked ? 0 : 1);
  91. }
  92. }
  93. /**
  94. * Gets a possibly multi-byte character at the specified position in a string as an integer.
  95. *
  96. * If the character is an ASCII character, the integer representation of the character will be
  97. * returned, and the number of characters read will be 1.
  98. *
  99. * This stock is made specifically for the purpose of handling strings with multi-byte
  100. * characters.
  101. */
  102. stock int GetMultiByteCharacterAt(const char[] text, int pos, int &read = 0) {
  103. int nCharBytes, charOutput;
  104. nCharBytes = GetCharBytes(text[pos]);
  105. for (int b = 0; b < nCharBytes; b++) {
  106. charOutput = charOutput << 8;
  107. // read in the current byte
  108. charOutput |= text[pos + b] & 0xFF;
  109. read++;
  110. }
  111. return charOutput;
  112. }
  113. /**
  114. * Appends a multi-byte character to a string.
  115. */
  116. stock void StrCatMultiByteChar(char[] buffer, int maxlen, int character, int size) {
  117. char[] appended = new char[size];
  118. MultiByteCharacterToCharArray(appended, size, character);
  119. StrCat(buffer, maxlen, appended);
  120. }
  121. /**
  122. * Converts a multi-byte character to a char array.
  123. */
  124. stock void MultiByteCharacterToCharArray(char[] buffer, int charsize, int character) {
  125. for (int i = 0; i < charsize; i++) {
  126. buffer[i] = (character >> ((charsize - i - 1) * 8 )) & 0xFF;
  127. }
  128. }