/*
* CSB Multimod Manager
* Copyright (C) 2026 counter-strike-boost.com
*
* Rotates one server through several mods by map prefix. At map start it reads a
* mapping file, and for every block whose prefix matches the current map name it
* execs that block's server .cfg and amx_unpauses its plugins; non-matching
* blocks are amx_paused. This keeps kz_, deathrun_, surf_, zm_, jail_ maps on
* the right plugin set without swapping configs by hand.
*
* Inspired by Multi-Mod Manager by Emp`. Independent GPL re-implementation.
*
* 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
new const PLUGIN[] = "CSB Multimod Manager"
new const VERSION[] = "1.0.0"
new const AUTHOR[] = "counter-strike-boost.com"
new g_pEnabled
new g_szMap[40]
public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
g_pEnabled = register_cvar("csb_mm_enabled", "1")
register_concmd("csb_mm_reload", "cmdReload", ADMIN_CFG, "- re-apply the multimod config for the current map")
}
public plugin_cfg()
{
if (get_pcvar_num(g_pEnabled))
{
get_mapname(g_szMap, charsmax(g_szMap))
applyConfig()
}
}
public cmdReload(id, level, cid)
{
get_mapname(g_szMap, charsmax(g_szMap))
applyConfig()
console_print(id, "[CSB MM] Multimod config re-applied for %s.", g_szMap)
return PLUGIN_HANDLED
}
applyConfig()
{
new path[128]
copy(path, charsmax(path), "addons/amxmodx/configs/csb_multimod.ini")
if (!file_exists(path))
{
log_amx("[CSB MM] Config %s not found; nothing to do.", path)
return
}
new fp = fopen(path, "rt")
if (!fp)
return
new line[256], matched = 0
while (!feof(fp))
{
fgets(fp, line, charsmax(line))
trim(line)
if (!line[0] || line[0] == ';' || line[0] == '#')
continue
new prefix[32], cfgfile[64], plugins[160], pos = 0
pos = argParse(line, pos, prefix, charsmax(prefix))
pos = argParse(line, pos, cfgfile, charsmax(cfgfile))
argParse(line, pos, plugins, charsmax(plugins))
if (!prefix[0])
continue
if (containi(g_szMap, prefix) == 0)
{
matched++
if (cfgfile[0] && !equal(cfgfile, "-"))
server_cmd("exec addons/amxmodx/configs/%s", cfgfile)
togglePlugins(plugins, true)
}
else
{
togglePlugins(plugins, false)
}
}
fclose(fp)
server_exec()
if (matched)
log_amx("[CSB MM] Applied %d matching mod block(s) for map %s.", matched, g_szMap)
}
togglePlugins(const list[], bool:enable)
{
new item[32], len = strlen(list), k = 0
for (new i = 0; i <= len; i++)
{
if (i == len || list[i] == ';')
{
item[k] = 0
trim(item)
if (item[0])
{
if (enable)
server_cmd("amx_unpause ^"%s^"", item)
else
server_cmd("amx_pause ^"%s^"", item)
}
k = 0
}
else if (k < charsmax(item))
{
item[k++] = list[i]
}
}
}
argParse(const src[], start, dest[], maxlen)
{
new len = strlen(src), i = start
while (i < len && (src[i] == ' ' || src[i] == '^t'))
i++
new k = 0
while (i < len && src[i] != ' ' && src[i] != '^t')
{
if (k < maxlen)
dest[k++] = src[i]
i++
}
dest[k] = 0
return i
}