From 2fb6774ec6d681b9d58e737ac75394441b2f4ee6 Mon Sep 17 00:00:00 2001 From: sindoring Date: Sat, 10 Oct 2026 16:05:55 +0400 Subject: [PATCH] Select the installed language pack when launching MoonWell Pass WOW_LOCALE=auto alongside the selected Data directory so inherited locale settings cannot override an installed ruRU pack. Keep language archives in the regular manifest download and verification flow. Document the contract and prepare launcher version 1.0.7+10 for the localization release. Verified on Windows through FVM: full format check and flutter analyze passed; all 76 Flutter tests passed, including manifest retention and verification of Russian archives and the game's child environment. No macOS build was run. --- AGENTS.md | 2 + README.md | 6 +++ docs/launcher_web_api_spec.md | 6 +++ .../data/game_installation_service.dart | 2 + pubspec.yaml | 2 +- .../data/game_installation_service_test.dart | 41 +++++++++++++++++++ 6 files changed, 58 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 2e44274..fa5e116 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -98,6 +98,8 @@ or local-only API URLs. plus legacy `WoW.exe` and DLLs directly in the installation root or `WoW`. Remove unused Data subdirectories only after their children, without recursive deletion. Preserve unrelated files and user state outside these areas. +- Language archives under `Data/ruRU` or `WoW/Data/ruRU` use the regular manifest sync. + Pass `WOW_LOCALE=auto` to the game so its selected install determines the locale. - Downloads must write to `*.moonwell.part`, verify size and SHA-256, then replace the destination file only after successful verification. - Local scans should ignore volatile client directories and launcher metadata: diff --git a/README.md b/README.md index 6a1bcfd..ddf7b15 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,12 @@ folder. For builds that pin an endpoint, set `--dart-define=MOONWELL_AUTH_ADDRESS=login.example:3724` instead. This endpoint is the vMaNGOS auth server, separate from the realm's world port and the Web API. +Russian 1.12.1 language archives are regular manifest files under `Data/ruRU` +(or `WoW/Data/ruRU` for a nested install). They are downloaded and verified with +the same size and SHA-256 checks as the base client. The launcher passes +`WOW_LOCALE=auto` to the game: an installed pack selects Russian, and a base-only +install selects English, regardless of the launcher's inherited environment. + Example run command: ```powershell diff --git a/docs/launcher_web_api_spec.md b/docs/launcher_web_api_spec.md index cf65c94..f55cbe1 100644 --- a/docs/launcher_web_api_spec.md +++ b/docs/launcher_web_api_spec.md @@ -286,6 +286,8 @@ When the user presses `Play`: - `WOW_HOST=` - `WOW_DATA=` - `BENILLA_HOME=/benilla-config` + - `WOW_LOCALE=auto` (select the installed `Data/ruRU` or `WoW/Data/ruRU` language pack, + otherwise English; override any locale inherited from the launcher's parent) 6. launcher retains a process handle; while the process is alive, it disables repeated game launches and client synchronization 7. when the process exits, the launcher returns to the ready-to-play state @@ -297,6 +299,10 @@ If the executable, data directory, or auth endpoint is missing or invalid, launch fails with an error. A consumed ticket cannot authenticate again; close the client and press Play for a fresh ticket after a session ends. +Language archives are ordinary client-manifest entries, including their nested paths, +sizes and SHA-256 hashes. Synchronization verifies and retains required `ruRU` files +and removes obsolete archives under the same manifest-authoritative Data policy. + ## Error Behavior The launcher surfaces errors for: diff --git a/lib/features/launcher/data/game_installation_service.dart b/lib/features/launcher/data/game_installation_service.dart index cb235d2..6c5061c 100644 --- a/lib/features/launcher/data/game_installation_service.dart +++ b/lib/features/launcher/data/game_installation_service.dart @@ -304,6 +304,8 @@ class GameInstallationService { 'WOW_PASS': authorization.ticket, 'WOW_HOST': authAddress, 'WOW_DATA': dataPath, + // Let the client select Data/ruRU independently of the launcher's parent environment. + 'WOW_LOCALE': 'auto', 'BENILLA_HOME': p.join(rootPath, 'benilla-config'), }, ); diff --git a/pubspec.yaml b/pubspec.yaml index 596e317..c2051af 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.5+8 +version: 1.0.7+10 environment: sdk: ">=3.10.0" diff --git a/test/features/launcher/data/game_installation_service_test.dart b/test/features/launcher/data/game_installation_service_test.dart index de0b22a..9c37809 100644 --- a/test/features/launcher/data/game_installation_service_test.dart +++ b/test/features/launcher/data/game_installation_service_test.dart @@ -421,6 +421,7 @@ void main() { 'WOW_PASS': 'A1B2C3D4E5F6G7H8', 'WOW_HOST': '127.0.0.1:3724', 'WOW_DATA': '${rootDirectory.path}${Platform.pathSeparator}Data', + 'WOW_LOCALE': 'auto', 'BENILLA_HOME': '${rootDirectory.path}${Platform.pathSeparator}benilla-config', }); @@ -479,6 +480,46 @@ void main() { expect(recordingService.environment?['WOW_DATA'], nestedData.path); }); + test('verifies and retains Russian archives before launching', () async { + final paths = [ + 'MoonWell.exe', + 'Data/common.MPQ', + 'Data/ruRU/patch-1.MPQ', + 'Data/ruRU/patch-2.MPQ', + 'Data/ruRU/patch-3.MPQ', + ]; + for (final path in paths.skip(2)) { + final file = File(service.resolveClientPath(rootDirectory.path, path)); + await file.parent.create(recursive: true); + await file.writeAsString('localized archive: $path'); + } + final manifest = await _manifestForPaths(service, rootDirectory, paths); + final snapshot = await service.scanInstallation( + rootDirectory.path, + manifest: manifest, + ); + expect(snapshot.buildHash, manifest.buildHash); + expect(snapshot.stalePaths, isEmpty); + final recordingService = _RecordingGameInstallationService(); + await recordingService.launchGame( + rootDirectory.path, + authorization: _validTicket(), + ); + expect(recordingService.environment?['WOW_LOCALE'], 'auto'); + expect( + recordingService.environment?['WOW_DATA'], + '${rootDirectory.path}${Platform.pathSeparator}Data', + ); + for (final path in paths.skip(2)) { + expect( + await File( + service.resolveClientPath(rootDirectory.path, path), + ).readAsString(), + 'localized archive: $path', + ); + } + }); + test('configured auth address overrides the install realmlist', () async { final configured = _RecordingGameInstallationService( authAddress: 'login.example:3725',