Repository navigation
Production hardening (0.6.1): SQL buffer + FK race, server_id churn, negative-score flush drop - #7
Merged
Merged
Conversation
Bug A — query buffer truncation in player_stats upsert ------------------------------------------------------- Bizzy_Session_Flush builds a wide INSERT INTO player_stats … ON DUPLICATE KEY UPDATE statement into a stack `char sql[2048]`. After the schema grew through migration 010 the rendered statement exceeded that buffer and FormatEx silently truncated, depending on the per-int substitution widths. MySQL then rejected the malformed statement, surfacing as: Unknown column 'damage_to_ta' / 'damage_to_tan' Unknown column 'VALUE' / 'VALU' / 'V' You have an error in your SQL syntax … near '' at line 1 …and the entire flush transaction was dropped, losing the session's accumulated counters. Bump the buffer to 4096 with a clear comment + #5 reference. Long-term fix (parameterized queries — no string-build) is noted there as the follow-up; this is the safe tactical fix to stop bleeding data. Bug B — tank_records.fk_tr_map FK violation on boss spawn --------------------------------------------------------- `g_CurrentMapId` is the resolved `maps.id` for the active map and is populated through a two-step async chain (INSERT IGNORE INTO maps → SELECT id → set g_CurrentMapId) kicked off from `OnMapStart`. On a plugin hot-reload mid-map or a DB connection that completes after `OnMapStart` has already fired, `g_CurrentMapId` stays at 0 and any boss insert (tank_records / witch_records) FK-violates against maps.id. Three-part fix: - `OnServerLookup` (the DB-ready callback) now calls `Bizzy_Identity_ResolveMap()` after setting `g_ServerId`. Covers the late-DB-connect / hot-reload-mid-map case. - `Bizzy_Identity_ResolveMap` now refreshes `g_CurrentMap` from the engine via `GetCurrentMap()` on every call, so it doesn't no-op when invoked from a code path other than `OnMapStart`. - Tank- and witch-spawn inserts in tank_witch.sp now bail early when `g_CurrentMapId == 0`. Defensive — converts any remaining edge case from a noisy FK error to a silently dropped boss record. Files touched ------------- plugin/scripting/bizzymod_stats/session.sp - sql buffer 2048→4096 plugin/scripting/bizzymod_stats/identity.sp - resolve on DB-ready; refresh on every call plugin/scripting/bizzymod_stats/tank_witch.sp - g_CurrentMapId guards CHANGELOG.md - [0.6.1] entry Refs #5
LoadOrCreateServerKey wrote the key via BuildPath(Path_SM,
"cfg/sourcemod/bizzymod_stats.server_key"), which resolves to
addons/sourcemod/cfg/sourcemod/ -- a directory that does not exist. The
OpenFile("w") failed, the key was never persisted, and every boot
generated a fresh random key and therefore a fresh servers.id, so player
stats fragmented across a new server_id on each restart.
Store the keyfile in addons/sourcemod/data/ instead (Path_SM root + the
writable SM data dir that always exists). The key now persists and the
server keeps a single stable server_id across restarts.
Verified on a live SM 1.12 server: after the fix a full restart returns
under the same server_id with no "could not persist" error.
Refs #6
Bizzy_Session_Flush's career_bests upsert inserted pointsThisSession into most_points_in_session, which is INT UNSIGNED. A player ending a session with negative points (griefing / FF with negative_score on) produced a negative value -> MySQL "Out of range value for column 'most_points_in_session'" -> the career-bests query failed and rolled back the whole flush transaction, dropping that session's stats. Clamp with GREATEST(0, pointsThisSession) in the INSERT; a negative career best is meaningless. The second career_bests upsert (peak_dps, distances, HP, ...) takes only non-negative values and is unaffected. Verified live on a SM 1.12 server (2026-08-26). Refs #7
6 tasks
The self-hosted Linux runner lacks the unzip/zip binaries and has no
passwordless sudo, so ensure-prereqs.sh could not auto-install them and
hard-failed both ci.yml and release.yml at the prereq gate — even though
neither binary is actually used on the Linux path:
* unzip — only tests/run.sh's Windows toolchain branch uses it; the
Linux runner fetches SourceMod as .tar.gz (tar).
* zip — only release.yml used it, to package the release archive.
Drop unzip/zip from REQUIRED_BINS, and package the release .zip with
python3's stdlib zipfile (python3 is already a required, present prereq)
instead of the zip binary. The archive is byte-for-identical in layout
(entries prefixed with the version dir). No runner changes needed.
Once the prereq gate stopped hard-failing, the next step (Install pymysql) failed with 'error: externally-managed-environment' — the runner's system Python is PEP-668 externally-managed, so a plain 'pip install --user' is refused. Add --break-system-packages, the sudo-free escape hatch; --user keeps it in the user site (~/.local). Applied to both ci.yml and release.yml (identical step).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidates the three production-hardening plugin fixes onto one branch. Closes #5. Supersedes and replaces PR #6 (which carried only the #5 fix). The server-key-churn and negative-score fixes did not have their own issues filed — they're grouped here.
All three were verified live on the SM 1.12 production server before this PR, and are already vendored and running in the
bizzymod-campaigndeployment (campaign PR bogware/bizzymod-campaign#9). This lands them upstream and cuts a single0.6.1patch release. Three commits, one per fix — each self-contained and independently revertable.Fix two production bugs from #5— closes #5INSERT INTO player_stats … ON DUPLICATE KEY UPDATE …outgrew itschar sql[2048]after migration 010, soFormatExsilently chopped the statement mid-keyword (Unknown column 'VALUE'/'VALU'/'V',syntax error near '') and the whole flush transaction was rejected — sessions never persisted. Buffer bumped to 4096; parameterized-query rewrite noted as the long-term follow-up.tank_records.fk_tr_mapFK violation on tank/witch spawn.g_CurrentMapIdis resolved via an asyncmapsinsert→select that only kicks off inOnMapStart; on hot-reload or DB-late-connect mid-map it stayed 0 and the next boss insert FK-violated. Fix: the DB-ready callback now resolves the map itself,ResolveMapre-readsGetCurrentMap()every call, and tank/witch inserts bail ong_CurrentMapId == 0.Fix server-key persistence to stop server_id churnBuildPath(Path_SM, "cfg/sourcemod/…")→addons/sourcemod/cfg/sourcemod/, a directory that doesn't exist, so the write silently failed and every boot minted a fresh key (hence a freshservers.id), fragmenting stats across manyserver_ids. Keyfile now lives in the writable SMdata/dir; the server keeps one stableserver_idacross restarts. Verified: full restart returns under the same id with no "could not persist" error.Clamp career-best points to >=0negative_score) fed a negative value intomost_points_in_session(INT UNSIGNED) → "Out of range value" → the career-bests query failed and rolled back the entire flush, dropping the session. Clamp withGREATEST(0, …). Verified live 2026-08-26: apts=-128session now flushes cleanly.Notes
[0.6.1]section covering all four fixes.server_ids 208–219 onto the stable 218 in production; no schema/plugin change needed for that.