/*
* CSB Match Config Loader
* Copyright (C) 2026 counter-strike-boost.com
*
* amx_mr15 / amx_mr12 exec the matching competitive config and then, a moment
* later, verify the critical cvars (mp_freezetime, mp_roundtime, mp_startmoney,
* sv_alltalk, mp_maxrounds). Any cvar that did not take the expected value is
* printed as a diff, so a silently broken match config is caught immediately.
*
* 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
new const PLUGIN[] = "CSB Match Config Loader"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pCfg15, g_pCfg12
new const g_szCheck[][] =
{
"mp_freezetime",
"mp_roundtime",
"mp_startmoney",
"sv_alltalk",
"mp_maxrounds"
}
new g_szExpected[sizeof(g_szCheck)][16]
new g_iPendingMode
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pCfg15 = register_cvar("csb_cfg_mr15", "csb_mr15.cfg")
g_pCfg12 = register_cvar("csb_cfg_mr12", "csb_mr12.cfg")
register_concmd("amx_mr15", "cmdMr15", ADMIN_BAN, "- exec the MR15 config and validate cvars")
register_concmd("amx_mr12", "cmdMr12", ADMIN_BAN, "- exec the MR12 config and validate cvars")
}
public cmdMr15(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
loadConfig(id, 15)
return PLUGIN_HANDLED
}
public cmdMr12(id, level, cid)
{
if (!cmd_access(id, level, cid, 1))
return PLUGIN_HANDLED
loadConfig(id, 12)
return PLUGIN_HANDLED
}
setExpected(const a[], const b[], const c[], const d[], const e[])
{
copy(g_szExpected[0], charsmax(g_szExpected[]), a)
copy(g_szExpected[1], charsmax(g_szExpected[]), b)
copy(g_szExpected[2], charsmax(g_szExpected[]), c)
copy(g_szExpected[3], charsmax(g_szExpected[]), d)
copy(g_szExpected[4], charsmax(g_szExpected[]), e)
}
loadConfig(id, mode)
{
new cfg[64]
if (mode == 15)
{
get_pcvar_string(g_pCfg15, cfg, charsmax(cfg))
setExpected("0", "1.75", "16000", "0", "30")
}
else
{
get_pcvar_string(g_pCfg12, cfg, charsmax(cfg))
setExpected("0", "1.75", "16000", "0", "24")
}
if (!cfg[0])
{
console_print(id, "[CSB] No config filename is set.")
return
}
server_cmd("exec %s", cfg)
server_exec()
client_print(0, print_chat, "[CSB] Executing match config %s (MR%d)...", cfg, mode)
g_iPendingMode = mode
new data[1]
data[0] = id
set_task(1.0, "taskVerify", 0, data, sizeof(data))
}
bool:cvarMatches(const a[], const b[])
{
new Float:fa = str_to_float(a)
new Float:fb = str_to_float(b)
new Float:diff = fa - fb
if (diff < 0.0)
diff = -diff
return diff < 0.01
}
public taskVerify(data[])
{
new id = data[0]
if (id && !is_user_connected(id))
id = 0
new mismatches = 0
for (new i = 0; i < sizeof(g_szCheck); i++)
{
new cur[16]
get_cvar_string(g_szCheck[i], cur, charsmax(cur))
if (!cvarMatches(cur, g_szExpected[i]))
{
mismatches++
if (id)
console_print(id, "[CSB] MISMATCH %s = %s (expected %s)", g_szCheck[i], cur, g_szExpected[i])
else
log_amx("[CSB MR] mismatch %s = %s (expected %s)", g_szCheck[i], cur, g_szExpected[i])
client_print(0, print_chat, "[CSB] Config warning: %s is %s, expected %s.", g_szCheck[i], cur, g_szExpected[i])
}
}
if (!mismatches)
{
client_print(0, print_chat, "[CSB] MR%d config verified: all critical cvars are correct.", g_iPendingMode)
if (id)
console_print(id, "[CSB] All %d critical cvars match. Config is good.", sizeof(g_szCheck))
}
else
{
client_print(0, print_chat, "[CSB] MR%d config has %d cvar(s) that did not take - check server console.", g_iPendingMode, mismatches)
}
}