From 24d620c3a2246f603aaf74ed0ecdaf19e792e2ba Mon Sep 17 00:00:00 2001 From: Luke Policinski Date: Fri, 16 Oct 2026 12:51:15 -0400 Subject: [PATCH] feature: map assets are built for every seeded map and any other map some mode enables, so prophunt overlays are skipped --- hasura/enums/maps.sql | 16 +++- .../1890000001090_maps_seeded/down.sql | 1 + .../default/1890000001090_maps_seeded/up.sql | 4 + src/map-assets/map-assets.service.ts | 19 +++-- test/map-asset-builds.spec.ts | 85 +++++++++++++++++++ 5 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 hasura/migrations/default/1890000001090_maps_seeded/down.sql create mode 100644 hasura/migrations/default/1890000001090_maps_seeded/up.sql diff --git a/hasura/enums/maps.sql b/hasura/enums/maps.sql index b34f1d63..28a39963 100644 --- a/hasura/enums/maps.sql +++ b/hasura/enums/maps.sql @@ -243,7 +243,8 @@ insert into maps ( "workshop_map_id", "poster", "patch", - "label" + "label", + "seeded" ) SELECT name, @@ -252,14 +253,22 @@ SELECT workshop_map_id, poster, patch, - label + label, + true FROM all_maps on conflict("name", "type") do update set "active_pool" = EXCLUDED."active_pool", "workshop_map_id" = EXCLUDED."workshop_map_id", "poster" = EXCLUDED."poster", "patch" = EXCLUDED."patch", - "label" = EXCLUDED."label" + "label" = EXCLUDED."label", + "seeded" = true +), +unseeded AS ( +UPDATE maps +SET seeded = false +WHERE seeded + AND name NOT IN (SELECT name FROM map_data) ) -- A map's details belong to the map, not one mode, so the mode rows the seed does not list (an -- admin's, or one it retired) carry them too; only a retired mode is switched off. @@ -268,6 +277,7 @@ SET workshop_map_id = md.workshop_map_id, poster = md.poster, patch = md.patch, label = md.label, + seeded = true, enabled = m.enabled AND NOT EXISTS ( SELECT 1 FROM retired_map_types r WHERE r.name = m.name AND r.type = m.type ) diff --git a/hasura/migrations/default/1890000001090_maps_seeded/down.sql b/hasura/migrations/default/1890000001090_maps_seeded/down.sql new file mode 100644 index 00000000..c6bfb98c --- /dev/null +++ b/hasura/migrations/default/1890000001090_maps_seeded/down.sql @@ -0,0 +1 @@ +ALTER TABLE public.maps DROP COLUMN IF EXISTS seeded; diff --git a/hasura/migrations/default/1890000001090_maps_seeded/up.sql b/hasura/migrations/default/1890000001090_maps_seeded/up.sql new file mode 100644 index 00000000..5855c75c --- /dev/null +++ b/hasura/migrations/default/1890000001090_maps_seeded/up.sql @@ -0,0 +1,4 @@ +-- Whether hasura/enums/maps.sql ships the map. Every instance seeds the same +-- maps, and their map assets are built once on 5stack.gg for all of them, so a +-- seeded map is built whatever this instance's own modes enable. +ALTER TABLE public.maps ADD COLUMN IF NOT EXISTS seeded boolean NOT NULL DEFAULT false; diff --git a/src/map-assets/map-assets.service.ts b/src/map-assets/map-assets.service.ts index 43566798..0936328c 100644 --- a/src/map-assets/map-assets.service.ts +++ b/src/map-assets/map-assets.service.ts @@ -980,18 +980,27 @@ export class MapAssetsService { }); } - // Only maps with a row are built and published. Every instance seeds the - // same maps, so this instance's table speaks for all of them. A row with a + // Every instance seeds the same maps and reads the assets built here, so a + // seeded map is always built, whatever this instance's modes enable. Any + // other map is built only while some mode enables it: one disabled in every + // mode, like a prophunt overlay that only a public server's rotation loads, + // is never played as a match and so never rendered. A row with a // workshop_map_id is built from the node's workshop downloads when the - // install does not carry it; one the node never downloaded keeps whatever - // is already published. + // install does not carry it; one the node never downloaded keeps whatever is + // already published. private async knownMaps(): Promise<{ names: Array; workshop: Array; }> { const rows = await this.postgres.query< Array<{ name: string; workshop_map_id: string | null }> - >(`SELECT DISTINCT name, workshop_map_id FROM public.maps ORDER BY name`); + >( + `SELECT name, workshop_map_id + FROM public.maps + WHERE seeded + OR (enabled AND deleted_at IS NULL) + ORDER BY name, enabled DESC`, + ); const names = [...new Set(rows.map(({ name }) => name))]; const workshop = new Map(); diff --git a/test/map-asset-builds.spec.ts b/test/map-asset-builds.spec.ts index ef0d0cf4..2a2bb4fa 100644 --- a/test/map-asset-builds.spec.ts +++ b/test/map-asset-builds.spec.ts @@ -1,3 +1,5 @@ +import { readFileSync } from "fs"; +import { join } from "path"; import { Logger } from "@nestjs/common"; import { PostgresService } from "./../src/postgres/postgres.service"; import { MapAssetsService } from "./../src/map-assets/map-assets.service"; @@ -206,6 +208,89 @@ describe("map asset builds (SQL-driven)", () => { expect(queue.add).not.toHaveBeenCalled(); }); + it("builds every seeded map, and any other only while some mode enables it", async () => { + await postgres.query( + `INSERT INTO maps (name, type, enabled, active_pool, workshop_map_id, deleted_at, seeded) + VALUES ('de_assets_playable', 'Competitive', true, false, NULL, NULL, false), + ('de_assets_one_mode', 'Competitive', false, false, '9101', NULL, true), + ('de_assets_one_mode', 'Duel', true, false, '9100', NULL, true), + ('9200', 'Competitive', false, false, '9200', NULL, false), + ('de_assets_deleted', 'Competitive', true, false, '9300', now(), false)`, + ); + await postgres.query( + `UPDATE maps SET enabled = false WHERE name = 'cs_office'`, + ); + const mapAssets = service(); + + try { + await (mapAssets as any).startJob("node-a", "25537370", false); + } finally { + await postgres.query( + `DELETE FROM maps WHERE name IN ('de_assets_playable', 'de_assets_one_mode', '9200', 'de_assets_deleted')`, + ); + await postgres.query( + `UPDATE maps SET enabled = true WHERE name = 'cs_office'`, + ); + } + + const [[{ body }]] = (mapAssets as any).batchApi.createNamespacedJob.mock + .calls; + const args: Array = body.spec.template.spec.containers[0].args; + const only = args[args.indexOf("--only-maps") + 1].split(","); + const workshop = args[args.indexOf("--workshop-maps") + 1].split(","); + expect(only).toEqual( + expect.arrayContaining([ + "cs_office", + "de_assets_playable", + "de_assets_one_mode", + ]), + ); + expect(only).not.toContain("9200"); + expect(only).not.toContain("de_assets_deleted"); + expect(workshop).toContain("de_assets_one_mode=9100"); + expect(workshop).not.toContain("de_assets_one_mode=9101"); + expect(workshop).not.toContain("9200=9200"); + expect(workshop).not.toContain("de_assets_deleted=9300"); + }); + + it("marks every mode of the maps the seed ships, and only those", async () => { + // As the migration leaves every existing row on upgrade. + await postgres.query(`UPDATE maps SET seeded = false`); + await postgres.query( + `INSERT INTO maps (name, type, active_pool, seeded) + VALUES ('de_mirage', 'Duel', false, false), + ('de_assets_retired', 'Competitive', false, true), + ('de_assets_custom', 'Competitive', false, false)`, + ); + + try { + await postgres.query( + readFileSync(join(__dirname, "../hasura/enums/maps.sql"), "utf8"), + ); + const seeded = await postgres.query< + Array<{ name: string; type: string; seeded: boolean }> + >( + `SELECT name, type, seeded FROM maps + WHERE (name = 'de_mirage' AND type IN ('Competitive', 'Duel')) + OR name IN ('cs_office', 'de_assets_retired', 'de_assets_custom') + ORDER BY name, type`, + ); + expect(seeded).toEqual([ + { name: "cs_office", type: "Competitive", seeded: true }, + { name: "de_assets_custom", type: "Competitive", seeded: false }, + { name: "de_assets_retired", type: "Competitive", seeded: false }, + { name: "de_mirage", type: "Competitive", seeded: true }, + { name: "de_mirage", type: "Duel", seeded: true }, + ]); + } finally { + await postgres.query( + `DELETE FROM maps + WHERE (name = 'de_mirage' AND type = 'Duel') + OR name IN ('de_assets_retired', 'de_assets_custom')`, + ); + } + }); + it("records a published build and the manifest revision it came from", async () => { const maps = { de_mirage: { tri: "25000000/de_mirage.tri.gz" } }; await postgres.query(