/*
* CSB Anti Retry
* Copyright (C) 2026 counter-strike-boost.com
*
* Caches a player's team, frags, deaths and money on disconnect (keyed by
* SteamID) and restores it if they reconnect within a short window, so the
* classic 'retry to reset' trick can't be used to escape a losing state.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version. It is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See for details.
*/
#include
#include
#include
new const PLUGIN[] = "CSB Anti Retry"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
#define MAX_RECORDS 64
new g_pEnabled, g_pWindow
new g_szAuth[MAX_RECORDS][35]
new g_iTeam[MAX_RECORDS]
new g_iFrags[MAX_RECORDS]
new g_iDeaths[MAX_RECORDS]
new g_iMoney[MAX_RECORDS]
new g_iStamp[MAX_RECORDS]
new bool:g_bUsed[MAX_RECORDS]
new g_iCount = 0
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_antiretry_enabled", "1")
g_pWindow = register_cvar("csb_antiretry_window", "90")
}
public client_disconnected(id)
{
if (!get_pcvar_num(g_pEnabled) || is_user_bot(id) || is_user_hltv(id))
return
new authid[35]
get_user_authid(id, authid, charsmax(authid))
if (!authid[0] || equal(authid, "STEAM_ID_PENDING"))
return
new slot = findRecord(authid)
if (slot == -1)
slot = newRecord()
if (slot == -1)
return
copy(g_szAuth[slot], charsmax(g_szAuth[]), authid)
g_iTeam[slot] = _:cs_get_user_team(id)
g_iFrags[slot] = get_user_frags(id)
g_iDeaths[slot] = cs_get_user_deaths(id)
g_iMoney[slot] = cs_get_user_money(id)
g_iStamp[slot] = get_systime()
g_bUsed[slot] = true
}
public client_putinserver(id)
{
if (!get_pcvar_num(g_pEnabled) || is_user_bot(id) || is_user_hltv(id))
return
// wait for the join to settle before touching team/money
set_task(2.0, "taskRestore", id)
}
public taskRestore(id)
{
if (!is_user_connected(id))
return
new authid[35]
get_user_authid(id, authid, charsmax(authid))
new slot = findRecord(authid)
if (slot == -1)
return
if (get_systime() - g_iStamp[slot] > get_pcvar_num(g_pWindow))
{
g_bUsed[slot] = false
return
}
new CsTeams:team = CsTeams:g_iTeam[slot]
if (team == CS_TEAM_T || team == CS_TEAM_CT)
cs_set_user_team(id, team)
set_user_frags(id, g_iFrags[slot])
cs_set_user_deaths(id, g_iDeaths[slot])
cs_set_user_money(id, g_iMoney[slot])
g_bUsed[slot] = false
client_print(id, print_chat, "[CSB] Your previous state was restored - retry can't be used to reset it.")
log_amx("[CSB AntiRetry] restored state for %s", authid)
}
findRecord(const authid[])
{
for (new i = 0; i < g_iCount; i++)
{
if (g_bUsed[i] && equal(g_szAuth[i], authid))
return i
}
return -1
}
newRecord()
{
for (new i = 0; i < g_iCount; i++)
{
if (!g_bUsed[i])
return i
}
if (g_iCount < MAX_RECORDS)
return g_iCount++
return -1
}