diff --git a/src/dedicated-servers/dedicated-servers.service.spec.ts b/src/dedicated-servers/dedicated-servers.service.spec.ts index e94e6652..02c12d80 100644 --- a/src/dedicated-servers/dedicated-servers.service.spec.ts +++ b/src/dedicated-servers/dedicated-servers.service.spec.ts @@ -723,7 +723,7 @@ describe("DedicatedServersService.setupDedicatedServer", () => { disableServerGuidelines: false, }; - const setup = async (type: string) => { + const deploy = async (type: string, game = "cs2") => { const createNamespacedDeployment = jest.fn().mockResolvedValue({}); const gameModes = { resolveForServer: jest.fn().mockResolvedValue(pluginsOnly), @@ -745,7 +745,7 @@ describe("DedicatedServersService.setupDedicatedServer", () => { type, port: 27015, tv_port: 27020, - game: "cs2", + game, max_players: 10, api_password: "api", rcon_password: "rcon", @@ -781,6 +781,12 @@ describe("DedicatedServersService.setupDedicatedServer", () => { const env: Array<{ name: string; value: string }> = createNamespacedDeployment.mock.calls[0][0].body.spec.template.spec .containers[0].env; + + return env; + }; + + const setup = async (type: string) => { + const env = await deploy(type); const pluginConfigs = env.find((entry) => entry.name === "PLUGIN_CONFIGS"); return pluginConfigs @@ -799,4 +805,24 @@ describe("DedicatedServersService.setupDedicatedServer", () => { it("leaves them to the match on a Ranked server", async () => { expect(await setup("Ranked")).toEqual({}); }); + + const launchParams = async (type: string, game?: string) => + (await deploy(type, game)) + .find((entry) => entry.name === "EXTRA_GAME_PARAMS") + .value.split(" "); + + it.each(["Casual", "Ranked", "Custom"])( + "boots a CS2 %s server with workshop command filtering off", + async (type) => { + expect(await launchParams(type)).toContain( + "-disable_workshop_command_filtering", + ); + }, + ); + + it("leaves the CS2-only flag off a CS:GO server", async () => { + expect(await launchParams("Casual", "csgo")).not.toContain( + "-disable_workshop_command_filtering", + ); + }); }); diff --git a/src/dedicated-servers/dedicated-servers.service.ts b/src/dedicated-servers/dedicated-servers.service.ts index c4e690d2..bb8720ab 100644 --- a/src/dedicated-servers/dedicated-servers.service.ts +++ b/src/dedicated-servers/dedicated-servers.service.ts @@ -352,6 +352,12 @@ export class DedicatedServersService { { name: "EXTRA_GAME_PARAMS", value: [ + // CS2 (since its January 2026 update) drops + // "workshop-disallowed" cvars from every cfg exec'd + // on a workshop map without it. + server.game === "csgo" + ? null + : "-disable_workshop_command_filtering", `-maxplayers ${server.type === "Ranked" ? 16 : server.max_players}`, `+map ${startMap && !startMap.workshop_map_id ? startMap.name : "de_dust2"}`, ...DedicatedServersService.launchMode( diff --git a/src/matches/match-assistant/match-assistant.on-demand-teardown.spec.ts b/src/matches/match-assistant/match-assistant.on-demand-teardown.spec.ts index 82e4ebdd..54fe6373 100644 --- a/src/matches/match-assistant/match-assistant.on-demand-teardown.spec.ts +++ b/src/matches/match-assistant/match-assistant.on-demand-teardown.spec.ts @@ -657,6 +657,20 @@ describe("MatchAssistantService — on-demand server teardown", () => { }); }); + it("boots CS2 with workshop command filtering off", async () => { + await (service as any).assignOnDemandServer("match-1"); + + const [container] = + createNamespacedJob.mock.calls[0][0].body.spec.template.spec.containers; + const params = container.env.find( + (entry: { name: string }) => entry.name === "EXTRA_GAME_PARAMS", + ); + + expect(params.value.split(" ")).toContain( + "-disable_workshop_command_filtering", + ); + }); + it("holds the pool lock past a teardown but not past the retries", async () => { await (service as any).assignOnDemandServer("match-1"); diff --git a/src/matches/match-assistant/match-assistant.service.ts b/src/matches/match-assistant/match-assistant.service.ts index 51c57261..b1c0c2c7 100644 --- a/src/matches/match-assistant/match-assistant.service.ts +++ b/src/matches/match-assistant/match-assistant.service.ts @@ -1257,7 +1257,10 @@ export class MatchAssistantService { }, { name: "EXTRA_GAME_PARAMS", - value: `-maxplayers ${match.max_players_per_lineup * 2 + 3} ${map.workshop_map_id ? `+map de_inferno` : `+map ${map.name}`} +game_type 0 +game_mode ${MatchAssistantService.getGameMode(match.options?.type)} +sv_password ${match.password}${gameMode?.extraGameParams ? ` ${gameMode.extraGameParams}` : ""}`, + // CS2 (since its January 2026 update) drops + // "workshop-disallowed" cvars from every cfg + // exec'd on a workshop map without it. + value: `-disable_workshop_command_filtering -maxplayers ${match.max_players_per_lineup * 2 + 3} ${map.workshop_map_id ? `+map de_inferno` : `+map ${map.name}`} +game_type 0 +game_mode ${MatchAssistantService.getGameMode(match.options?.type)} +sv_password ${match.password}${gameMode?.extraGameParams ? ` ${gameMode.extraGameParams}` : ""}`, }, { name: "SERVER_ID", value: server.id }, { name: "MATCH_ID", value: matchId },