Repository navigation
fix(sql): userid in query callbacks, map-proof DB retries, null guards - #67
Conversation
- Pass the userid instead of the raw client index to the ban / spray hash / NSFW lookups, resolved with GetClientOfUserId in the callbacks. - Reconnect and table-creation retry timers no longer die on map change. - Guard DB_UpdateSprayNSFWStatus / DB_DeleteSprayNSFWStatus against a null handle. - Retry the NSFW lookup when the DB isn't ready, like the other two lookups. - Set the charset with SetCharset once per connection, SET NAMES only as fallback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WareZ57
left a comment
There was a problem hiding this comment.
Went through this against current master (it sits on top of #66, merges cleanly, CI green) and compiled it with spcomp 1.12.0.7259 + MultiColors/FixSprayExploit: no errors, no warnings.
What I checked:
- The three lookups now pass
GetClientUserId(client)and resolve it withGetClientOfUserIdbefore touching per-client arrays (SprayManager.sp:1054,:1071,:1088, callbacks at:1106,:1140,:1159). The retry paths in the callbacks still use serials, so nothing gets applied to a reused slot anymore. - Dropping
TIMER_FLAG_NO_MAPCHANGEfromReconnectSQL/RetryTableCreation(:629,:664) is right. Each chain only has one timer at a time (a failed connect arms one reconnect, a failed table transaction arms one retry) so nothing stacks, and the plugin's timers still die on unload. SetCharsetruns on the new handle before it's published tog_hDatabaseand before the table transaction is queued (:644-649), so nothing else is using the connection yet.SET NAMESstays as the fallback only when it returns false. SQLite is skipped.- The null guards in
DB_UpdateSprayNSFWStatus/DB_DeleteSprayNSFWStatuscover all callers (menu:948/:987, commands, and both natives inmodule_api.inc).
One small edge case, not blocking: with the new NSFW retry in UpdateNSFWInfo (:1076-1080), if the DB is down at startup and an admin marks a player's spray SFW in the meantime, the delete is skipped (:1408) and the in-memory flag goes to false. When the DB comes back, the pending RetryNSFWSprayLookup finds the old spraynsfwlist row and OnSQLCheckNSFWSprayHashQuery sets g_bHasNSFWSpray back to true (:1174). It needs a startup outage plus an admin action during that window, so I'm fine leaving it. If you want to cover it, skip the retry once an admin has changed the status in-session.
LGTM.
Summary
SprayManager.sp): the ban, spray-hash-ban and NSFW lookups passed the client index as callback data and the callbacks only checkedIsValidClient(client). If the player left and someone else took the slot before the result came back, the other player got the first player's spray ban / hash ban / NSFW status. The queries now passGetClientUserId(client)and the callbacks resolve it withGetClientOfUserId.SprayManager.sp): the reconnect timer (ReconnectSQL) and the table-creation retry (RetryTableCreation) usedTIMER_FLAG_NO_MAPCHANGE, and nothing reconnects on map start. A failed connect or table creation shortly before a map change left the plugin without a database (bans not loaded or enforced) until a plugin reload. Both flags are dropped.SprayManager.sp):DB_UpdateSprayNSFWStatus/DB_DeleteSprayNSFWStatuscalledg_hDatabase.Querywithout a null check. They are reachable from the admin menu/commands and from theSprayManager_MarkSprayAsNSFW/SFWnatives (modules/module_api.inc), so they threw native errors while the DB was down. They now return early (the in-memory status still changes for the session).SprayManager.sp):UpdateNSFWInfosilently returned when the DB was not ready yet (late load, reconnect), so the player's NSFW flag was never loaded for the session. It now schedules the existingRetryNSFWSprayLookuptimer, like the ban and hash lookups do.SprayManager.sp,modules/functions.inc): the charset was only set with an asyncSET NAMESinside the table transaction, which does not survive the MySQL driver's auto-reconnect.OnSQLConnectednow callsdb.SetCharset("utf8mb4")once per new MySQL connection, before any query is queued.SET NAMESstays only as fallback whenSetCharsetfails.Test plan
sm plugins reload SprayManager) with a player whose spray is inspraynsfwlist: the spray is shown as NSFW after the retry.