Skip to content

feat: add SBPP_BanPlayerBySteamId native for offline player bans - #23

Open
Rushaway wants to merge 2 commits into
mainfrom
port/pr-1511
Open

feat: add SBPP_BanPlayerBySteamId native for offline player bans#23
Rushaway wants to merge 2 commits into
mainfrom
port/pr-1511

Conversation

@Rushaway

@Rushaway Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member

Ports upstream sbpp#1511 onto the srcdslab fork.

What it adds

SBPP_BanPlayerBySteamId(int iAdmin, const char[] steamId, const char[] name, int iTime, const char[] sReason) — a native for recording a ban in the SBPP database for a player who is no longer connected (TK managers, anti-cheat, and other automated systems that fire after the target disconnects). SBPP_BanPlayer requires a live client index; this native takes a STEAM_0:X:Y string and a display name instead.

Behaviour (unchanged from upstream):

  • Throws a native error if steamId is not SteamID2 format.
  • Runs a duplicate-check SELECT first and skips the INSERT if an active ban already exists.
  • Falls back to a server-lookup subquery for sid when serverID == -1.
  • Fires SBPP_OnBanPlayer with iTarget = -1 only after the INSERT succeeds.

sourcebanspp.inc gains the native declaration and MarkNativeAsOptional.

Fork adaptations

The fork's _bans INSERT statements carry an extra admin_name column populated by an IFNULL((SELECT user FROM %s_admins WHERE authid = ... OR authid REGEXP ...), '') subquery. The ported INSERT (both the serverID == -1 and registered-server branches) was adjusted to include that column and subquery, matching the fork's existing UTIL_InsertBan / Native_SBBanPlayer queries. No fork behaviour was reverted.

Notes

🤖 Generated with Claude Code

Ports upstream sbpp#1511 onto the srcdslab fork.

Adds SBPP_BanPlayerBySteamId, a native for banning players who are no
longer connected to the server (TK managers, anti-cheat, etc.). It
accepts a SteamID2 string and player name directly instead of a client
index.

- Validates SteamID2 format before inserting
- Runs a duplicate-check SELECT first, skipping the INSERT on an active ban
- Fires SBPP_OnBanPlayer with iTarget = -1 only after the INSERT succeeds
- Falls back to a server-lookup subquery for sid when serverID == -1

Fork adaptation: the INSERT keeps the fork's admin_name column and the
IFNULL((SELECT user FROM %s_admins ...)) subquery, matching the fork's
UTIL_InsertBan / Native_SBBanPlayer statements.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Sigibert <christoph.ziegler@hotmail.de>
Follow-up review pass on the sbpp#1511 port.

- Grow the INSERT buffer from 1024 to 2048. The fork's extra admin_name
  column adds a second IFNULL((SELECT user FROM %s_admins ...)) subquery
  plus two more adminAuth expansions, pushing the worst-case rendering to
  ~1210 bytes (128-byte escaped authid, 64-byte escaped name, 256-byte
  escaped reason, four DatabasePrefix expansions). FormatEx would have
  silently truncated that into invalid SQL.
- Size reasonEscaped as sizeof(reason) * 2 + 1 (257) instead of 256.
  SQL_EscapeString refuses to write anything when the destination is one
  byte short, so a 128-char reason of all quotes would have spliced an
  uninitialised buffer into the INSERT.
- Replace the "STEAM_" prefix test with UTIL_IsValidSteamID2(), a full
  STEAM_X:Y:Z check. The INSERT slices authid[8] to build the
  '^STEAM_[0-9]:%s$' REGEXP, so "STEAM_junk" previously produced a
  garbage ban row instead of an error. The helper short-circuits before
  reading past the terminator on every truncated prefix.
- Reject negative iTime, which would otherwise store a negative length
  and an ends timestamp in the past.
- Bounds-check iAdmin before IsClientInGame()/g_sSteamIDs[] and require
  the ban flag, mirroring Native_SBBanPlayer. An out-of-range index
  previously faulted the native.
- Normalise a non-live iAdmin to 0 so SBPP_OnBanPlayer subscribers never
  receive an unusable client index.
- Use the callback's db handle rather than the global DB, make both
  callbacks public, and drop the "[SBPP] " log prefix, matching
  SelectAddbanCallback / InsertAddbanCallback.
- Document the new error conditions on the native and the iTarget = -1
  contract on the SBPP_OnBanPlayer forward.

Verified with spcomp 1.12.0.7253: sbpp_main, sbpp_sleuth and sbpp_report
all compile clean with no warnings.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Sigibert <christoph.ziegler@hotmail.de>
@Rushaway

Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Review: approve after fixes (pushed as 99b7e22)

The port itself is faithful and the fork adaptation is correct. I verified the parts most likely to go wrong in a port of this shape:

Correct as ported

  • admin_name column adaptation is right in both branches. Column list is 11 wide (authid, name, created, ends, length, reason, aid, adminIp, admin_name, sid, country) and the VALUES list is 11 wide, in the same order — IFNULL((SELECT user ...)) sits between adminIp and sid, matching SelectAddbanCallback. Format specifiers vs. arguments: 16/16 for the serverID == -1 branch, 14/14 for the registered-server branch.
  • Style matches the fork: FormatEx + %s with manual DB.Escape, not DB.Format.
  • The duplicate-check SELECT is byte-identical to the existing one at sbpp_main.sp:723.
  • adminAuth[8] slicing is correct for the '^STEAM_[0-9]:%s$' REGEXP, and the "STEAM_ID_SERVER" fallback slices to "_SERVER", which harmlessly matches nothing and falls through to the IFNULL defaults.
  • DataPack lifetime is sound: pack is deleted on both the error and success paths of the select callback, fwdPack is read and deleted before the results == null check in the insert callback. No leak, no use-after-free.
  • .inc: signature matches the implementation, MarkNativeAsOptional present, no BOM, no merge residue.

Fixed

  1. Query buffer truncation (the fork-specific one). char query[1024] was inherited from upstream, but the extra admin_name sub-select adds a second IFNULL((SELECT user FROM %s_admins ...)) plus two more adminAuth expansions. Worst case is now ~1210 bytes (128-byte escaped authid, 64-byte escaped name, 256-byte escaped reason, four DatabasePrefix and four adminAuth expansions), which FormatEx truncates into invalid SQL — silently, since the failure only shows up as a logged query error. Grown to 2048.
  2. reasonEscaped off-by-one. reason is 128 bytes, so the escape destination needs 128 * 2 + 1 = 257, not 256. SQL_EscapeString refuses to write anything when the destination is one byte short, so a 128-char reason of all quotes would have spliced an uninitialised stack buffer into the INSERT. Now sizeof(reason) * 2 + 1. (Note: the same off-by-one exists pre-PR at sbpp_main.sp:1246 and :1457 — not touched here.)
  3. SteamID2 validation was prefix-only. strncmp(steamId, "STEAM_", 6) accepts "STEAM_junk", and since the INSERT slices authid[8] for the REGEXP, that produced a garbage ban row rather than an error. Replaced with UTIL_IsValidSteamID2(), a full STEAM_X:Y:Z check that short-circuits before reading past the terminator on every truncated prefix.
  4. Negative iTime accepted. Stored a negative length and an ends timestamp in the past. Now rejected.
  5. iAdmin unbounded. if (!admin || !IsClientInGame(admin)) faults on an out-of-range index, and g_sSteamIDs[admin] would read out of bounds. Now bounds-checked, and the Admin_Ban flag check from Native_SBBanPlayer is applied for consistency with the sibling native. (Flag this if you'd rather keep upstream's permissive behaviour — it's a deliberate deviation.)
  6. Stale index leaking into the forward. A non-live iAdmin was still written to the DataPack and pushed to SBPP_OnBanPlayer. Now normalised to 0.
  7. Style: use the callback's db handle instead of the global DB, make both callbacks public, drop the [SBPP] log prefix — matching SelectAddbanCallback / InsertAddbanCallback.
  8. Docs: recorded the error conditions on the native and the iTarget = -1 contract on the SBPP_OnBanPlayer forward, so third-party subscribers know to range-check.

Not changed (noted for the record)

  • The duplicate-check uses authid = '%s' rather than the universe-agnostic authid REGEXP '^STEAM_[0-9]:%s$' used by the connect-time check at :353. Worst case is a redundant row when the same account is stored under a different universe digit. sm_addban has the same behaviour, so this matches file convention.
  • adminAuth is interpolated unescaped. It comes from GetClientAuthId / the literal "STEAM_ID_SERVER", never from user input, and every existing _bans INSERT in this file does the same.

Verification: compiled locally with spcomp 1.12.0.7253 — sbpp_main.sp, sbpp_sleuth.sp and sbpp_report.sp all build clean with zero warnings. CI on 99b7e22 is green (Compile SourceMod plugins: pass).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant