Skip to content

fix(plugin): enforce bans via engine-supported method (Synergy) - #27

Open
Rushaway wants to merge 2 commits into
mainfrom
fix/issue-1507
Open

fix(plugin): enforce bans via engine-supported method (Synergy)#27
Rushaway wants to merge 2 commits into
mainfrom
fix/issue-1507

Conversation

@Rushaway

@Rushaway Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member

Problem

Ports upstream issue sbpp#1507.

On some engines (notably Synergy, appid 17520) the server console command
banid <minutes> STEAM_X:Y:Z is rejected with "STEAM_ format is not supported
for this command"
. SB++ used that raw command in the paths that enforce a ban
against a player who is already connected / reconnecting and in the offline
temp-ban queue retry, so on those engines the ban was silently dropped and the
player kept rejoining (log shows Banid: "<><UNKNOWN><>").

sm_ban / PrepareBan already went through BanClient() and worked; only the
reconnect-check and queue paths still used the raw command.

Change

game/addons/sourcemod/scripting/sbpp_main.sp:

  • New helper SBPP_BanIdentity(auth, minutes, kickMessage) — if the target is
    still connected it bans through BanClient(... BANFLAG_AUTHID ...) so
    SourceMod picks a ban method the running engine supports; otherwise it falls
    back to banid using the SteamID3 ([U:1:W]) form, which those engines
    accept. Helper SBPP_Steam2ToSteam3() does the conversion.
  • VerifyBan() reconnect enforcement now calls BanClient() instead of
    banid <STEAM_...> + KickClient().
  • AddedFromSQLiteCallback() and UTIL_InsertTempBan() queue paths now use
    SBPP_BanIdentity().

No DB/schema changes; fork-specific DB.Format/admin_name INSERT paths are
untouched. No local SourcePawn compiler available — relying on plugin-build CI.

🤖 Generated with Claude Code

Rushaway and others added 2 commits September 3, 2026 10:55
The SB++ plugin enforced bans of already-connected / reconnecting players
and temp-ban queue retries by sending "banid <minutes> STEAM_X:Y:Z" to the
server console. Some engines (notably Synergy, appid 17520) reject the
STEAM_ format for that command ("STEAM_ format is not supported for this
command"), so the ban was silently dropped and the player kept rejoining.

Route these paths through BanClient() when the target is still connected so
SourceMod selects a ban method the running engine actually supports, and
fall back to "banid" with the SteamID3 form when the player is offline.

Fixes sbpp#1507

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Follow-ups on the Synergy engine-ban fix:

- Restore per-client translation. KickClient() set the global translation
  target before formatting; FormatEx() does not, so "Banned Check Site" /
  "Kick Reason" were rendered against whatever target happened to be set
  last. Added SetGlobalTransTarget() in VerifyBan() and UTIL_InsertTempBan().
- SBPP_BanIdentity(): take an optional client hint (UTIL_InsertTempBan
  already knows the target), skip fake clients so BanClient() cannot throw
  "Cannot ban fake client", bail on an empty authid, and clamp minutes to
  >= 1 so a misconfigured ProcessQueueTime cannot turn a temporary hold into
  a permanent ban written to banned_user.cfg.
- SBPP_BanIdentity(): build the localised "Banned Check Site" message when no
  kick message is supplied, instead of letting SourceMod kick with "Kicked"
  (hit by the AddedFromSQLiteCallback retry path).
- Use the BanIdentity() native for the offline path rather than a hand-built
  ServerCommand("banid ..."): it strips command separators from the identity
  and fires OnBanIdentity, matching the RemoveBan() call already used nearby.
- SBPP_Steam2ToSteam3(): reject STEAM_ID_PENDING / STEAM_ID_LAN and empty
  fields, and render the account id with %u so ids past 2^31 do not come out
  negative.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Rushaway

Rushaway commented Sep 3, 2026

Copy link
Copy Markdown
Member Author

Review: approach is correct, five follow-up fixes pushed (6895d2c)

Verdict: the fix is sound and I verified the mechanism against SourceMod's own source, not just the symptom.

BanClient() with BANFLAG_AUTHID formats banid %d %s with pPlayer->GetAuthString() (smn_banning.cpp:377-379), and m_AuthID is set straight from engine->GetPlayerNetworkIDString() (PlayerManager.cpp:2187-2196) — i.e. the engine's own rendering, which on Synergy is [U:1:x] exactly as the issue's log shows. That is why basebans works where the plugin's hand-built banid <STEAM_...> did not, and it confirms the connected-player path here is the right one. For non-Synergy engines GetPlayerNetworkIDString() returns the STEAM_ form, so behaviour is unchanged — no regression on existing engines. I also confirmed AddDelayedKick() is used for the authid path, so the kick happens next frame and UTIL_InsertTempBan()'s later use of auth/name/ip (which are g_sSteamIDs[target] & co. passed by reference, cleared in OnClientDisconnect) is safe.

SBPP_Steam2ToSteam3()'s math (Z*2+Y) and hardcoded universe 1 match CPlayer::UpdateAuthIds()'s own [U:%u:%u] rendering — hardcoding is in fact more correct than parsing X, since mods with UseInvalidUniverseInSteam2IDs render public accounts as STEAM_0.

Fixes pushed

  1. Lost per-client translation (real regression). KickClient() calls SetGlobalTarget(client) before formatting; FormatEx() does not. Both VerifyBan() and UTIL_InsertTempBan() were rendering %t against whatever target happened to be set last — nondeterministic, and no longer the banned player's language. Added SetGlobalTransTarget(client) at both sites.
  2. AddedFromSQLiteCallback() could kick with "Kicked". That path passes no kick message; if the target is connected, SourceMod substitutes its default "Kicked" instead of the site message. SBPP_BanIdentity() now builds the localised Banned Check Site phrase when none is supplied.
  3. SBPP_BanIdentity() hardening. Added an optional client hint (UTIL_InsertTempBan() already knows the target); dropped IsClientAuthorized() in favour of !IsFakeClient()g_sSteamIDs[] is populated at OnClientConnected, before authorization, and BanClient() throws a native error on fake clients; bail on an empty authid so we never fire a malformed banid; clamp minutes to >= 1, since ProcessQueueTime is config-driven and banid 0 means permanent plus a banned_user.cfg write.
  4. Use the BanIdentity() native for the offline path instead of ServerCommand("banid %d \"%s\""). It strips ; from the identity (the raw path passed the unquoted DB-sourced authid to the console on the fallback branch), fires OnBanIdentity, and is symmetric with the RemoveBan() call twelve lines up in the same callback.
  5. SBPP_Steam2ToSteam3() edge cases. Reject STEAM_ID_PENDING / STEAM_ID_LAN explicitly (they reached ExplodeString and only failed by luck of the split count) and empty Y/Z fields; render the account id with %u rather than %d, since account ids past 2^31 would otherwise come out negative.

Checked and found fine

  • No OnBanClient / OnBanIdentity hooks anywhere in scripting/, and basebans is unloaded at startup — no double-ban, no recursion.
  • Site kick message preserved in VerifyBan() (BanReason is passed as both reason and kick message).
  • time == 0 never reaches these paths as a permanent engine ban; writeid is not triggered.
  • Helpers placed after use — fine in SourcePawn; no merge residue.

CI green: Compile SourceMod plugins — pass, zero spcomp warnings.

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