обновил лаунчер под новую версию веба

This commit is contained in:
2026-10-04 13:19:12 +04:00
parent dd189fe48f
commit d3d19bfaeb
20 changed files with 486 additions and 64 deletions
@@ -33,6 +33,10 @@ final class GameProcessHandle {
@lazySingleton
class GameInstallationService {
String get operatingSystem => Platform.operatingSystem;
String get configuredAuthAddress => Config.gameAuthAddress;
Future<ClientInstallationSnapshot> scanInstallation(
String installationDir, {
required ClientManifest manifest,
@@ -249,18 +253,39 @@ class GameInstallationService {
final executable = File(executablePath);
if (!await executable.exists()) {
throw LauncherSyncException(
'MoonWell client was not found: $executablePath',
);
}
final authAddress = await resolveAuthAddress(installationDir);
final rootPath = p.absolute(installationDir);
final directDataPath = p.join(rootPath, 'Data');
final nestedDataPath = p.join(rootPath, 'WoW', 'Data');
final dataPath = await Directory(directDataPath).exists()
? directDataPath
: nestedDataPath;
if (!await Directory(dataPath).exists()) {
throw const LauncherSyncException(
'Wow.exe was not found in the selected folder.',
'WoW 1.12.1 Data or WoW/Data was not found in the selected folder.',
);
}
if (!authorization.isUsable()) {
throw const LauncherSyncException(
'Game ticket has expired. Press Play to request a new ticket.',
);
}
return startGameProcess(
executablePath: executablePath,
arguments: const <String>[],
workingDirectory: installationDir,
workingDirectory: rootPath,
environment: <String, String>{
'MOONWELL_LAUNCH_ACCOUNT': authorization.account,
'MOONWELL_LAUNCH_TICKET': authorization.ticket,
'WOW_USER': authorization.account,
'WOW_PASS': authorization.ticket,
'WOW_HOST': authAddress,
'WOW_DATA': dataPath,
'BENILLA_HOME': p.join(rootPath, 'benilla-config'),
},
);
}
@@ -308,7 +333,56 @@ class GameInstallationService {
}
String getExecutablePath(String installationDir) {
return p.join(installationDir, Config.gameExecutableName);
final rootPath = p.absolute(installationDir);
return switch (operatingSystem) {
'windows' => p.join(rootPath, Config.gameExecutableName),
'macos' => p.join(
rootPath,
'MoonWell.app',
'Contents',
'MacOS',
'launch',
),
_ => p.join(rootPath, 'MoonWell'),
};
}
/// The auth endpoint is separate from a realm's world port (normally 8085).
Future<String> resolveAuthAddress(String installationDir) async {
final configured = configuredAuthAddress.trim();
if (configured.isNotEmpty) {
return _validateAuthAddress(configured);
}
final realmlist = File(p.join(installationDir, 'realmlist.wtf'));
if (await realmlist.exists()) {
final pattern = RegExp(
r'^\s*set\s+realmlist\s+(?:"([^"]+)"|([^\s]+))\s*(?://.*)?$',
caseSensitive: false,
multiLine: true,
);
final matches = pattern.allMatches(await realmlist.readAsString());
if (matches.isNotEmpty) {
final match = matches.last;
return _validateAuthAddress((match.group(1) ?? match.group(2)!).trim());
}
}
throw const LauncherSyncException(
'Set realmlist in realmlist.wtf or configure MOONWELL_AUTH_ADDRESS.',
);
}
String _validateAuthAddress(String address) {
final match = RegExp(
r'^([A-Za-z0-9][A-Za-z0-9._-]*)(?::([0-9]{1,5}))?$',
).firstMatch(address);
final port = int.tryParse(match?.group(2) ?? '3724');
if (match == null || port == null || port < 1 || port > 65535) {
throw const LauncherSyncException(
'The auth address must be a hostname or IPv4 address with an optional TCP port.',
);
}
return address;
}
String resolveClientPath(String installationDir, String relativePath) {
@@ -18,6 +18,8 @@ import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_ses
@lazySingleton
class LauncherApiClient {
String get apiBaseUrl => Config.launcherApiBaseUrl;
LauncherApiClient({LauncherLogService? launcherLogService})
: _launcherLogService = launcherLogService ?? LauncherLogService(),
_dio = Dio(
@@ -169,7 +171,7 @@ class LauncherApiClient {
try {
final response = await _dio.postUri(
ticketUri,
data: const <String, Object?>{'client_build': 12340},
data: const <String, Object?>{'client_build': Config.gameClientBuild},
options: _authorizedOptions(accessToken),
);
final ticket = LauncherGameTicket.fromJson(_coerceMap(response.data));
@@ -425,7 +427,7 @@ class LauncherApiClient {
}
Uri _resolveUri(String path) {
final rawBaseUrl = Config.launcherApiBaseUrl.trim();
final rawBaseUrl = apiBaseUrl.trim();
if (rawBaseUrl.isEmpty) {
throw const LauncherConfigurationException(
'Set MOONWELL_API_BASE_URL before using the launcher API.',