diff --git a/packages/scripts/src/config/wgcClient.ts b/packages/scripts/src/config/wgcClient.ts new file mode 100644 index 000000000..dd8062fde --- /dev/null +++ b/packages/scripts/src/config/wgcClient.ts @@ -0,0 +1,4 @@ +export const WGC_CLIENT_GUID = "WOTB.WW.PRODUCTION"; +export const WGC_CLIENT_URL = "https://wgus-eu.wargaming.net"; +export const WGC_METADATA_PROTOCOL_VERSION = "7.10"; +export const WGC_PATCHES_CHAIN_PROTOCOL_VERSION = "1.11"; diff --git a/packages/scripts/src/downloadClient/index.ts b/packages/scripts/src/downloadClient/index.ts index 18787d63f..44b5531bd 100644 --- a/packages/scripts/src/downloadClient/index.ts +++ b/packages/scripts/src/downloadClient/index.ts @@ -1,31 +1,38 @@ import { assertSecret } from "@blitzkit/core"; import { mkdir, rm } from "node:fs/promises"; import { downloadSteamClient } from "./steam"; +import { downloadWGCClient } from "./wgc"; import { downloadWGPKGClient } from "./wgpkg"; import { downloadZipClient } from "./zip"; export const DEPOT_DOWNLOADER_RETRY_TIME = 30_000; -const provider = assertSecret(import.meta.env.CLIENT_PROVIDER); -export const source = assertSecret(import.meta.env.CLIENT_SOURCE); +const provider = import.meta.env.CLIENT_PROVIDER; +export const source = import.meta.env.CLIENT_SOURCE; export const dir = assertSecret(import.meta.env.CLIENT_DIR); await rm(dir, { recursive: true, force: true }); await mkdir(dir, { recursive: true }); switch (provider) { + // steam, identical to wgc case "steam": await downloadSteamClient(); break; + // from zip, usually has a root directory for opentest case "zip": await downloadZipClient(); break; + // effectively the same as wgc, but direct for preview case "wgpkg": await downloadWGPKGClient(); break; default: - throw new Error(`Unknown provider: ${provider}`); + console.log("Unknown client provider specified, using latest wgc client"); + case "wgc": + await downloadWGCClient(); + break; } diff --git a/packages/scripts/src/downloadClient/wgc/index.ts b/packages/scripts/src/downloadClient/wgc/index.ts new file mode 100644 index 000000000..6b1637c79 --- /dev/null +++ b/packages/scripts/src/downloadClient/wgc/index.ts @@ -0,0 +1,59 @@ +import locales from "@blitzkit/i18n/locales.json"; +import { XMLParser } from "fast-xml-parser"; +import { + WGC_CLIENT_GUID, + WGC_CLIENT_URL, + WGC_METADATA_PROTOCOL_VERSION, + WGC_PATCHES_CHAIN_PROTOCOL_VERSION, +} from "../../config/wgcClient"; +import { ClientMetadata, PatchesChain } from "./types"; + +const parser = new XMLParser(); + +export async function downloadWGCClient() { + const metadataUrl = new URL("api/v1/metadata", WGC_CLIENT_URL); + + metadataUrl.searchParams.set("guid", WGC_CLIENT_GUID); + metadataUrl.searchParams.set("chain_id", "unknown"); + metadataUrl.searchParams.set( + "protocol_version", + WGC_METADATA_PROTOCOL_VERSION, + ); + + const metadata = await fetch(`${metadataUrl}`) + .then((response) => response.text()) + .then((text) => parser.parse(text) as Promise); + + const patchesChainUrl = new URL("api/v1/patches_chain", WGC_CLIENT_URL); + + patchesChainUrl.searchParams.set( + "protocol_version", + WGC_PATCHES_CHAIN_PROTOCOL_VERSION, + ); + patchesChainUrl.searchParams.set("client_type", "full"); + patchesChainUrl.searchParams.set("lang", locales.default); + patchesChainUrl.searchParams.set( + "metadata_version", + `${metadata.protocol.version}`, + ); + patchesChainUrl.searchParams.set( + "metadata_protocol_version", + WGC_METADATA_PROTOCOL_VERSION, + ); + patchesChainUrl.searchParams.set( + "chain_id", + metadata.protocol.predefined_section.chain_id, + ); + patchesChainUrl.searchParams.set("game_installation", "true"); + patchesChainUrl.searchParams.set("game_id", WGC_CLIENT_GUID); + patchesChainUrl.searchParams.set("client_current_version", "0"); + + const patchesChain = await fetch(`${patchesChainUrl}`) + .then((response) => response.text()) + .then((text) => parser.parse(text) as Promise); + + for (const file of patchesChain.protocol.patches_chain.patch.files.file) { + const fileUrl = new URL(file.name, patchesChain.protocol.web_seeds.url); + console.log(`${fileUrl}`); + } +} diff --git a/packages/scripts/src/downloadClient/wgc/types.ts b/packages/scripts/src/downloadClient/wgc/types.ts new file mode 100644 index 000000000..c5ba3b367 --- /dev/null +++ b/packages/scripts/src/downloadClient/wgc/types.ts @@ -0,0 +1,87 @@ +export interface ClientMetadata { + protocol: { + version: number; + predefined_section: PredefinedSection; + generated_section: GeneratedSection; + }; +} + +interface GeneratedSection { + launch_forbidden: string; + stop_supporting: string; +} + +interface PredefinedSection { + app_id: string; + chain_id: string; + supported_languages: string; + default_language: string; + name: string; + fs_name: string; + shortcut_name: string; + executables: { executable: string }; + keep_patches_interval: number; + client_types: { client_type: ClientType }; + login_enabled: boolean; + realms_settings: RealmsSettings; + localization: Localization; +} + +interface ClientType { + initial_app_type: string; + final_app_type: string; + min_supported_os: string; + client_parts: { client_part: string }; +} + +interface Localization { + item: { text: string[] }; +} + +interface RealmsSettings { + instances: { instance: Instance[] }; +} + +interface Instance { + game_id: string; + title_id: string; + platform_realm_id: string; + options: { option: string }; + content_service_url: string; +} + +export interface PatchesChain { + protocol: { + parts_info: { part: Part }; + patches_chain: { patch: Patch }; + delay_preload: string; + web_seeds: { url: string }; + meta_need_update: string; + version_name: string; + }; +} + +interface Part { + name: string; + version: number; + status: string; +} + +interface Patch { + files: { file: File[] }; + torrent: Torrent; + part: string; + version_from: number; + version_to: string; +} + +interface File { + name: string; + size: number; + unpacked_size: number; +} + +interface Torrent { + hash: string; + urls: { url: string }; +}