diff --git a/.github/workflows/assets.yml b/.github/workflows/assets.yml deleted file mode 100644 index e9280a7a9..000000000 --- a/.github/workflows/assets.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: Assets - -on: - push: - branches: production - - workflow_dispatch: - inputs: - asset_repo: - type: choice - required: false - description: What asset repo to write to - options: - - blitzkit/assets - - blitzkit/assets-preview - - blitzkit/assets-dev - default: blitzkit/assets - - asset_source: - type: string - required: false - description: "Archive URL or Steam to download (example: steam:APP_ID/DEPOT_ID or zip:URL_TO_ZIP)" - default: "steam:444200/444202" - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.asset_repo }} - cancel-in-progress: true - -env: - DISCORD_CLIENT_ID: ${{ secrets.DISCORD_CLIENT_ID }} - DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - VIDEOS_SHEET: ${{ secrets.VIDEOS_SHEET }} - GOOGLE_APPLICATION_CREDENTIALS: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} - WOTB_GLOSSARY: ${{ secrets.WOTB_GLOSSARY }} - WOTB_DLC_CDN: ${{ secrets.WOTB_DLC_CDN }} - PUBLIC_WARGAMING_APPLICATION_ID: ${{ secrets.PUBLIC_WARGAMING_APPLICATION_ID }} - PUBLIC_ASSET_REPO: ${{ github.event.inputs.asset_repo != '' && github.event.inputs.asset_repo || secrets.PUBLIC_ASSET_REPO }} - PUBLIC_ASSET_BRANCH: ${{ secrets.PUBLIC_ASSET_BRANCH }} - WOTB_GLOSSARY_DOMAIN: ${{ secrets.WOTB_GLOSSARY_DOMAIN }} - WOTB_GLOSSARY_PATH: ${{ secrets.WOTB_GLOSSARY_PATH }} - PUBLIC_PROMOTE_OPENTEST: ${{ secrets.PUBLIC_PROMOTE_OPENTEST }} - ALLOW_ROBOTS: true - PUBLIC_GOOGLE_TAG_MANAGER_ID: ${{ secrets.PUBLIC_GOOGLE_TAG_MANAGER_ID }} - PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID: ${{ secrets.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID }} - PUBLIC_PATREON_CLIENT_ID: ${{ secrets.PUBLIC_PATREON_CLIENT_ID }} - PUBLIC_PATREON_REDIRECT_URI: ${{ secrets.PUBLIC_PATREON_REDIRECT_URI }} - PATREON_CLIENT_SECRET: ${{ secrets.PATREON_CLIENT_SECRET }} - PUBLIC_NITROPAY_ID: ${{ secrets.PUBLIC_NITROPAY_ID }} - PUBLIC_DEBUG_MISSING_I18N: false - PUBLIC_DISCORD_SERVER_ID: ${{ secrets.PUBLIC_DISCORD_SERVER_ID }} - PUBLIC_ASSET_FAST_USER: blitzkit - PUBLIC_ASSET_FAST_REPO: assets - PUBLIC_ASSET_BASE: ${{ secrets.PUBLIC_ASSET_BASE }} - GH_TOKEN: ${{ secrets.GH_TOKEN }} - STEAM_USERNAME: ${{ secrets.STEAM_USERNAME }} - STEAM_PASSWORD: ${{ secrets.STEAM_PASSWORD }} - -jobs: - build_assets: - name: Build assets - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - with: - submodules: recursive - token: ${{ secrets.GH_TOKEN }} - - uses: arduino/setup-protoc@v3 - with: - repo-token: ${{ secrets.GH_TOKEN }} - - uses: oven-sh/setup-bun@v2 - - - run: bun run setup - - - name: AppArmor hack - run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns - - - name: Install patches - run: bun run setup:patches --source=${{ github.event.inputs.asset_source || 'steam:444200/444202' }} - - - name: Build assets - run: bun run build:assets --all-targets --source=${{ github.event.inputs.asset_source || 'steam:444200/444202' }} diff --git a/packages/scripts/src/buildAssets/constants.ts b/packages/scripts/src/buildAssets/constants.ts deleted file mode 100644 index 3e5cf4720..000000000 --- a/packages/scripts/src/buildAssets/constants.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { assertSecret } from "@blitzkit/core"; -import { AbstractVFS } from "@blitzkit/core/src/blitzkit/vfs/abstract"; -import { LocalVFS } from "@blitzkit/core/src/blitzkit/vfs/local"; -import { MixedVFS } from "@blitzkit/core/src/blitzkit/vfs/mixed"; -import { SteamVFS } from "@blitzkit/core/src/blitzkit/vfs/steam"; -import { ZipVFS } from "@blitzkit/core/src/blitzkit/vfs/zip"; - -export const PATCHES_ROOT = "../../temp/patches"; - -const steamPattern = /steam:(\d+)\/(\d+)/; -const zipPattern = /zip:(.+)/; -const localePattern = /local:"?(.+)"?/; - -const source = Bun.argv - .find((arg) => arg.startsWith("--source=")) - ?.split("=")[1]; - -let resolved: AbstractVFS; - -if (!source) { - throw new Error("No source specified"); -} else if (steamPattern.test(source)) { - const [, app, depot] = source.match(steamPattern)!.map(Number); - - resolved = await new SteamVFS( - assertSecret(import.meta.env.STEAM_USERNAME), - assertSecret(import.meta.env.STEAM_PASSWORD), - app, - depot, - ).init(); -} else if (zipPattern.test(source)) { - const [, url] = source.match(zipPattern)!; - resolved = await new ZipVFS(url).init(); -} else if (localePattern.test(source)) { - const [, path] = source.match(localePattern)!; - resolved = await new LocalVFS(path).init(); -} else { - throw new Error("No valid source specified"); -} - -resolved = new MixedVFS([new LocalVFS(PATCHES_ROOT), resolved]); - -export const vfs = resolved; diff --git a/packages/scripts/src/buildAssets/equipmentIcons.ts b/packages/scripts/src/buildAssets/equipmentIcons.ts deleted file mode 100644 index 280d9ec87..000000000 --- a/packages/scripts/src/buildAssets/equipmentIcons.ts +++ /dev/null @@ -1,73 +0,0 @@ -import sharp from "sharp"; -import { AssetUploader } from "../core/github/assetUploader"; -import { vfs } from "./constants"; -import { OptionalDevices } from "./definitions"; - - - -export async function equipmentIcons() { - console.log("Building equipment icons..."); - - using uploader = new AssetUploader("equipment icons"); - const optionalDevices = await vfs.xml<{ root: OptionalDevices }>( - `Data/XML/item_defs/vehicles/common/optional_devices.xml`, - ); - const mappings = await vfs.yaml( - `Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`, - ); - const image = sharp( - await vfs.file( - `Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`, - ), - ); - - for (const optionalDeviceKey in optionalDevices.root) { - const optionalDevice = optionalDevices.root[optionalDeviceKey]; - - if (optionalDeviceKey === "nextAvailableId") continue; - - const mapping = mappings.StyleSheets.find((mapping) => - mapping.selectors.includes( - `.optional_device_item.${optionalDevice.icon} #Img`, - ), - ); - - if (!mapping) { - console.warn(`No mapping found for ${optionalDevice.icon}; skipping...`); - continue; - } - - const configPathRaw = mapping.properties["bg-sprite"]; - const configPath = configPathRaw.replace("~res:/", ""); - - if (configPath.startsWith("Gfx/Lobby")) { - const configPathWebp = configPath.replace(".txt", ""); - const image = sharp(await vfs.file(`Data/${configPathWebp}.packed.webp`)); - const content = await image.trim().toBuffer(); - - await uploader.add({ - path: `icons/equipment/${optionalDevice.id}.webp`, - content, - }); - } else { - const config = await vfs.text(`Data/${configPath}`); - const sizes = config.split("\n")[4].split(" ").map(Number); - const content = await image - .clone() - .extract({ - left: sizes[0], - top: sizes[1], - width: sizes[2], - height: sizes[3], - }) - .toBuffer(); - - await uploader.add({ - path: `icons/equipment/${optionalDevice.id}.webp`, - content, - }); - } - } - - await uploader.flush(); -} diff --git a/packages/scripts/src/buildAssets/glossary.ts b/packages/scripts/src/buildAssets/glossary.ts deleted file mode 100644 index ed3b83cfd..000000000 --- a/packages/scripts/src/buildAssets/glossary.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { Avatar, fetchGlossary, Gallery } from "@blitzkit/core"; -import locales from "@blitzkit/i18n/locales.json"; -import { extname } from "path"; -import ProgressBar from "progress"; -import { AssetUploader } from "../core/github/assetUploader"; - -export async function glossary() { - console.log("Building glossary..."); - - using uploader = new AssetUploader("glossary"); - const avatars: Record = {}; - - await Promise.all( - locales.supported.map(async (supported) => { - const glossary = await fetchGlossary(supported.blitz ?? supported.locale); - - console.log( - `Found ${Object.keys(glossary).length} things for ${supported.locale}`, - ); - - for (const key in glossary) { - const glossaryEntry = glossary[key]; - - if ( - !key.startsWith("avatar") || - key.endsWith("_part") || - glossaryEntry.image_url === null - ) { - continue; - } - - if (key in avatars) { - avatars[key].avatar.name!.locales[supported.locale] = - glossaryEntry.title; - } else { - const extension = extname(glossaryEntry.image_url); - - avatars[key] = { - url: glossaryEntry.image_url, - avatar: { - id: key, - name: { locales: { [supported.locale]: glossaryEntry.title } }, - extension, - }, - }; - } - } - }), - ); - - const gallery: Gallery = { avatars: [] }; - const totalCount = Object.keys(avatars).length; - - const bar = new ProgressBar( - `Fetching avatars of ${totalCount} things :bar`, - totalCount, - ); - - await Promise.all( - Object.entries(avatars).map(async ([key, { url, avatar }]) => { - if (!(locales.default in avatars[key].avatar.name!.locales)) { - console.warn(`Avatar ${key} has no ${locales.default} name`); - bar.tick(); - - return; - } - - gallery.avatars.push(avatar); - - const content = await fetch(url) - .then((response) => response.arrayBuffer()) - .then((buffer) => new Uint8Array(buffer)); - - await uploader.add({ - path: `gallery/avatars/${key}${avatar.extension}`, - content, - }); - - bar.tick(); - }), - ); - - await uploader.add({ - path: "definitions/gallery.pb", - content: Gallery.encode(gallery).finish(), - }); - - await uploader.flush(); -} diff --git a/packages/scripts/src/buildAssets/index.ts b/packages/scripts/src/buildAssets/index.ts deleted file mode 100644 index 495765e0c..000000000 --- a/packages/scripts/src/buildAssets/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { config } from "dotenv"; -import { argv } from "process"; -import { boosterIcons } from "./boosterIcons"; -import { camouflageIcons } from "./camouflageIcons"; -import { vfs } from "./constants"; -import { consumableProvisionIcons } from "./consumableProvisionIcons"; -import { currencies } from "./currencies"; -import { definitions } from "./definitions"; -import { equipmentIcons } from "./equipmentIcons"; -import { flags } from "./flags"; -import { gameModeBanners } from "./gameModeBanners"; -import { glossary } from "./glossary"; -import { migration } from "./migration"; -import { moduleIcons } from "./moduleIcons"; -import { popularTanks } from "./popularTanks"; -import { shellIcons } from "./shellIcons"; -import { skillIcons } from "./skillIcons"; -import { tankArmors } from "./tankArmors"; -import { tankIcons } from "./tankIcons"; -import { tankModels } from "./tankModels"; - -config(); - -const allTargets = argv.includes("--all-targets"); -const targets = argv - .find((argument) => argument.startsWith("--target")) - ?.split("=")[1] - .split(","); - -if (!targets && !allTargets) throw new Error("No target(s) specified"); - -const methods = [ - definitions, - tankIcons, - tankModels, - tankArmors, - gameModeBanners, - currencies, - skillIcons, - flags, - shellIcons, - moduleIcons, - equipmentIcons, - consumableProvisionIcons, - camouflageIcons, - boosterIcons, - glossary, - migration, - popularTanks, -]; - -for (const method of methods) { - if (allTargets || targets?.includes(method.name)) { - try { - await method(); - } catch (error) { - console.warn(`Failed method ${method.name}; skipping...`); - console.error(error); - } - } -} - -vfs.dispose(); -process.exit(0); diff --git a/packages/scripts/src/buildAssets/migration.ts b/packages/scripts/src/buildAssets/migration.ts deleted file mode 100644 index ca806f961..000000000 --- a/packages/scripts/src/buildAssets/migration.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { toUniqueId } from "@blitzkit/core"; -import { AssetUploader } from "../core/github/assetUploader"; -import { vfs } from "./constants"; -import { VehicleDefinitionList } from "./definitions"; - -export async function migration() { - console.log("Building migration map..."); - - using uploader = new AssetUploader("migration map"); - const map: Record = {}; - const nations = await vfs - .dir(`Data/XML/item_defs/vehicles`) - .then((files) => files.filter((nation) => nation !== "common")); - - for (const nation of nations) { - const tankList = await vfs.xml<{ root: VehicleDefinitionList }>( - `Data/XML/item_defs/vehicles/${nation}/list.xml`, - ); - - for (const tankKey in tankList.root) { - const tank = tankList.root[tankKey]; - const tankId = toUniqueId(nation, tank.id); - const davaKey = `${nation}:${tankKey}`; - - map[davaKey] = tankId; - } - } - - await uploader.add({ - path: "definitions/migration.json", - content: new TextEncoder().encode(JSON.stringify(map)), - }); - await uploader.flush(); -} diff --git a/packages/scripts/src/buildAssets/popularTanks.ts b/packages/scripts/src/buildAssets/popularTanks.ts deleted file mode 100644 index 9d281d4d2..000000000 --- a/packages/scripts/src/buildAssets/popularTanks.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { - assertSecret, - fetchTankDefinitions, - PopularTanks, -} from "@blitzkit/core"; -import { google } from "googleapis"; -import { AssetUploader } from "../core/github/assetUploader"; - -export async function popularTanks() { - using uploader = new AssetUploader("popular tanks"); - const tankDefinitions = await fetchTankDefinitions(); - - const auth = await google.auth.getClient({ - keyFile: import.meta.env.GOOGLE_APPLICATION_CREDENTIALS, - scopes: ["https://www.googleapis.com/auth/analytics.readonly"], - }); - const analytics = google.analyticsdata({ version: "v1beta", auth }); - const report = await analytics.properties.runReport({ - property: `properties/${assertSecret( - import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID, - )}`, - requestBody: { - dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], - dimensions: [{ name: "pagePath" }], - metrics: [{ name: "screenPageViews" }], - orderBys: [{ metric: { metricName: "screenPageViews" }, desc: true }], - dimensionFilter: { - filter: { - fieldName: "pagePath", - stringFilter: { - matchType: "BEGINS_WITH", - value: "/tanks/", - }, - }, - }, - }, - }); - - if (!report.data.rows) { - throw new Error("No rows in report"); - } - - const tanks = report.data.rows - .filter( - (row) => - row.dimensionValues && - row.dimensionValues[0].value && - row.dimensionValues[0].value !== "/tanks/" && - row.metricValues && - row.metricValues[0].value, - ) - .map((row) => ({ - id: Number( - row.dimensionValues![0].value!.match( - /\/tools\/tankopedia\/(\d+)\/?/, - )?.[1], - ), - views: Number(row.metricValues![0].value!), - })) - .filter((row) => { - if (!(row.id in tankDefinitions.tanks)) { - console.warn("Skipping unknown popular tank", row.id); - return false; - } - - return row.id in tankDefinitions.tanks; - }) - .slice(0, 8) - .map(({ id, views }) => ({ id, views })); - const popularTanks: PopularTanks = { tanks }; - - await uploader.add({ - path: "definitions/popular-tanks.pb", - content: PopularTanks.encode(popularTanks).finish(), - }); - await uploader.flush(); -} diff --git a/packages/scripts/src/buildAssets/tankIcons.ts b/packages/scripts/src/buildAssets/tankIcons.ts deleted file mode 100644 index 1534e800b..000000000 --- a/packages/scripts/src/buildAssets/tankIcons.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { NATION_IDS } from "@blitzkit/core"; -import sharp from "sharp"; -import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect"; -import { AssetUploader } from "../core/github/assetUploader"; -import { vfs } from "./constants"; -import type { VehicleDefinitionList } from "./definitions"; - - -export async function tankIcons() { - console.log("Building tank icons..."); - - using uploader = new AssetUploader("tank icons"); - const nations = await vfs - .dir(`Data/XML/item_defs/vehicles`) - .then((files) => files.filter((nation) => nation !== "common")); - - for (const nation of nations) { - const tanks = await vfs.xml<{ root: VehicleDefinitionList }>( - `Data/XML/item_defs/vehicles/${nation}/list.xml`, - ); - - for (const tankKey in tanks.root) { - const tank = tanks.root[tankKey]; - - if (tankKey.includes("tutorial_bot")) continue; - - const nationVehicleId = tank.id; - const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; - - const parameters = await vfs.yaml( - `Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, - ); - const smallPath = `Data/${parameters.resourcesPath.smallIconPath - .replace(/~res:\//, "") - .replace(/\..+/, "")}.packed.webp`; - const bigPath = `Data/${parameters.resourcesPath.bigIconPath - .replace(/~res:\//, "") - .replace(/\..+/, "")}.packed.webp`; - const big = await extractPackedTankIcon( - await vfs.file(bigPath), - `tank ${tankKey} big`, - ); - const small = await extractPackedTankIcon( - await vfs.file(smallPath), - `tank ${tankKey} small`, - ); - - if (big) { - await uploader.add({ - content: big, - path: `icons/tanks/big/${id}.webp`, - }); - } - if (small) { - await uploader.add({ - content: small, - path: `icons/tanks/small/${id}.webp`, - }); - } - } - } - - await uploader.flush(); -} diff --git a/packages/scripts/src/buildAssets/tankModels.ts b/packages/scripts/src/buildAssets/tankModels.ts deleted file mode 100644 index e0f609ef7..000000000 --- a/packages/scripts/src/buildAssets/tankModels.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { toUniqueId } from "@blitzkit/core"; -import { NodeIO } from "@gltf-transform/core"; -import { ALL_EXTENSIONS } from "@gltf-transform/extensions"; -import { extractModel } from "../core/blitz/extractModel"; -import { AssetUploader } from "../core/github/assetUploader"; -import { vfs } from "./constants"; -import { VehicleDefinitionList } from "./definitions"; -import { TankParameters } from "./tankIcons"; - -export async function tankModels() { - console.log("Building tank models..."); - - using uploader = new AssetUploader("tank models"); - const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS); - const nations = await vfs - .dir(`Data/XML/item_defs/vehicles`) - .then((files) => files.filter((nation) => nation !== "common")); - - for (const nationIndex in nations) { - const nation = nations[nationIndex]; - const tanks = await vfs.xml<{ root: VehicleDefinitionList }>( - `Data/XML/item_defs/vehicles/${nation}/list.xml`, - ); - const entries = Object.entries(tanks.root); - - for (const [tankKey, tank] of entries) { - if (tankKey.includes("tutorial_bot")) continue; - - const id = toUniqueId(nation, tank.id); - const parameters = await vfs.yaml( - `Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`, - ); - const model = await extractModel( - vfs, - parameters.resourcesPath.blitzModelPath.replace(/\.sc2$/, ""), - ); - const content = Buffer.from(await nodeIO.writeBinary(model)); - - await uploader.add({ path: `3d/tanks/models/${id}.glb`, content }); - } - } - - await uploader.flush(); -}