3 Commits

Author SHA1 Message Date
Alessandro Autiero
d42946c44b 10.0.3 2024-12-09 22:28:24 +01:00
Alessandro Autiero
0a59a32c1b 10.0.2 2024-12-09 14:36:43 +01:00
Alessandro Autiero
2046cb14f6 10.0 2024-12-09 12:59:14 +01:00
9 changed files with 141 additions and 52 deletions

View File

@@ -134,17 +134,13 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
} }
Future<void> _startAriaServer() async { Future<void> _startAriaServer() async {
final running = await _isAriaRunning(); await stopDownloadServer();
if(running) {
await killProcessByPort(_ariaPort);
}
final aria2c = File("${assetsDirectory.path}\\build\\aria2c.exe"); final aria2c = File("${assetsDirectory.path}\\build\\aria2c.exe");
if(!aria2c.existsSync()) { if(!aria2c.existsSync()) {
throw "Missing aria2c.exe"; throw "Missing aria2c.exe";
} }
await startProcess( final process = await startProcess(
executable: aria2c, executable: aria2c,
args: [ args: [
"--max-connection-per-server=${Platform.numberOfProcessors}", "--max-connection-per-server=${Platform.numberOfProcessors}",
@@ -153,10 +149,14 @@ Future<void> _startAriaServer() async {
"--rpc-listen-all=true", "--rpc-listen-all=true",
"--rpc-allow-origin-all", "--rpc-allow-origin-all",
"--rpc-secret=$_ariaSecret", "--rpc-secret=$_ariaSecret",
"--rpc-listen-port=$_ariaPort" "--rpc-listen-port=$_ariaPort",
"--file-allocation=none"
], ],
window: false window: false
); );
process.stdOutput.listen((message) => log("[ARIA] Message: $message"));
process.stdError.listen((error) => log("[ARIA] Error: $error"));
process.exitCode.then((exitCode) => log("[ARIA] Exit code: $exitCode"));
for(var i = 0; i < _ariaMaxSpawnTime.inSeconds; i++) { for(var i = 0; i < _ariaMaxSpawnTime.inSeconds; i++) {
if(await _isAriaRunning()) { if(await _isAriaRunning()) {
return; return;
@@ -177,8 +177,8 @@ Future<bool> _isAriaRunning() async {
"token:${_ariaSecret}" "token:${_ariaSecret}"
] ]
}; };
await http.post(_ariaEndpoint, body: jsonEncode(statusRequest)); final response = await http.post(_ariaEndpoint, body: jsonEncode(statusRequest));
return true; return response.statusCode == 200;
}catch(_) { }catch(_) {
return false; return false;
} }
@@ -227,11 +227,16 @@ Future<void> _stopAriaDownload(String downloadId) async {
] ]
}; };
await http.post(_ariaEndpoint, body: jsonEncode(addDownloadRequest)); await http.post(_ariaEndpoint, body: jsonEncode(addDownloadRequest));
stopDownloadServer();
}catch(error) { }catch(error) {
throw "Stop failed (${error})"; throw "Stop failed (${error})";
} }
} }
Future<void> stopDownloadServer() async {
await killProcessByPort(_ariaPort);
}
Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File tempFile, FortniteBuildDownloadOptions options) async { Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File tempFile, FortniteBuildDownloadOptions options) async {
Process? process; Process? process;

View File

@@ -216,7 +216,6 @@
"downloadedVersion": "The download was completed successfully!", "downloadedVersion": "The download was completed successfully!",
"download": "Download", "download": "Download",
"downloading": "Downloading...", "downloading": "Downloading...",
"allocatingSpace": "Allocating disk space...",
"startingDownload": "Starting download...", "startingDownload": "Starting download...",
"extracting": "Extracting...", "extracting": "Extracting...",
"buildProgress": "{progress}%", "buildProgress": "{progress}%",
@@ -237,7 +236,7 @@
"startGame": "Start fortnite", "startGame": "Start fortnite",
"stopGame": "Close fortnite", "stopGame": "Close fortnite",
"waitingForGameServer": "Waiting for the game server to boot up...", "waitingForGameServer": "Waiting for the game server to boot up...",
"gameServerStartWarning": "The game server was started successfully, but Reboot didn't load", "gameServerStartWarning": "Unsupported version: the game server crashed while setting up the server",
"gameServerStartLocalWarning": "The game server was started successfully, but other players can't join", "gameServerStartLocalWarning": "The game server was started successfully, but other players can't join",
"gameServerStarted": "The game server was started successfully", "gameServerStarted": "The game server was started successfully",
"gameClientStarted": "The game client was started successfully", "gameClientStarted": "The game client was started successfully",

View File

@@ -171,7 +171,8 @@ Future<void> _initWindow() async {
}else { }else {
await windowManager.setAlignment(Alignment.center); await windowManager.setAlignment(Alignment.center);
} }
await windowManager.setPreventClose(true);
await windowManager.setResizable(true);
if(isWin11) { if(isWin11) {
await Window.setEffect( await Window.setEffect(
effect: WindowEffect.acrylic, effect: WindowEffect.acrylic,

View File

@@ -8,10 +8,10 @@ import 'package:get/get.dart';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart'; import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart'; import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/translations.dart'; import 'package:reboot_launcher/src/util/translations.dart';
import 'package:reboot_launcher/src/util/types.dart'; import 'package:reboot_launcher/src/util/types.dart';
import 'package:reboot_launcher/src/widget/file_selector.dart'; import 'package:reboot_launcher/src/widget/file_selector.dart';
import 'package:universal_disk_space/universal_disk_space.dart';
import 'package:windows_taskbar/windows_taskbar.dart'; import 'package:windows_taskbar/windows_taskbar.dart';
class AddVersionDialog extends StatefulWidget { class AddVersionDialog extends StatefulWidget {
@@ -35,9 +35,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
final Rxn<double> _progress = Rxn(); final Rxn<double> _progress = Rxn();
final RxInt _speed = RxInt(0); final RxInt _speed = RxInt(0);
late DiskSpace _diskSpace;
late Future<List<FortniteBuild>> _fetchFuture; late Future<List<FortniteBuild>> _fetchFuture;
late Future _diskFuture;
SendPort? _downloadPort; SendPort? _downloadPort;
Object? _error; Object? _error;
@@ -45,10 +43,10 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
@override @override
void initState() { void initState() {
_fetchFuture = compute(fetchBuilds, null); _fetchFuture = compute(fetchBuilds, null).then((value) {
_diskSpace = DiskSpace(); _updateFormDefaults();
_diskFuture = _diskSpace.scan() return value;
.then((_) => _updateFormDefaults()); });
super.initState(); super.initState();
} }
@@ -71,7 +69,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
switch(_status.value){ switch(_status.value){
case _DownloadStatus.form: case _DownloadStatus.form:
return FutureBuilder( return FutureBuilder(
future: Future.wait([_fetchFuture, _diskFuture]).then((_) async => await _fetchFuture), future: _fetchFuture,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasError) { if (snapshot.hasError) {
WidgetsBinding.instance.addPostFrameCallback((_) => _onDownloadError(snapshot.error, snapshot.stackTrace)); WidgetsBinding.instance.addPostFrameCallback((_) => _onDownloadError(snapshot.error, snapshot.stackTrace));
@@ -244,12 +242,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
), ),
), ),
if(_progress.value != null && !_isAllocatingDiskSpace) if(_progress.value != null)
const SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
if(_progress.value != null && !_isAllocatingDiskSpace) if(_progress.value != null)
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@@ -272,7 +270,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ProgressBar(value: _isAllocatingDiskSpace ? null : _progress.value?.toDouble()) child: ProgressBar(value: _progress.value?.toDouble())
), ),
const SizedBox( const SizedBox(
@@ -291,15 +289,9 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
return translations.startingDownload; return translations.startingDownload;
} }
if (_speed.value == 0) {
return translations.allocatingSpace;
}
return translations.downloading; return translations.downloading;
} }
bool get _isAllocatingDiskSpace => _status.value == _DownloadStatus.downloading && _speed.value == 0;
Widget _buildFormBody(List<FortniteBuild> builds) { Widget _buildFormBody(List<FortniteBuild> builds) {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@@ -450,16 +442,16 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
_build.value = null; _build.value = null;
} }
if(_source.value != _BuildSource.local && _diskSpace.disks.isNotEmpty) { final disks = WindowsDisk.available();
await _fetchFuture; if(_source.value != _BuildSource.local && disks.isNotEmpty) {
final bestDisk = _diskSpace.disks final bestDisk = disks.reduce((first, second) => first.freeBytesAvailable > second.freeBytesAvailable ? first : second);
.reduce((first, second) => first.availableSpace > second.availableSpace ? first : second);
final build = _build.value; final build = _build.value;
if(build == null){ if(build == null){
return; return;
} }
final pathText = "${bestDisk.devicePath}\\FortniteBuilds\\${build.version}"; print("${bestDisk.path}\\FortniteBuilds\\${build.version}");
final pathText = "${bestDisk.path}FortniteBuilds\\${build.version}";
_pathController.text = pathText; _pathController.text = pathText;
_pathController.selection = TextSelection.collapsed(offset: pathText.length); _pathController.selection = TextSelection.collapsed(offset: pathText.length);
} }

View File

@@ -10,6 +10,7 @@ import 'package:get/get.dart';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
import 'package:reboot_launcher/src/controller/backend_controller.dart'; import 'package:reboot_launcher/src/controller/backend_controller.dart';
import 'package:reboot_launcher/src/controller/dll_controller.dart'; import 'package:reboot_launcher/src/controller/dll_controller.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
import 'package:reboot_launcher/src/controller/hosting_controller.dart'; import 'package:reboot_launcher/src/controller/hosting_controller.dart';
import 'package:reboot_launcher/src/controller/settings_controller.dart'; import 'package:reboot_launcher/src/controller/settings_controller.dart';
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart'; import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
@@ -43,6 +44,7 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepAliveClientMixin { class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepAliveClientMixin {
final BackendController _backendController = Get.find<BackendController>(); final BackendController _backendController = Get.find<BackendController>();
final GameController _gameController = Get.find<GameController>();
final HostingController _hostingController = Get.find<HostingController>(); final HostingController _hostingController = Get.find<HostingController>();
final SettingsController _settingsController = Get.find<SettingsController>(); final SettingsController _settingsController = Get.find<SettingsController>();
final DllController _dllController = Get.find<DllController>(); final DllController _dllController = Get.find<DllController>();
@@ -160,11 +162,45 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
@override @override
void onWindowClose() async { void onWindowClose() async {
try {
await windowManager.hide();
}catch(error) {
log("[WINDOW] Cannot hide window: $error");
}
try { try {
await _hostingController.discardServer(); await _hostingController.discardServer();
}catch(error) { }catch(error) {
log("[HOSTING] Cannot discard server: $error"); log("[HOSTING] Cannot discard server on exit: $error");
} }
try {
if(_backendController.started.value) {
await _backendController.toggleInteractive();
}
}catch(error) {
log("[BACKEND] Cannot stop backend on exit: $error");
}
try {
_gameController.instance.value?.kill();
}catch(error) {
log("[GAME] Cannot stop game on exit: $error");
}
try {
_hostingController.instance.value?.kill();
}catch(error) {
log("[HOST] Cannot stop host on exit: $error");
}
try {
await stopDownloadServer();
}catch(error) {
log("[ARIA] Cannot stop aria server on exit: $error");
}
exit(0);
} }
@override @override

View File

@@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:math';
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
@@ -9,8 +10,12 @@ const Duration _timeout = Duration(seconds: 5);
Completer<bool> pingGameServerOrTimeout(String address, Duration timeout) { Completer<bool> pingGameServerOrTimeout(String address, Duration timeout) {
final completer = Completer<bool>(); final completer = Completer<bool>();
final start = DateTime.now(); final start = DateTime.now();
(() async { _pingGameServerOrTimeout(completer, start, timeout, address);
while (!completer.isCompleted && DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch < timeout.inMilliseconds) { return completer;
}
Future<void> _pingGameServerOrTimeout(Completer<bool> completer, DateTime start, Duration timeout, String address) async {
while (!completer.isCompleted && max(DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch, 0) < timeout.inMilliseconds) {
final result = await pingGameServer(address); final result = await pingGameServer(address);
if(result) { if(result) {
completer.complete(true); completer.complete(true);
@@ -21,8 +26,6 @@ Completer<bool> pingGameServerOrTimeout(String address, Duration timeout) {
if(!completer.isCompleted) { if(!completer.isCompleted) {
completer.complete(false); completer.complete(false);
} }
})();
return completer;
} }
Future<bool> pingGameServer(String address) async { Future<bool> pingGameServer(String address) async {

View File

@@ -492,3 +492,57 @@ int _convertToHString(String string) {
extension WindowManagerExtension on WindowManager { extension WindowManagerExtension on WindowManager {
Future<void> maximizeOrRestore() async => await windowManager.isMaximized() ? windowManager.restore() : windowManager.maximize(); Future<void> maximizeOrRestore() async => await windowManager.isMaximized() ? windowManager.restore() : windowManager.maximize();
} }
class WindowsDisk {
static final String _nullTerminator = String.fromCharCode(0);
final String path;
final int freeBytesAvailable;
final int totalNumberOfBytes;
const WindowsDisk._internal(this.path, this.freeBytesAvailable, this.totalNumberOfBytes);
static List<WindowsDisk> available() {
final buffer = malloc.allocate<Utf16>(MAX_PATH);
try {
final length = GetLogicalDriveStrings(MAX_PATH, buffer);
if (length == 0) {
return [];
}
return buffer.toDartString(length: length)
.split(_nullTerminator)
.where((drive) => drive.length > 1)
.map((driveName) {
final freeBytesAvailable = calloc<Uint64>();
final totalNumberOfBytes = calloc<Uint64>();
final totalNumberOfFreeBytes = calloc<Uint64>();
try {
GetDiskFreeSpaceEx(
driveName.toNativeUtf16(),
freeBytesAvailable,
totalNumberOfBytes,
totalNumberOfFreeBytes
);
return WindowsDisk._internal(
driveName,
freeBytesAvailable.value,
totalNumberOfBytes.value
);
} finally {
calloc.free(freeBytesAvailable);
calloc.free(totalNumberOfBytes);
calloc.free(totalNumberOfFreeBytes);
}
})
.toList(growable: false);
} finally {
calloc.free(buffer);
}
}
@override
String toString() {
return 'WindowsDisk{path: $path, freeBytesAvailable: $freeBytesAvailable, totalNumberOfBytes: $totalNumberOfBytes}';
}
}

View File

@@ -492,7 +492,7 @@ class _LaunchButtonState extends State<LaunchButton> {
final pingOperation = pingGameServerOrTimeout( final pingOperation = pingGameServerOrTimeout(
"$publicIp:$gameServerPort", "$publicIp:$gameServerPort",
const Duration(days: 365) const Duration(days: 1)
); );
this._pingOperation = pingOperation; this._pingOperation = pingOperation;
_gameServerInfoBar = showRebootInfoBar( _gameServerInfoBar = showRebootInfoBar(

View File

@@ -1,6 +1,6 @@
name: reboot_launcher name: reboot_launcher
description: Graphical User Interface for Project Reboot description: Graphical User Interface for Project Reboot
version: "10.0.0" version: "10.0.3"
publish_to: 'none' publish_to: 'none'
@@ -58,7 +58,6 @@ dependencies:
# Storage # Storage
get_storage: ^2.1.1 get_storage: ^2.1.1
universal_disk_space: ^0.2.3
path: ^1.9.0 path: ^1.9.0
# Translations # Translations