${errorMsg}
`; } else { alert(errorMsg); } return; } this.viewportAdapter.rootElement = this.rootEl; this.applyRootLayout(this.viewport.value); if (this.splashScreenUpdateCallback) { this.splashScreenUpdateCallback({ width: this.viewport.value.width, height: this.viewport.value.height, parentElement: this.rootEl, loadingText: 'Initializing...' }); } try { await this.loadConfig(); this.initializePreferredViewportSize(); this.updateViewportSize(); } catch (e) { console.error("CRITICAL: Application.loadConfig() failed. See previous errors.", e); if (this.rootEl) this.rootEl.innerHTML = "Failed to load critical application configuration. Please check console.
"; return; } const locale = this.config.defaultLocale; if (this.splashScreenUpdateCallback) { this.splashScreenUpdateCallback({ width: this.viewport.value.width, height: this.viewport.value.height, parentElement: this.rootEl, loadingText: 'Loading translations...' }); } try { await this.loadTranslations(); } catch (e) { console.error(`Missing translation ${locale}.`, e); return; } if (this.splashScreenUpdateCallback) { this.splashScreenUpdateCallback({ width: this.viewport.value.width, height: this.viewport.value.height, parentElement: this.rootEl, loadingText: this.strings.get("gui:loadingex"), copyrightText: this.strings.get("txt_copyright") + "\n" + this.strings.get("gui:wwbrand"), disclaimerText: this.strings.get("ts:disclaimer") }); } try { this.checkGlobalLibs(); } catch (e: any) { console.error("Global library check failed:", e); const errorMsg = this.strings.get("TS:DownloadFailed"); const errorBox = new BasicErrorBoxApi(this.viewport, this.strings, this.rootEl!); await errorBox.show(errorMsg, true); return; } this.runtimeVars = new MockConsoleVars(); this.bindPerformanceRuntimeVars(); MockDevToolsApi.registerVar("freecamera", this.runtimeVars.freeCamera); await this.initLogging(); this.fullScreen.init(); this.fullScreen.onChange.subscribe((isFS: boolean) => { this.onFullScreenChange(isFS); }); this.runtimeVars.forceResolution.onChange.subscribe(this.handleForceResolutionChange); if (typeof window !== 'undefined') { window.addEventListener('resize', this.handleViewportEnvironmentChange); window.addEventListener('orientationchange', this.handleViewportEnvironmentChange); window.visualViewport?.addEventListener('resize', this.handleViewportEnvironmentChange); this.generalOptions.graphics.resolution.onChange.subscribe(this.handlePreferredResolutionChange); this.updateViewportSize(); } this.loadGpuBenchmarkData() .then(gpuData => this.gpuTier = gpuData) .catch(e => this.sentry?.captureException(e)); this.fsAccessLib = browserFileSystemAccess; const urlParams = new URLSearchParams(window.location.search); const modName = urlParams.get('mod'); let gameResConfig = this.loadGameResConfig(this.localPrefs); try { const gameRes = new GameRes(this.getVersion(), modName || undefined, this.fsAccessLib, this.localPrefs, this.strings, this.rootEl, this.createSplashScreenInterface(), this.viewportAdapter, this.config, "res/", this.sentry); const { configToPersist, cdnResLoader } = await gameRes.init(gameResConfig, (error, strings) => this.handleGameResLoadError(error, strings), (error, strings) => this.handleGameResImportError(error, strings)); try { const vfsAny: any = (Engine as any).vfs; if (vfsAny?.debugListFileOwners) { vfsAny.debugListFileOwners('rules.ini'); vfsAny.debugListFileOwners('art.ini'); vfsAny.debugListFileOwners('rulescd.ini'); vfsAny.debugListFileOwners('artcd.ini'); } try { const rulesFile = (Engine as any).vfs.openFile('rules.ini'); const rulesHead = rulesFile.readAsString().split(/\r?\n/).slice(0, 40); console.log('[Diag] rules.ini head (first 40 lines):', rulesHead); } catch (e) { console.warn('[Diag] Failed to read rules.ini head:', e); } try { const rulesCdFile = (Engine as any).vfs.openFile('rulescd.ini'); const rulesCdHead = rulesCdFile.readAsString().split(/\r?\n/).slice(0, 40); console.log('[Diag] rulescd.ini head (first 40 lines):', rulesCdHead); } catch (e) { console.warn('[Diag] Failed to read rulescd.ini head:', e); } } catch (e) { console.warn('[Diag] VFS ownership diagnostics failed:', e); } if (configToPersist) { if (configToPersist.isCdn()) { this.localPrefs.removeItem(StorageKey.GameRes); } else { this.localPrefs.setItem(StorageKey.GameRes, configToPersist.serialize()); } gameResConfig = configToPersist; } this.gameResConfig = gameResConfig; this.cdnResourceLoader = cdnResLoader; ImageContext.cdnBaseUrl = this.gameResConfig?.isCdn() ? this.gameResConfig.getCdnBaseUrl() : undefined; ImageContext.vfs = Engine.vfs; try { console.log("Engine.iniFiles.has('rules.ini'):", Engine.iniFiles.has("rules.ini")); console.log("Engine.iniFiles.has('art.ini'):", Engine.iniFiles.has("art.ini")); console.log("[Diag] Engine.iniFiles.has('rulescd.ini'):", Engine.iniFiles.has("rulescd.ini")); console.log("[Diag] Engine.iniFiles.has('artcd.ini'):", Engine.iniFiles.has("artcd.ini")); Engine.loadRules(); try { const rulesIniUsed = Engine.getFileNameVariant('rules.ini'); const artIniUsed = Engine.getFileNameVariant('art.ini'); console.log('[Diag] Using base INIs:', { rulesIniUsed, artIniUsed }); const hasAPSplashSection = !!Engine.getIni(artIniUsed).getSection('APSplash') || !!Engine.getIni(rulesIniUsed).getSection('APSplash'); console.log('[Diag] APSplash section present in INIs:', hasAPSplashSection); try { const orderedSections: any[] = (Engine.getRules() as any).getOrderedSections?.() ?? []; console.log('[Diag] Merged rules - first sections:', orderedSections.slice(0, 120).map(s => s.name)); } catch (e) { console.warn('[Diag] Failed to dump ordered sections from merged rules:', e); } try { const mergedRules: any = Engine.getRules(); const warheads = mergedRules?.getSection?.('Warheads'); const listed: string[] = []; if (warheads?.entries) { warheads.entries.forEach((v: any) => { if (typeof v === 'string') listed.push(v); else if (Array.isArray(v)) listed.push(...v); }); } console.log('[Diag] Warheads listed (sample):', listed.slice(0, 40), 'total=', listed.length); } catch (e) { console.warn('[Diag] Failed to dump Warheads list from merged rules:', e); } try { const mergedRules: any = Engine.getRules(); const ObjectType: any = (Engine as any).ObjectType || (window as any).ra2web?.engine?.type?.ObjectType; const hasCdestSection = !!mergedRules.getSection?.('CDEST'); console.log('[Diag] CDEST section exists in merged rules:', hasCdestSection); try { const s = mergedRules.getSection?.('CDEST'); if (s?.entries) { const keys: string[] = []; s.entries.forEach((_v: any, k: string) => keys.push(k)); console.log('[Diag] CDEST merged keys (sample):', keys.slice(0, 30)); console.log('[Diag] CDEST Primary/Secondary/ElitePrimary/EliteSecondary:', s.get?.('Primary'), s.get?.('Secondary'), s.get?.('ElitePrimary'), s.get?.('EliteSecondary')); } } catch (e) { console.warn('[Diag] CDEST merged entries dump failed:', e); } const cdest = mergedRules.getObject?.('CDEST', ObjectType?.Vehicle ?? 2); let weaponName: string | undefined = cdest?.primary || cdest?.elitePrimary || cdest?.secondary || cdest?.eliteSecondary; if (weaponName) { const wpn = mergedRules.getWeapon(weaponName); console.log('[Diag] CDEST weapon mapping:', { weapon: wpn.name, warhead: wpn.warhead }); const hasWh = !!mergedRules.getSection?.(wpn.warhead); console.log('[Diag] CDEST warhead section exists in merged rules:', hasWh); } else { console.log('[Diag] CDEST weapon mapping: no primary/secondary found'); } try { const cdestSection = mergedRules.getSection?.('CDEST'); const spawnsName = cdestSection?.get?.('Spawns'); console.log('[Diag] CDEST Spawns:', spawnsName); if (spawnsName) { try { const spawnedRules = mergedRules.getObject?.(spawnsName, ObjectType?.Aircraft ?? 1); const spawnedPrimary = spawnedRules?.primary || spawnedRules?.elitePrimary || spawnedRules?.secondary || spawnedRules?.eliteSecondary; if (spawnedPrimary) { const spawnedWpn = mergedRules.getWeapon(spawnedPrimary); console.log('[Diag] Spawned unit primary:', { weapon: spawnedWpn.name, warhead: spawnedWpn.warhead }); } else { console.log('[Diag] Spawned unit has no primary/secondary'); } } catch (e) { console.warn('[Diag] Spawned unit probe failed:', e); } } } catch (e) { console.warn('[Diag] CDEST Spawns probe failed:', e); } try { const aswMerged = mergedRules.getSection?.('ASWLauncher'); const aswMergedWh = aswMerged?.get?.('Warhead'); console.log('[Diag] ASWLauncher in merged rules: warhead=', aswMergedWh); } catch (e) { console.warn('[Diag] ASWLauncher merged probe failed:', e); } try { const baseAsw = Engine.getIni('rules.ini').getSection('ASWLauncher'); const baseAswWh = baseAsw?.get?.('Warhead'); console.log('[Diag] ASWLauncher in base rules.ini: warhead=', baseAswWh); } catch (e) { console.warn('[Diag] ASWLauncher base probe failed:', e); } try { const apsInRulesCd = !!Engine.getIni('rulescd.ini').getSection('APSplash'); const apsInArtCd = !!Engine.getIni('artcd.ini').getSection('APSplash'); console.log('[Diag] APSplash in rulescd.ini:', apsInRulesCd, 'APSplash in artcd.ini:', apsInArtCd); } catch (e) { console.warn('[Diag] APSplash custom INI presence probe failed:', e); } try { const mergedHasAPSplash = !!mergedRules.getSection?.('APSplash'); console.log('[Diag] APSplash section exists in merged rules:', mergedHasAPSplash); } catch (e) { console.warn('[Diag] APSplash merged presence check failed:', e); } try { const baseCdest = Engine.getIni('rules.ini').getSection('CDEST'); const customCdest = Engine.getIni('rulescd.ini').getSection('CDEST'); console.log('[Diag] Base CDEST present:', !!baseCdest, 'Custom CDEST present:', !!customCdest); if (baseCdest) { console.log('[Diag] Base CDEST Primary/Secondary:', baseCdest.get?.('Primary'), baseCdest.get?.('Secondary')); } if (customCdest) { console.log('[Diag] Custom CDEST Primary/Secondary:', customCdest.get?.('Primary'), customCdest.get?.('Secondary')); } } catch (e) { console.warn('[Diag] Base/Custom CDEST probe failed:', e); } } catch (e) { console.warn('[Diag] CDEST mapping diagnostics failed:', e); } } catch (e) { console.warn('[Diag] INI presence diagnostics failed:', e); } try { const baseArt = Engine.getIni('art.ini'); const customArt = Engine.getIni('artcd.ini'); const mergedArt = Engine.getArt(); const probe = (name: string) => ({ name, base: !!baseArt?.getSection(name), custom: !!customArt?.getSection(name), merged: !!mergedArt?.getSection(name), }); console.log('[Diag] Art sections presence:', probe('GI'), probe('CONS'), probe('SEAL'), probe('ENGINEER'), probe('ROCK')); } catch (e) { console.warn('[Diag] Art presence diagnostics failed:', e); } } catch (err) { console.error('[Application] Engine.loadRules() failed:', err); } if (typeof window.gtag === 'function') { window.gtag('event', 'app_init', { res: this.gameResConfig?.source || 'unknown', modName: modName || '