правки по лаунчеру

This commit is contained in:
2026-07-28 20:43:53 +04:00
parent 155922a3f1
commit 0b6f7fc647
51 changed files with 3158 additions and 437 deletions
@@ -22,6 +22,13 @@ typedef InstallationScanProgressCallback =
int totalFiles,
);
final class GameProcessHandle {
final int pid;
final Future<int> exitCode;
const GameProcessHandle({required this.pid, required this.exitCode});
}
@lazySingleton
class GameInstallationService {
Future<ClientInstallationSnapshot> scanInstallation(
@@ -209,7 +216,7 @@ class GameInstallationService {
await cacheDirectory.create(recursive: true);
}
Future<void> launchGame(String installationDir) async {
Future<GameProcessHandle> launchGame(String installationDir) async {
final executablePath = getExecutablePath(installationDir);
final executable = File(executablePath);
@@ -219,12 +226,16 @@ class GameInstallationService {
);
}
await Process.start(
final process = await Process.start(
executablePath,
const <String>[],
workingDirectory: installationDir,
mode: ProcessStartMode.detached,
mode: ProcessStartMode.normal,
);
await process.stdin.close();
unawaited(process.stdout.drain<void>());
unawaited(process.stderr.drain<void>());
return GameProcessHandle(pid: process.pid, exitCode: process.exitCode);
}
Future<bool> hasClientExecutable(String installationDir) async {
@@ -9,8 +9,10 @@ import 'package:moonwell_launcher/features/downloader/domain/entities/download_e
import 'package:moonwell_launcher/features/launcher/data/launcher_log_service.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/client_manifest.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/client_manifest_file.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_account.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_exception.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_news_item.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_realm.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_session.dart';
@lazySingleton
@@ -58,6 +60,108 @@ class LauncherApiClient {
}
}
Future<String> register({
required String username,
required String email,
required String password,
required String passwordConfirmation,
required bool terms,
String? inviteCode,
}) async {
final registerUri = _resolveUri('api/launcher/register');
final normalizedInviteCode = inviteCode?.trim();
try {
final response = await _dio.postUri(
registerUri,
data: <String, Object?>{
'username': username,
'email': email,
'password': password,
'password_confirmation': passwordConfirmation,
'terms': terms,
if (normalizedInviteCode != null && normalizedInviteCode.isNotEmpty)
'invite_code': normalizedInviteCode,
},
);
final payload = _coerceMap(response.data);
return payload['message'] as String? ?? 'Аккаунт успешно создан.';
} on DioException catch (error, stackTrace) {
await _launcherLogService.error(
'Launcher registration request failed.',
details: <String, Object?>{
'endpoint': registerUri.toString(),
'statusCode': error.response?.statusCode,
'dioType': error.type.name,
'responseBody': _summarizePayload(error.response?.data),
},
error: error,
stackTrace: stackTrace,
);
throw LauncherApiException(_extractErrorMessage(error));
}
}
Future<List<LauncherRealm>> fetchRealms() async {
final realmsUri = _resolveUri('api/launcher/realms');
try {
final response = await _dio.getUri(realmsUri);
final payload = _coerceMap(response.data);
final rawItems = payload['data'];
if (rawItems is! List) {
throw const LauncherApiException('Unexpected launcher realms payload.');
}
return rawItems
.whereType<Map>()
.map(
(item) => LauncherRealm.fromJson(
item.map((key, value) => MapEntry(key.toString(), value)),
),
)
.toList(growable: false);
} on DioException catch (error, stackTrace) {
await _launcherLogService.error(
'Launcher realms request failed.',
details: <String, Object?>{
'endpoint': realmsUri.toString(),
'statusCode': error.response?.statusCode,
'dioType': error.type.name,
'responseBody': _summarizePayload(error.response?.data),
},
error: error,
stackTrace: stackTrace,
);
throw LauncherApiException(_extractErrorMessage(error));
}
}
Future<LauncherAccount> fetchAccount(String accessToken) async {
final accountUri = _resolveUri('api/launcher/account');
try {
final response = await _dio.getUri(
accountUri,
options: _authorizedOptions(accessToken),
);
return LauncherAccount.fromJson(_coerceMap(response.data));
} on DioException catch (error, stackTrace) {
await _launcherLogService.error(
'Launcher account request failed.',
details: <String, Object?>{
'endpoint': accountUri.toString(),
'statusCode': error.response?.statusCode,
'dioType': error.type.name,
'responseBody': _summarizePayload(error.response?.data),
},
error: error,
stackTrace: stackTrace,
);
throw LauncherApiException(_extractErrorMessage(error));
}
}
Future<ClientManifest> fetchManifest(String accessToken) async {
final manifestUri = _resolveUri('api/launcher/manifest');
@@ -0,0 +1,14 @@
Uri? buildLauncherNewsUri(String rawBaseUrl, int newsId) {
final normalizedBaseUrl = rawBaseUrl.trim();
final baseUri = Uri.tryParse(
normalizedBaseUrl.endsWith('/') ? normalizedBaseUrl : '$normalizedBaseUrl/',
);
if (baseUri == null ||
!baseUri.hasScheme ||
baseUri.host.isEmpty ||
newsId <= 0) {
return null;
}
return baseUri.resolve('news/$newsId');
}