/* * 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 }