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.
This commit is contained in:
2026-10-10 16:05:55 +04:00
parent 18c8e07744
commit 2fb6774ec6
6 changed files with 58 additions and 1 deletions
+2
View File
@@ -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:
+6
View File
@@ -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
+6
View File
@@ -286,6 +286,8 @@ When the user presses `Play`:
- `WOW_HOST=<auth-address>`
- `WOW_DATA=<absolute-data-directory>`
- `BENILLA_HOME=<install-root>/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:
@@ -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'),
},
);
+1 -1
View File
@@ -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"
@@ -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',