From b7851f3414e2cd52149c8f84329b3d788a7df08b Mon Sep 17 00:00:00 2001 From: nwrl Date: Wed, 6 Aug 2025 16:17:26 -0500 Subject: Move some things around --- nameblocker.sp | 165 +++++++++++++++++++++++++++++++++++++++----------------- orderchecker.sp | 29 ++++++++++ 2 files changed, 144 insertions(+), 50 deletions(-) create mode 100644 orderchecker.sp diff --git a/nameblocker.sp b/nameblocker.sp index 554b086..3d5498c 100644 --- a/nameblocker.sp +++ b/nameblocker.sp @@ -1,19 +1,27 @@ // Notes: // Memory allocated via the "new" keyword IS garbage collected. Handles are not. Close handles when you're done with them // 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 - // The contents of ArrayList objects are lost on map transition / server restart. Any persistent data should be stored in a + // The contents of ArrayList objects are lost on map transition / server restart. Any persistent data should be stored in a // key-value file OR in a database - + // Basically every string function will add a null terminator, so add `+ 1` to the end of any defined size string definition // It is possible to throw an error with the ThrowError function, but anything that would bother should probably disable the // plugin entirely, so SetFailState is better - + // Large local var allocations if done commonly should be made static. Increases baseline memory usage, but lowers time spent // allocating memory. That being said, don't bother unless it's actually a problem. Name checks should be relatively // infrequent, and everything else exponentially less frequent. Dropping a frame or 2 to allocate 2048 bytes of memory // every century shouldn't be the end of the world - // + // The database should contain: + // The string used to compile a regex pattern, sanitized to avoid sql injection attacks + // The steamid of the admin who added a regex pattern + // The time a regex pattern was added + + // In the future, this may change to include regex compilation flags, kick/ban modes, etc. . Depends on how many features + // I want to cram into this given how fundamentally shitty SourcePawn as a language is. I'm not trying to keep track of 17 + // different arrays and make sure that the indicies between them never get out of date. Maybe enum structs will solve my + // concerns, maybe not #pragma newdecls required #pragma semicolon 1 @@ -35,6 +43,8 @@ public Plugin myinfo = { #define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */ +#define DATABASE_FAIL_MSG "Could not populate regex & pattern lists from database" + enum OperatingMode { OP_DISABLED, OP_KICK, @@ -57,7 +67,7 @@ ConVar gcvarAmdImmFlag; static const char ADMINIMMUNITYFLAGNAME[] = "nameb void logAndFail(const char[] format, any ...) { char buf[2048 + 1]; VFormat(buf, sizeof(buf), format, 2); - + LogError(buf); SetFailState(buf); } @@ -68,7 +78,7 @@ int concatArgs(char[] buf, int maxbuflen, int maxarglen, int end, int start=1) { char[] arg = new char[maxarglen]; char[] tmp = new char[maxbuflen]; - + GetCmdArg(start, tmp, maxarglen); // Priming the first argument to prevent weirdness for(int i = start + 1; i <= end; i++) { GetCmdArg(i, arg, maxarglen); @@ -82,7 +92,7 @@ int concatArgs(char[] buf, int maxbuflen, int maxarglen, int end, int start=1) { int registerIntConVar(ConVar& cv, int defaultVal, const char[] name, const char[] desc) { char tmp[32]; // Consider this a byte array Format(tmp, sizeof(tmp), "%d", defaultVal); - + cv = CreateConVar(name, tmp, desc); if(cv == null) return -1; cv.IntValue = defaultVal; @@ -98,16 +108,7 @@ void xRegisterIntConVar(ConVar& cv, int defaultVal, const char[] name, const cha public void OnAllPluginsLoaded() { - // Initialize and populate datatypes - regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); - if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); - - patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); - if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); - - char sqlerr[256 + 1]; - db = SQL_DefConnect(sqlerr, sizeof(sqlerr)); // Default connect until I get a little more acquainted with sm's sql api - if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); + if(loadFromDatabase()) logAndFail(DATABASE_FAIL_MSG); // Register convars xRegisterIntConVar(gcvarOperMode, view_as(DEFAULTOPERMODE), OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); @@ -118,23 +119,53 @@ public void OnAllPluginsLoaded() { AutoExecConfig(true, "nameblocker_cvars"); // Register commands - RegAdminCmd("nb_addpattern", registerPattern, gcvarAdmCmdFlag.IntValue, "Add a regex pattern to the filter list"); - RegAdminCmd("nb_removepattern", deletePattern, gcvarAdmCmdFlag.IntValue, "Remove a regex pattern from the filter list"); - RegAdminCmd("nb_modifypattern", replacePattern, gcvarAdmCmdFlag.IntValue, "Replace an existing regex pattern with a new pattern"); - RegAdminCmd("nb_listpatterns", listPatterns, gcvarAdmCmdFlag.IntValue, "List current regex patterns and their indicies"); + RegAdminCmd("nb_addpattern", cmdRegisterPattern, gcvarAdmCmdFlag.IntValue, "Add a regex pattern to the filter list"); + RegAdminCmd("nb_removepattern", cmdDeletePattern, gcvarAdmCmdFlag.IntValue, "Remove a regex pattern from the filter list"); + RegAdminCmd("nb_modifypattern", cmdReplacePattern, gcvarAdmCmdFlag.IntValue, "Replace an existing regex pattern with a new pattern"); + RegAdminCmd("nb_listpatterns", cmdListPatterns, gcvarAdmCmdFlag.IntValue, "List current regex patterns and their indicies"); +} + +public void OnConfigsExecuted() { + // I'm not sure when this is executed in relation to when OnClientPostAdminCheck is. Sourcemod's API reference says it's ran + // once after OnMapStart, but idk if "Map Start" is a sufficient enough game state for players to join + + // Let me illustrate my concern: + // Server starts normally, plugin is working + // Players join. Nothing weird happens because the plugin loaded everything before a player could have joined + // Players/Server initiates a map change WHILE players are still connected. If OnMapStart is when players can start + // connecting, and is before OnConfigsExecuted, the following scenarios could happen: + + // 1: OnMapStart fires, players may join + // 2: Player joins. This fires OnClientPostAdminCheck + // 3: OnClientPostAdminCheck fires checkName, which tries to querry the array lists + // 4: ArrayLists are empty or null, causing unexpected behavior + // 4.1: ArrayLists are empty, nothing major happens, but a player with an invalid name gets through. Not ideal, but + // also not the end of the world + // 4.2: ArrayLists are null, trying to use them causes a null pointer dereference, either crashing the plugin &/or + // server, or resulting in some other undefined behavior for sourcemod to deal with + + // If OnMapStart doesn't let players join, or rather OnConfigsExecuted fires before players can join, then there's no + // problem. Alternatively, I can write something to kick/retry players until OnConfigsExecuted fires + + // As of now, I will simply leave this as a note. No need to go making weird systems if they may not be necessary + + if(loadFromDatabase()) logAndFail(DATABASE_FAIL_MSG); + } public void OnClientPostAdminCheck(int client) { checkName(client); + } public void OnClientSettingsChanged(int client) { checkName(client); + } -public Action registerPattern(int client, int args) { +public Action cmdRegisterPattern(int client, int args) { if(args < 2) { ReplyToCommand(client, "Error: missing regex pattern"); return Plugin_Handled; @@ -146,7 +177,7 @@ public Action registerPattern(int client, int args) { return Plugin_Handled; } - if(insertPattern(pattern, regexlist.Length)) { + if(modPattern(DBM_INSERT, regexlist.Length, pattern)) { ReplyToCommand(client, "Error: could not register pattern"); return Plugin_Handled; } @@ -154,7 +185,7 @@ public Action registerPattern(int client, int args) { return Plugin_Handled; } -public Action deletePattern(int client, int args) { +public Action cmdDeletePattern(int client, int args) { if(args < 2) { ReplyToCommand(client, "Error: no pattern index given"); return Plugin_Handled; @@ -166,7 +197,7 @@ public Action deletePattern(int client, int args) { return Plugin_Handled; } - if(removePattern(index)) { + if(modPattern(DBM_DELETE, index)) { ReplyToCommand(client, "Error: could not remove pattern"); return Plugin_Handled; } @@ -174,12 +205,12 @@ public Action deletePattern(int client, int args) { return Plugin_Handled; } -public Action replacePattern(int client, int args) { +public Action cmdReplacePattern(int client, int args) { if(args < 3) { ReplyToCommand(client, "Error: missing index, replacement pattern, or both"); return Plugin_Handled; } - + int index; if(!GetCmdArgIntEx(1, index)) { ReplyToCommand(client, "Error: index argument not numerical"); @@ -192,23 +223,15 @@ public Action replacePattern(int client, int args) { return Plugin_Handled; } - if(removePattern(index)) { - ReplyToCommand(client, "Error: could not remove pattern"); - return Plugin_Handled; - } - if(insertPattern(pattern, index)) { - ReplyToCommand(client, "Error: could not register pattern"); + if(modPattern(DBM_INSERT | DBM_DELETE, index, pattern)) { + ReplyToCommand(client, "Error: could not remove pattern from list"); return Plugin_Handled; } - // Preferably this would be atomic as to not lose a pattern, but that's something I can do later - - // I know I can make the database change "atomic" via the use of a transaction, but handling that would necessitate a different - // function return Plugin_Handled; } -public Action listPatterns(int client, int args) { +public Action cmdListPatterns(int client, int args) { for(int i = 0; i < patternlist.Length; i++) { ReplyToCommand(client, "[%d] %s", i, patternlist.Get(i)); } @@ -218,16 +241,9 @@ public Action listPatterns(int client, int args) { -int insertPattern(char pattern[PATTERN_MAX_LEN], int index) { +int aInsertPattern(char pattern[PATTERN_MAX_LEN], Regex res, int index) { if(IsNullString(pattern) || index < 0 || index > regexlist.Length) return -1; - char errstr[512]; RegexError reerr; - Regex res = CompileRegex(pattern, gcvarRegexCompFlags.IntValue, errstr, sizeof(errstr), reerr); - if(res == null) { - LogError("Error: Could not compile regex pattern \"%s\": %s (%d)", pattern, errstr, reerr); - return -1; - } - if(index == regexlist.Length) { regexlist.Push(res); patternlist.Push(pattern); @@ -240,18 +256,67 @@ int insertPattern(char pattern[PATTERN_MAX_LEN], int index) { } - // TODO: This should also insert the pattern into the database - return 0; } -int removePattern(int index) { +int aRemovePattern(int index) { if(index < 0 || index >= regexlist.Length) return -1; regexlist.Erase(index); patternlist.Erase(index); - // TODO: This should also remove the pattern from the database + return 0; +} + +enum DBMOD_MODE { + DBM_UNDEF, + DBM_INSERT = (1<<0), + DBM_DELETE = (1<<1), + DBM_TOOBIG +}; + +int modPattern(DBMOD_MODE mode, int index, char[] pattern="") { + if(index < 0 || index > regexlist.Length) return -1; + if(mode <= DBM_UNDEF || mode >= DBM_TOOBIG) return -1; + + Transaction modification = SQL_CreateTransaction(); + if(modification == null) return -1; + + // char errstr[512]; RegexError reerr; + // Regex res = CompileRegex(pattern, gcvarRegexCompFlags.IntValue, errstr, sizeof(errstr), reerr); + // if(res == null) { + // LogError("Error: Could not compile regex pattern \"%s\": %s (%d)", pattern, errstr, reerr); + // return -1; + // } + + // Update transaction + // Update lists + // Try to delete before inserting + + return 0; +} + +int loadFromDatabase() { + logAndFail("function \"loadFromDatabase\" not implemented"); + + // Get database handle + char sqlerr[256 + 1]; + db = SQLite_UseDatabase("sourcemod-local", sqlerr, sizeof(sqlerr)); + if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); + + // Initialize table if it doesn't exist + + + // Initialize and populate datatypes + regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); + if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); + + patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); + if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); + + // select patterns from nameblock table/database + // compile each pattern + // insert pattern & regex into respective lists return 0; } diff --git a/orderchecker.sp b/orderchecker.sp new file mode 100644 index 0000000..a80ac4d --- /dev/null +++ b/orderchecker.sp @@ -0,0 +1,29 @@ +#pragma newdecls required +#pragma semicolon 1 + +#include + +public Plugin myinfo = { + name = "orderchecker", + description = "logs a message in chat when a callback is fired", + author = "NW/RL", + version = "alpha-0.1", + url = "" +}; + +public void OnAllPluginsLoaded() { + PrintToChatAll("OnAllPluginsLoaded"); +} + +public void OnConfigsExecuted() { + PrintToChatAll("OnConfigsExecuted"); +} + +public void OnClientPostAdminCheck(int client) { + PrintToChatAll("OnClientPostAdminCheck on %N", client); +} + +public void OnClientSettingsChanged(int client) { + PrintToChatAll("OnClientSettingsChanged on %N", client); +} + -- cgit v1.2.3