Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions hasura/enums/maps.sql
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ insert into maps (
"workshop_map_id",
"poster",
"patch",
"label"
"label",
"seeded"
)
SELECT
name,
Expand All @@ -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.
Expand All @@ -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
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE public.maps DROP COLUMN IF EXISTS seeded;
4 changes: 4 additions & 0 deletions hasura/migrations/default/1890000001090_maps_seeded/up.sql
Original file line number Diff line number Diff line change
@@ -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;
19 changes: 14 additions & 5 deletions src/map-assets/map-assets.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>;
workshop: Array<MapAssetsWorkshopMap>;
}> {
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<string, string>();
Expand Down
85 changes: 85 additions & 0 deletions test/map-asset-builds.spec.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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<string> = 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(
Expand Down
Loading