diff --git a/game/addons/sourcemod/scripting/sbpp_main.sp b/game/addons/sourcemod/scripting/sbpp_main.sp index 75746aa6c..d793f32fd 100644 --- a/game/addons/sourcemod/scripting/sbpp_main.sp +++ b/game/addons/sourcemod/scripting/sbpp_main.sp @@ -1624,8 +1624,7 @@ public void AddedFromSQLiteCallback(Database db, DBResultSet results, const char } else { // the insert failed so we leave the record in the queue and increase our temporary ban - FormatEx(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth); - ServerCommand(buffer); + SBPP_BanIdentity(auth, ProcessQueueTime); } delete dataPack; } @@ -1692,7 +1691,7 @@ public void VerifyBan(Database db, DBResultSet results, const char[] error, int if (results.RowCount > 0) { - char buffer[40], Name[MAX_NAME_LENGTH], Query[512]; + char Name[MAX_NAME_LENGTH], Query[512]; // Amending to ban record's IP field if (results.FetchRow()) @@ -1731,9 +1730,15 @@ public void VerifyBan(Database db, DBResultSet results, const char[] error, int db.Query(ErrorCheckCallback, Query, client, DBPrio_High); - FormatEx(buffer, sizeof(buffer), "banid 5 %s", clientAuth); - ServerCommand(buffer); - KickClient(client, "%t", "Banned Check Site", WebsiteAddress); + // Ban via BanClient() so SourceMod bans with the engine's own auth + // string. The raw "banid " console command is rejected by + // some engines (e.g. Synergy), which left the player unbanned. + // SetGlobalTransTarget() keeps the message in the client's language, + // which KickClient() used to do for us. + char BanReason[256]; + SetGlobalTransTarget(client); + FormatEx(BanReason, sizeof(BanReason), "%t", "Banned Check Site", WebsiteAddress); + BanClient(client, 5, BANFLAG_AUTHID, BanReason, BanReason, "sbpp"); return; } @@ -2595,11 +2600,7 @@ stock void UTIL_InsertTempBan(int time, const char[] name, const char[] auth, co delete dataPack; // we add a temporary ban and then add the record into the queue to be processed when the database is available - char buffer[50]; - - Format(buffer, sizeof(buffer), "banid %d %s", ProcessQueueTime, auth); - - ServerCommand(buffer); + char kickMessage[512] = ""; if (IsClientInGame(client)) { @@ -2608,9 +2609,13 @@ stock void UTIL_InsertTempBan(int time, const char[] name, const char[] auth, co FormatEx(length, sizeof(length), "permanent"); else FormatEx(length, sizeof(length), "%d %s", time, time == 1 ? "minute" : "minutes"); - KickClient(client, "%t\n\n%t", "Banned Check Site", WebsiteAddress, "Kick Reason", admin, reason, length); + // KickClient() used to localise this for us; keep doing so. + SetGlobalTransTarget(client); + FormatEx(kickMessage, sizeof(kickMessage), "%t\n\n%t", "Banned Check Site", WebsiteAddress, "Kick Reason", admin, reason, length); } + SBPP_BanIdentity(auth, ProcessQueueTime, kickMessage, client); + char banName[MAX_NAME_LENGTH], banReason[256], query[512]; SQLiteDB.Escape(name, banName, sizeof(banName)); @@ -2838,4 +2843,113 @@ stock void AccountForLateLoading() } } +/** + * Converts a SteamID2 ("STEAM_X:Y:Z") into a SteamID3 ("[U:1:W]"). + * + * The universe is always rendered as 1 (public), mirroring how SourceMod + * itself renders AuthId_Steam3: mods that set "UseInvalidUniverseInSteam2IDs" + * render public-universe accounts as STEAM_0, so the X field is not a + * trustworthy universe. + * + * @return true on success, false if the input was not a convertible SteamID2. + */ +stock bool SBPP_Steam2ToSteam3(const char[] steam2, char[] buffer, int maxlen) +{ + if (strncmp(steam2, "STEAM_", 6) != 0) + return false; + + // "STEAM_ID_PENDING" / "STEAM_ID_LAN" carry no account id. + if (strncmp(steam2[6], "ID_", 3) == 0) + return false; + + char parts[3][12]; + if (ExplodeString(steam2[6], ":", parts, sizeof(parts), sizeof(parts[])) != 3) + return false; + + if (parts[1][0] == '\0' || parts[2][0] == '\0') + return false; + + int y = StringToInt(parts[1]); + int z = StringToInt(parts[2]); + + // %u, not %d: account ids past 2^31 would otherwise render negative. + FormatEx(buffer, maxlen, "[U:1:%u]", (z * 2) + y); + return true; +} + +/** + * Bans a player by authid regardless of whether they are still connected. + * + * Uses BanClient() when the player is in game so SourceMod bans with the + * engine's own auth string (SourceMod feeds "banid" the string the engine + * itself reports for that player). Some engines (e.g. Synergy, appid 17520) + * reject the "STEAM_" format passed to the "banid" console command, which + * silently dropped the ban. When the player is offline we ban by identity + * using the SteamID3 form for the same reason. + * + * @param auth SteamID2 of the target. + * @param minutes Ban length in minutes. + * @param kickMessage Message shown to the target if still connected; the + * generic "check the website" phrase is used when empty. + * @param client Optional client index hint for the target. + */ +stock void SBPP_BanIdentity(const char[] auth, int minutes, const char[] kickMessage = "", int client = 0) +{ + if (auth[0] == '\0') + return; + + // These are always temporary holds until the database catches up, so never + // let a misconfigured length through: "banid 0" is a permanent ban and + // SourceMod additionally writes it out to banned_user.cfg. + if (minutes < 1) + minutes = 5; + + int target = 0; + + if (client > 0 && client <= MaxClients && IsClientInGame(client) && !IsFakeClient(client) + && StrEqual(g_sSteamIDs[client], auth, false)) + { + target = client; + } + else + { + for (int i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && StrEqual(g_sSteamIDs[i], auth, false)) + { + target = i; + break; + } + } + } + + if (target) + { + char message[512]; + + if (kickMessage[0] != '\0') + { + strcopy(message, sizeof(message), kickMessage); + } + else + { + SetGlobalTransTarget(target); + FormatEx(message, sizeof(message), "%t", "Banned Check Site", WebsiteAddress); + } + + // BanClient() kicks on the next frame, so callers passing a global + // buffer (g_sSteamIDs[] & co.) can keep using it after this returns. + BanClient(target, minutes, BANFLAG_AUTHID, message, message, "sbpp"); + return; + } + + // BanIdentity() rather than a hand-built ServerCommand("banid ..."): it + // strips command separators out of the identity and notifies OnBanIdentity. + char steam3[MAX_AUTHID_LENGTH]; + if (SBPP_Steam2ToSteam3(auth, steam3, sizeof(steam3))) + BanIdentity(steam3, minutes, BANFLAG_AUTHID, kickMessage, "sbpp"); + else + BanIdentity(auth, minutes, BANFLAG_AUTHID, kickMessage, "sbpp"); +} + //Yarr!