An in-game game mode switcher for Counter-Strike 2 dedicated servers running CounterStrikeSharp.
!gamemode opens a menu built from JSON presets you edit. Each preset is a display name plus the game_type / game_mode / game_alias / map the plugin should apply. The plugin does not scan workshop directories.
!gamemodeopens an in-game CenterHTML menu (AdminPlus-style interaction: W/S move, E select, A back, R exit). The player is held in place while the menu is open; a possessed bot is held the same way. The menu closes on round restart and respawn.- Top-level groups come from
Presets[].Group. A group whose labels share a known mode prefix (休闲/竞技/军备竞赛/爆破/死亡竞赛/训练) or an explicitSubGroupopens a second-level folder, then the map names with that prefix stripped. Wingman and Retakes live under Classic viaSubGroup(搭档/回防) without changingLabel. Custom stays two-level. Consolecss_gamemodestill matches the fullLabel - Selecting an option announces a countdown, then writes
game_type/game_mode, runsgame_aliaswhen set, waits briefly, and changes map:host_workshop_map <id>whenMapisworkshop/<id>/…, otherwisechangelevel <Map> - After a successful plugin-initiated switch, a preset can reassert
mp_ignore_round_win_conditions 0,bot_quota 0,bot_quota_mode fill,mp_autoteambalance 0,mp_limitteams 0, and kick inherited bots, then run optional extra commands - Optional
ToggleProfileis sent to PluginToggle ascss_plugintoggle profile <name>immediately beforechangelevel/host_workshop_map. Empty clears the profile. Do not put plugin load/unload inAfter - First-run JSON ships the current vanilla mode/map table. Workshop maps are added by hand
- Localized 简体中文 / en-US chrome (prefix, permissions, countdown). Preset labels are used as written
- Permission gated by a configurable admin flag (
@css/changemapby default)
- CS2 dedicated server (Linux/Windows)
- Metamod:Source + CounterStrikeSharp (Minimum API 260, tested with CSS 1.0.372)
- Grab the latest zip from Releases
- Extract it into
addons/counterstrikesharp/plugins/ - Restart the server — done
Do not replace addons/counterstrikesharp/configs/plugins/CS2-Switch-Gamemode/CS2-Switch-Gamemode.json when updating the DLL. Edit that file to add or remove menu entries.
| Command | Where | Description |
|---|---|---|
!gamemode |
in-game chat | Open the preset menu |
css_gamemode |
console / rcon | No args: list presets. With a preset Label: start that switch (sets _pendingSwitch) |
After picking a preset the server announces the switch, counts down (5s by default; the center
HTML is painted every tick, same channel as the menu), then sets
game_type / game_mode, runs game_alias when the preset has one, and after a short delay
changes map. Map values of the form workshop/<id>/<bsp> run host_workshop_map <id> so the
handshake carries addons:'<id>'. Other maps still use changelevel <Map>. changelevel workshop/…
does not host the addon and can fail with CREATE_SERVER_FAILED (58) on this server.
The explicit type/mode write is required when leaving custom mode: game_alias alone can leave
game_type 3 in place, so Valve keeps executing gamemode_custom.cfg. host_workshop_map can
also leave customgamemode stuck after changelevel back to an official map (map is right,
game_type/game_mode stay 3/0). If the map matches the preset but the mode does not, the
plugin waits until that world is simulating, then retries type/mode/alias once and execs
the Valve gamemode_<alias>.cfg plus the matching _server.cfg. A second changelevel loads
those cfgs but disconnects clients with CREATE_SERVER_FAILED (58). game_alias can make
type/mode read 0/1 without exec'ing the Valve cfg; skipping the cfg exec leaves custom round
rules behind.
If the new map reaches the preset type, mode, and map, the plugin may then reassert
mp_ignore_round_win_conditions 0 plus the BOT population policy and run After commands,
unless that preset sets ResetBots to false.
addons/counterstrikesharp/configs/plugins/CS2-Switch-Gamemode/CS2-Switch-Gamemode.json is auto-generated on first load:
| Field | Meaning |
|---|---|
Group |
First-level menu heading. Groups appear in JSON order |
SubGroup |
Optional second-level heading. Empty: derive from a known Label prefix (休闲 竞技 军备竞赛 爆破 死亡竞赛 训练) |
Label |
Preset name for css_gamemode and the leaf row. Menu map lists strip a matching {SubGroup} prefix |
GameType / GameMode |
Written before the map command |
GameAlias |
game_alias is skipped when this is empty |
Map |
workshop/<id>/<bsp> → host_workshop_map <id> (use the addon's default BSP name). Anything else → changelevel <Map> |
ResetBots |
Default true. BOT reset runs only when this is true and ResetBotPopulationAfterSwitch is true |
After |
Commands run after map start and after the BOT reset, if any. Empty strings are dropped |
ToggleProfile |
Optional. Sent as css_plugintoggle profile <name> before the map command. Empty sends profile - |
A missing Presets key (plugin upgrade) is filled with the default vanilla table. "Presets": [] leaves the menu empty. Entries with an empty Label or Map are dropped on load.
The first-run / upgrade default contains the previous vanilla pools (Casual, Competitive, Wingman, Retakes, Arms Race, Demolition, Deathmatch, Training) and no workshop maps. Add workshop rows yourself.
Requires the .NET 10 SDK:
dotnet build -c ReleaseDeploy only these files into the plugin folder: CS2-Switch-Gamemode.dll, .deps.json, .pdb and lang/.
面向 CS2 独服(CounterStrikeSharp)的游戏模式切换插件。
!gamemode 打开的菜单来自你编辑的 JSON 预设。每条预设是显示名,加上插件要写入的 game_type / game_mode / game_alias 和地图。插件不扫描创意工坊目录。
- 菜单交互复刻 AdminPlus:W/S 移动、E 确认、A 返回、R 退出。菜单打开时玩家将被固定在原地;已接管的机器人同样受影响。回合开始或重生时菜单会关闭
- 一级分组来自
Presets[].Group。同一组里 Label 带已知模式前缀(休闲/竞技/军备竞赛/爆破/死亡竞赛/训练)或写了SubGroup时,会再拆一层文件夹,第三层地图名去掉该前缀。现网搭档 / 回防挂在「经典模式」下(SubGroup),Label 仍是地图名。自定义仍是两层。控制台css_gamemode仍精确匹配完整Label - 选中后全服广播并倒计时,然后写
game_type/game_mode,有 alias 再跑game_alias,短延迟后切图:Map为workshop/<id>/…时发host_workshop_map <id>,否则changelevel <Map> - 切图成功后,该条预设可重申
mp_ignore_round_win_conditions 0和bot_quota 0等命令,再执行可选的After - 可选
ToggleProfile在changelevel/host_workshop_map之前发给 PluginToggle:css_plugintoggle profile <name>。空则发profile -。不要把插件 load/unload 写进After - 首次生成的 JSON 带当前原版模式/地图表;工坊图需要自己加
- 界面铬(前缀、权限、倒计时)走简体中文 / English;预设标签按 JSON 原文显示
- 权限默认
@css/changemap
下载 Release 压缩包,解压到 addons/counterstrikesharp/plugins/ 后重启服务器即可。
更新 DLL 时不要覆盖 addons/counterstrikesharp/configs/plugins/CS2-Switch-Gamemode/CS2-Switch-Gamemode.json。增删菜单条目只改这个文件。
首次加载会生成 CS2-Switch-Gamemode.json:
{
"Version": 3,
"RequiredFlag": "@css/changemap",
"CountdownSeconds": 5,
"ResetBotPopulationAfterSwitch": true,
"BotPopulationResetDelaySeconds": 0.75,
"MenuCommand": "css_gamemode",
"Presets": [
{
"Group": "经典模式",
"Label": "竞技 炙热沙城 II",
"GameType": 0,
"GameMode": 1,
"GameAlias": "competitive",
"Map": "de_dust2",
"ResetBots": true,
"After": []
},
{
"Group": "自定义",
"Label": "工坊地图",
"GameType": 3,
"GameMode": 0,
"GameAlias": "custom",
"Map": "workshop/<id>/<bsp>",
"ResetBots": false,
"After": []
}
]
}| 字段 | 含义 |
|---|---|
Group |
一级菜单分组,按 JSON 出现顺序 |
SubGroup |
可选二级标题。空则从 Label 已知前缀推导(休闲 竞技 军备竞赛 爆破 死亡竞赛 训练) |
Label |
css_gamemode 用的完整名;第三层地图列表会去掉匹配的 {SubGroup} 前缀 |
GameType / GameMode |
切图命令前写入 |
GameAlias |
空则不跑 game_alias |
Map |
workshop/<id>/<bsp> → host_workshop_map <id>(BSP 用该工坊项的默认图)。其它 → changelevel <Map> |
ResetBots |
缺省 true。清 BOT 仅在本字段为 true 且 全局 ResetBotPopulationAfterSwitch 为 true 时执行 |
After |
新图起来、清 BOT 之后依次执行。空字符串会丢掉 |
ToggleProfile |
可选。切图命令前发 css_plugintoggle profile <name>。空则发 profile - |
缺少 Presets 键(升级旧配置)会填入默认原版表。"Presets": [] 菜单为空。Label 或 Map 为空的条目加载时丢弃。
默认表包含原先的原版图池(休闲、竞技、搭档、回防、军备竞赛、爆破、死亡竞赛、训练),不含工坊图。工坊条目自己加。
切图时序由插件固定,不写在 JSON 里:game_type / game_mode / game_alias → 约 0.2 秒 → 再写 type/mode → host_workshop_map <id> 或 changelevel <Map>。从 custom 出来时必须先写数字 CVar,否则 Valve 会继续执行 gamemode_custom.cfg。host_workshop_map 之后 changelevel 回官图时,customgamemode 可能把 type/mode 盖回 3/0(图对了,模式没带回)。1.3.2:图对上而模式不对时立刻开 retry 定时器,写完 type/mode/alias 后一律再发一次切图命令。game_alias 可以把 type/mode 读成 0/1 但不 exec 竞技 cfg;跳过二次 changelevel 会把 custom 回合规则留在官图上。1.3.3:二次切图仍一律发,但 CSS OnMapStart(切图开始约 68ms)只挂 OnServerPreWorldUpdate,等 simulating 且当前图已是目标图再开原来的 delay。实测第一次切图客户端已进服,二次 changelevel 仍 CREATE_SERVER_FAILED (58)。1.3.4:不再二次切图;simulating 之后写 type/mode/alias,再 exec gamemode_<alias>.cfg 和对应 _server.cfg(wingman→gamemode_competitive2v2,retakes→gamemode_retakecasual,training 只有 _server.cfg)。切图倒计时的居中 HTML 每 tick 重绘(与菜单同一通道)。控制台 css_gamemode <Label> 会走同一条 pending 路径。1.3.5:菜单按 Label 前缀(或可选 SubGroup)拆第三层,例如「经典模式 → 休闲 → 荒漠迷城」;切图时序与 css_gamemode 精确匹配不变。本服不要对工坊图用 changelevel workshop/…(同样是 58)。原版预设 ResetBots: true 时会重申 mp_ignore_round_win_conditions 0,避免工坊插件把歼灭判胜负留在官图上。1.3.6:可选 ToggleProfile,切图前通知 PluginToggle。1.3.7:菜单打开时玩家将被固定在原地;已接管的机器人同样受影响。1.3.8:回合开始或重生时菜单会关闭。
需要 .NET 10 SDK:
dotnet build -c Release部署时只需 CS2-Switch-Gamemode.dll、.deps.json、.pdb 与 lang/ 目录。
{ "Version": 3, "RequiredFlag": "@css/changemap", "CountdownSeconds": 5, "ResetBotPopulationAfterSwitch": true, "BotPopulationResetDelaySeconds": 0.75, "MenuCommand": "css_gamemode", "Presets": [ { "Group": "Classic Modes", "Label": "Competitive Dust II", "GameType": 0, "GameMode": 1, "GameAlias": "competitive", "Map": "de_dust2", "ResetBots": true, "After": [] }, { "Group": "Custom", "Label": "Workshop map", "GameType": 3, "GameMode": 0, "GameAlias": "custom", "Map": "workshop/<id>/<bsp>", "ResetBots": false, "After": [] } ] }