diff options
| -rw-r--r-- | nameblocker.sp | 165 | ||||
| -rw-r--r-- | orderchecker.sp | 29 |
2 files changed, 144 insertions, 50 deletions
diff --git a/nameblocker.sp b/nameblocker.sp index 554b086..3d5498c 100644 --- a/nameblocker.sp +++ b/nameblocker.sp | |||
| @@ -1,19 +1,27 @@ | |||
| 1 | // Notes: | 1 | // Notes: |
| 2 | // Memory allocated via the "new" keyword IS garbage collected. Handles are not. Close handles when you're done with them | 2 | // Memory allocated via the "new" keyword IS garbage collected. Handles are not. Close handles when you're done with them |
| 3 | // Handles are closed when the plugin is unloaded, so if the lifetime of a handle is the plugin's lifetime, don't worry about closing it | 3 | // Handles are closed when the plugin is unloaded, so if the lifetime of a handle is the plugin's lifetime, don't worry about closing it |
| 4 | // The contents of ArrayList objects are lost on map transition / server restart. Any persistent data should be stored in a | 4 | // The contents of ArrayList objects are lost on map transition / server restart. Any persistent data should be stored in a |
| 5 | // key-value file OR in a database | 5 | // key-value file OR in a database |
| 6 | 6 | ||
| 7 | // Basically every string function will add a null terminator, so add `+ 1` to the end of any defined size string definition | 7 | // Basically every string function will add a null terminator, so add `+ 1` to the end of any defined size string definition |
| 8 | // It is possible to throw an error with the ThrowError function, but anything that would bother should probably disable the | 8 | // It is possible to throw an error with the ThrowError function, but anything that would bother should probably disable the |
| 9 | // plugin entirely, so SetFailState is better | 9 | // plugin entirely, so SetFailState is better |
| 10 | 10 | ||
| 11 | // Large local var allocations if done commonly should be made static. Increases baseline memory usage, but lowers time spent | 11 | // Large local var allocations if done commonly should be made static. Increases baseline memory usage, but lowers time spent |
| 12 | // allocating memory. That being said, don't bother unless it's actually a problem. Name checks should be relatively | 12 | // allocating memory. That being said, don't bother unless it's actually a problem. Name checks should be relatively |
| 13 | // infrequent, and everything else exponentially less frequent. Dropping a frame or 2 to allocate 2048 bytes of memory | 13 | // infrequent, and everything else exponentially less frequent. Dropping a frame or 2 to allocate 2048 bytes of memory |
| 14 | // every century shouldn't be the end of the world | 14 | // every century shouldn't be the end of the world |
| 15 | 15 | ||
| 16 | // | 16 | // The database should contain: |
| 17 | // The string used to compile a regex pattern, sanitized to avoid sql injection attacks | ||
| 18 | // The steamid of the admin who added a regex pattern | ||
| 19 | // The time a regex pattern was added | ||
| 20 | |||
| 21 | // In the future, this may change to include regex compilation flags, kick/ban modes, etc. . Depends on how many features | ||
| 22 | // I want to cram into this given how fundamentally shitty SourcePawn as a language is. I'm not trying to keep track of 17 | ||
| 23 | // different arrays and make sure that the indicies between them never get out of date. Maybe enum structs will solve my | ||
| 24 | // concerns, maybe not | ||
| 17 | 25 | ||
| 18 | #pragma newdecls required | 26 | #pragma newdecls required |
| 19 | #pragma semicolon 1 | 27 | #pragma semicolon 1 |
| @@ -35,6 +43,8 @@ public Plugin myinfo = { | |||
| 35 | 43 | ||
| 36 | #define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */ | 44 | #define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */ |
| 37 | 45 | ||
| 46 | #define DATABASE_FAIL_MSG "Could not populate regex & pattern lists from database" | ||
| 47 | |||
| 38 | enum OperatingMode { | 48 | enum OperatingMode { |
| 39 | OP_DISABLED, | 49 | OP_DISABLED, |
| 40 | OP_KICK, | 50 | OP_KICK, |
| @@ -57,7 +67,7 @@ ConVar gcvarAmdImmFlag; static const char ADMINIMMUNITYFLAGNAME[] = "nameb | |||
| 57 | void logAndFail(const char[] format, any ...) { | 67 | void logAndFail(const char[] format, any ...) { |
| 58 | char buf[2048 + 1]; | 68 | char buf[2048 + 1]; |
| 59 | VFormat(buf, sizeof(buf), format, 2); | 69 | VFormat(buf, sizeof(buf), format, 2); |
| 60 | 70 | ||
| 61 | LogError(buf); | 71 | LogError(buf); |
| 62 | SetFailState(buf); | 72 | SetFailState(buf); |
| 63 | } | 73 | } |
| @@ -68,7 +78,7 @@ int concatArgs(char[] buf, int maxbuflen, int maxarglen, int end, int start=1) { | |||
| 68 | 78 | ||
| 69 | char[] arg = new char[maxarglen]; | 79 | char[] arg = new char[maxarglen]; |
| 70 | char[] tmp = new char[maxbuflen]; | 80 | char[] tmp = new char[maxbuflen]; |
| 71 | 81 | ||
| 72 | GetCmdArg(start, tmp, maxarglen); // Priming the first argument to prevent weirdness | 82 | GetCmdArg(start, tmp, maxarglen); // Priming the first argument to prevent weirdness |
| 73 | for(int i = start + 1; i <= end; i++) { | 83 | for(int i = start + 1; i <= end; i++) { |
| 74 | GetCmdArg(i, arg, maxarglen); | 84 | GetCmdArg(i, arg, maxarglen); |
| @@ -82,7 +92,7 @@ int concatArgs(char[] buf, int maxbuflen, int maxarglen, int end, int start=1) { | |||
| 82 | int registerIntConVar(ConVar& cv, int defaultVal, const char[] name, const char[] desc) { | 92 | int registerIntConVar(ConVar& cv, int defaultVal, const char[] name, const char[] desc) { |
| 83 | char tmp[32]; // Consider this a byte array | 93 | char tmp[32]; // Consider this a byte array |
| 84 | Format(tmp, sizeof(tmp), "%d", defaultVal); | 94 | Format(tmp, sizeof(tmp), "%d", defaultVal); |
| 85 | 95 | ||
| 86 | cv = CreateConVar(name, tmp, desc); | 96 | cv = CreateConVar(name, tmp, desc); |
| 87 | if(cv == null) return -1; | 97 | if(cv == null) return -1; |
| 88 | cv.IntValue = defaultVal; | 98 | cv.IntValue = defaultVal; |
| @@ -98,16 +108,7 @@ void xRegisterIntConVar(ConVar& cv, int defaultVal, const char[] name, const cha | |||
| 98 | 108 | ||
| 99 | 109 | ||
| 100 | public void OnAllPluginsLoaded() { | 110 | public void OnAllPluginsLoaded() { |
| 101 | // Initialize and populate datatypes | 111 | if(loadFromDatabase()) logAndFail(DATABASE_FAIL_MSG); |
| 102 | regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); | ||
| 103 | if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); | ||
| 104 | |||
| 105 | patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); | ||
| 106 | if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); | ||
| 107 | |||
| 108 | char sqlerr[256 + 1]; | ||
| 109 | db = SQL_DefConnect(sqlerr, sizeof(sqlerr)); // Default connect until I get a little more acquainted with sm's sql api | ||
| 110 | if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); | ||
| 111 | 112 | ||
| 112 | // Register convars | 113 | // Register convars |
| 113 | xRegisterIntConVar(gcvarOperMode, view_as<int>(DEFAULTOPERMODE), OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); | 114 | xRegisterIntConVar(gcvarOperMode, view_as<int>(DEFAULTOPERMODE), OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); |
| @@ -118,23 +119,53 @@ public void OnAllPluginsLoaded() { | |||
| 118 | AutoExecConfig(true, "nameblocker_cvars"); | 119 | AutoExecConfig(true, "nameblocker_cvars"); |
| 119 | 120 | ||
| 120 | // Register commands | 121 | // Register commands |
| 121 | RegAdminCmd("nb_addpattern", registerPattern, gcvarAdmCmdFlag.IntValue, "Add a regex pattern to the filter list"); | 122 | RegAdminCmd("nb_addpattern", cmdRegisterPattern, gcvarAdmCmdFlag.IntValue, "Add a regex pattern to the filter list"); |
| 122 | RegAdminCmd("nb_removepattern", deletePattern, gcvarAdmCmdFlag.IntValue, "Remove a regex pattern from the filter list"); | 123 | RegAdminCmd("nb_removepattern", cmdDeletePattern, gcvarAdmCmdFlag.IntValue, "Remove a regex pattern from the filter list"); |
| 123 | RegAdminCmd("nb_modifypattern", replacePattern, gcvarAdmCmdFlag.IntValue, "Replace an existing regex pattern with a new pattern"); | 124 | RegAdminCmd("nb_modifypattern", cmdReplacePattern, gcvarAdmCmdFlag.IntValue, "Replace an existing regex pattern with a new pattern"); |
| 124 | RegAdminCmd("nb_listpatterns", listPatterns, gcvarAdmCmdFlag.IntValue, "List current regex patterns and their indicies"); | 125 | RegAdminCmd("nb_listpatterns", cmdListPatterns, gcvarAdmCmdFlag.IntValue, "List current regex patterns and their indicies"); |
| 126 | } | ||
| 127 | |||
| 128 | public void OnConfigsExecuted() { | ||
| 129 | // I'm not sure when this is executed in relation to when OnClientPostAdminCheck is. Sourcemod's API reference says it's ran | ||
| 130 | // once after OnMapStart, but idk if "Map Start" is a sufficient enough game state for players to join | ||
| 131 | |||
| 132 | // Let me illustrate my concern: | ||
| 133 | // Server starts normally, plugin is working | ||
| 134 | // Players join. Nothing weird happens because the plugin loaded everything before a player could have joined | ||
| 135 | // Players/Server initiates a map change WHILE players are still connected. If OnMapStart is when players can start | ||
| 136 | // connecting, and is before OnConfigsExecuted, the following scenarios could happen: | ||
| 137 | |||
| 138 | // 1: OnMapStart fires, players may join | ||
| 139 | // 2: Player joins. This fires OnClientPostAdminCheck | ||
| 140 | // 3: OnClientPostAdminCheck fires checkName, which tries to querry the array lists | ||
| 141 | // 4: ArrayLists are empty or null, causing unexpected behavior | ||
| 142 | // 4.1: ArrayLists are empty, nothing major happens, but a player with an invalid name gets through. Not ideal, but | ||
| 143 | // also not the end of the world | ||
| 144 | // 4.2: ArrayLists are null, trying to use them causes a null pointer dereference, either crashing the plugin &/or | ||
| 145 | // server, or resulting in some other undefined behavior for sourcemod to deal with | ||
| 146 | |||
| 147 | // If OnMapStart doesn't let players join, or rather OnConfigsExecuted fires before players can join, then there's no | ||
| 148 | // problem. Alternatively, I can write something to kick/retry players until OnConfigsExecuted fires | ||
| 149 | |||
| 150 | // As of now, I will simply leave this as a note. No need to go making weird systems if they may not be necessary | ||
| 151 | |||
| 152 | if(loadFromDatabase()) logAndFail(DATABASE_FAIL_MSG); | ||
| 153 | |||
| 125 | } | 154 | } |
| 126 | 155 | ||
| 127 | public void OnClientPostAdminCheck(int client) { | 156 | public void OnClientPostAdminCheck(int client) { |
| 128 | checkName(client); | 157 | checkName(client); |
| 158 | |||
| 129 | } | 159 | } |
| 130 | 160 | ||
| 131 | public void OnClientSettingsChanged(int client) { | 161 | public void OnClientSettingsChanged(int client) { |
| 132 | checkName(client); | 162 | checkName(client); |
| 163 | |||
| 133 | } | 164 | } |
| 134 | 165 | ||
| 135 | 166 | ||
| 136 | 167 | ||
| 137 | public Action registerPattern(int client, int args) { | 168 | public Action cmdRegisterPattern(int client, int args) { |
| 138 | if(args < 2) { | 169 | if(args < 2) { |
| 139 | ReplyToCommand(client, "Error: missing regex pattern"); | 170 | ReplyToCommand(client, "Error: missing regex pattern"); |
| 140 | return Plugin_Handled; | 171 | return Plugin_Handled; |
| @@ -146,7 +177,7 @@ public Action registerPattern(int client, int args) { | |||
| 146 | return Plugin_Handled; | 177 | return Plugin_Handled; |
| 147 | } | 178 | } |
| 148 | 179 | ||
| 149 | if(insertPattern(pattern, regexlist.Length)) { | 180 | if(modPattern(DBM_INSERT, regexlist.Length, pattern)) { |
| 150 | ReplyToCommand(client, "Error: could not register pattern"); | 181 | ReplyToCommand(client, "Error: could not register pattern"); |
| 151 | return Plugin_Handled; | 182 | return Plugin_Handled; |
| 152 | } | 183 | } |
| @@ -154,7 +185,7 @@ public Action registerPattern(int client, int args) { | |||
| 154 | return Plugin_Handled; | 185 | return Plugin_Handled; |
| 155 | } | 186 | } |
| 156 | 187 | ||
| 157 | public Action deletePattern(int client, int args) { | 188 | public Action cmdDeletePattern(int client, int args) { |
| 158 | if(args < 2) { | 189 | if(args < 2) { |
| 159 | ReplyToCommand(client, "Error: no pattern index given"); | 190 | ReplyToCommand(client, "Error: no pattern index given"); |
| 160 | return Plugin_Handled; | 191 | return Plugin_Handled; |
| @@ -166,7 +197,7 @@ public Action deletePattern(int client, int args) { | |||
| 166 | return Plugin_Handled; | 197 | return Plugin_Handled; |
| 167 | } | 198 | } |
| 168 | 199 | ||
| 169 | if(removePattern(index)) { | 200 | if(modPattern(DBM_DELETE, index)) { |
| 170 | ReplyToCommand(client, "Error: could not remove pattern"); | 201 | ReplyToCommand(client, "Error: could not remove pattern"); |
| 171 | return Plugin_Handled; | 202 | return Plugin_Handled; |
| 172 | } | 203 | } |
| @@ -174,12 +205,12 @@ public Action deletePattern(int client, int args) { | |||
| 174 | return Plugin_Handled; | 205 | return Plugin_Handled; |
| 175 | } | 206 | } |
| 176 | 207 | ||
| 177 | public Action replacePattern(int client, int args) { | 208 | public Action cmdReplacePattern(int client, int args) { |
| 178 | if(args < 3) { | 209 | if(args < 3) { |
| 179 | ReplyToCommand(client, "Error: missing index, replacement pattern, or both"); | 210 | ReplyToCommand(client, "Error: missing index, replacement pattern, or both"); |
| 180 | return Plugin_Handled; | 211 | return Plugin_Handled; |
| 181 | } | 212 | } |
| 182 | 213 | ||
| 183 | int index; | 214 | int index; |
| 184 | if(!GetCmdArgIntEx(1, index)) { | 215 | if(!GetCmdArgIntEx(1, index)) { |
| 185 | ReplyToCommand(client, "Error: index argument not numerical"); | 216 | ReplyToCommand(client, "Error: index argument not numerical"); |
| @@ -192,23 +223,15 @@ public Action replacePattern(int client, int args) { | |||
| 192 | return Plugin_Handled; | 223 | return Plugin_Handled; |
| 193 | } | 224 | } |
| 194 | 225 | ||
| 195 | if(removePattern(index)) { | 226 | if(modPattern(DBM_INSERT | DBM_DELETE, index, pattern)) { |
| 196 | ReplyToCommand(client, "Error: could not remove pattern"); | 227 | ReplyToCommand(client, "Error: could not remove pattern from list"); |
| 197 | return Plugin_Handled; | ||
| 198 | } | ||
| 199 | if(insertPattern(pattern, index)) { | ||
| 200 | ReplyToCommand(client, "Error: could not register pattern"); | ||
| 201 | return Plugin_Handled; | 228 | return Plugin_Handled; |
| 202 | } | 229 | } |
| 203 | // Preferably this would be atomic as to not lose a pattern, but that's something I can do later | ||
| 204 | |||
| 205 | // I know I can make the database change "atomic" via the use of a transaction, but handling that would necessitate a different | ||
| 206 | // function | ||
| 207 | 230 | ||
| 208 | return Plugin_Handled; | 231 | return Plugin_Handled; |
| 209 | } | 232 | } |
| 210 | 233 | ||
| 211 | public Action listPatterns(int client, int args) { | 234 | public Action cmdListPatterns(int client, int args) { |
| 212 | for(int i = 0; i < patternlist.Length; i++) { | 235 | for(int i = 0; i < patternlist.Length; i++) { |
| 213 | ReplyToCommand(client, "[%d] %s", i, patternlist.Get(i)); | 236 | ReplyToCommand(client, "[%d] %s", i, patternlist.Get(i)); |
| 214 | } | 237 | } |
| @@ -218,16 +241,9 @@ public Action listPatterns(int client, int args) { | |||
| 218 | 241 | ||
| 219 | 242 | ||
| 220 | 243 | ||
| 221 | int insertPattern(char pattern[PATTERN_MAX_LEN], int index) { | 244 | int aInsertPattern(char pattern[PATTERN_MAX_LEN], Regex res, int index) { |
| 222 | if(IsNullString(pattern) || index < 0 || index > regexlist.Length) return -1; | 245 | if(IsNullString(pattern) || index < 0 || index > regexlist.Length) return -1; |
| 223 | 246 | ||
| 224 | char errstr[512]; RegexError reerr; | ||
| 225 | Regex res = CompileRegex(pattern, gcvarRegexCompFlags.IntValue, errstr, sizeof(errstr), reerr); | ||
| 226 | if(res == null) { | ||
| 227 | LogError("Error: Could not compile regex pattern \"%s\": %s (%d)", pattern, errstr, reerr); | ||
| 228 | return -1; | ||
| 229 | } | ||
| 230 | |||
| 231 | if(index == regexlist.Length) { | 247 | if(index == regexlist.Length) { |
| 232 | regexlist.Push(res); | 248 | regexlist.Push(res); |
| 233 | patternlist.Push(pattern); | 249 | patternlist.Push(pattern); |
| @@ -240,18 +256,67 @@ int insertPattern(char pattern[PATTERN_MAX_LEN], int index) { | |||
| 240 | 256 | ||
| 241 | } | 257 | } |
| 242 | 258 | ||
| 243 | // TODO: This should also insert the pattern into the database | ||
| 244 | |||
| 245 | return 0; | 259 | return 0; |
| 246 | } | 260 | } |
| 247 | 261 | ||
| 248 | int removePattern(int index) { | 262 | int aRemovePattern(int index) { |
| 249 | if(index < 0 || index >= regexlist.Length) return -1; | 263 | if(index < 0 || index >= regexlist.Length) return -1; |
| 250 | 264 | ||
| 251 | regexlist.Erase(index); | 265 | regexlist.Erase(index); |
| 252 | patternlist.Erase(index); | 266 | patternlist.Erase(index); |
| 253 | 267 | ||
| 254 | // TODO: This should also remove the pattern from the database | 268 | return 0; |
| 269 | } | ||
| 270 | |||
| 271 | enum DBMOD_MODE { | ||
| 272 | DBM_UNDEF, | ||
| 273 | DBM_INSERT = (1<<0), | ||
| 274 | DBM_DELETE = (1<<1), | ||
| 275 | DBM_TOOBIG | ||
| 276 | }; | ||
| 277 | |||
| 278 | int modPattern(DBMOD_MODE mode, int index, char[] pattern="") { | ||
| 279 | if(index < 0 || index > regexlist.Length) return -1; | ||
| 280 | if(mode <= DBM_UNDEF || mode >= DBM_TOOBIG) return -1; | ||
| 281 | |||
| 282 | Transaction modification = SQL_CreateTransaction(); | ||
| 283 | if(modification == null) return -1; | ||
| 284 | |||
| 285 | // char errstr[512]; RegexError reerr; | ||
| 286 | // Regex res = CompileRegex(pattern, gcvarRegexCompFlags.IntValue, errstr, sizeof(errstr), reerr); | ||
| 287 | // if(res == null) { | ||
| 288 | // LogError("Error: Could not compile regex pattern \"%s\": %s (%d)", pattern, errstr, reerr); | ||
| 289 | // return -1; | ||
| 290 | // } | ||
| 291 | |||
| 292 | // Update transaction | ||
| 293 | // Update lists | ||
| 294 | // Try to delete before inserting | ||
| 295 | |||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | int loadFromDatabase() { | ||
| 300 | logAndFail("function \"loadFromDatabase\" not implemented"); | ||
| 301 | |||
| 302 | // Get database handle | ||
| 303 | char sqlerr[256 + 1]; | ||
| 304 | db = SQLite_UseDatabase("sourcemod-local", sqlerr, sizeof(sqlerr)); | ||
| 305 | if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); | ||
| 306 | |||
| 307 | // Initialize table if it doesn't exist | ||
| 308 | |||
| 309 | |||
| 310 | // Initialize and populate datatypes | ||
| 311 | regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); | ||
| 312 | if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); | ||
| 313 | |||
| 314 | patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); | ||
| 315 | if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); | ||
| 316 | |||
| 317 | // select patterns from nameblock table/database | ||
| 318 | // compile each pattern | ||
| 319 | // insert pattern & regex into respective lists | ||
| 255 | 320 | ||
| 256 | return 0; | 321 | return 0; |
| 257 | } | 322 | } |
diff --git a/orderchecker.sp b/orderchecker.sp new file mode 100644 index 0000000..a80ac4d --- /dev/null +++ b/orderchecker.sp | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #pragma newdecls required | ||
| 2 | #pragma semicolon 1 | ||
| 3 | |||
| 4 | #include <sourcemod> | ||
| 5 | |||
| 6 | public Plugin myinfo = { | ||
| 7 | name = "orderchecker", | ||
| 8 | description = "logs a message in chat when a callback is fired", | ||
| 9 | author = "NW/RL", | ||
| 10 | version = "alpha-0.1", | ||
| 11 | url = "" | ||
| 12 | }; | ||
| 13 | |||
| 14 | public void OnAllPluginsLoaded() { | ||
| 15 | PrintToChatAll("OnAllPluginsLoaded"); | ||
| 16 | } | ||
| 17 | |||
| 18 | public void OnConfigsExecuted() { | ||
| 19 | PrintToChatAll("OnConfigsExecuted"); | ||
| 20 | } | ||
| 21 | |||
| 22 | public void OnClientPostAdminCheck(int client) { | ||
| 23 | PrintToChatAll("OnClientPostAdminCheck on %N", client); | ||
| 24 | } | ||
| 25 | |||
| 26 | public void OnClientSettingsChanged(int client) { | ||
| 27 | PrintToChatAll("OnClientSettingsChanged on %N", client); | ||
| 28 | } | ||
| 29 | |||
