Released 9.2.7

This commit is contained in:
Alessandro Autiero
2024-09-14 12:37:56 +02:00
parent a9af28273a
commit bfe15e43d9
15 changed files with 331 additions and 255 deletions

View File

@@ -133,9 +133,9 @@ Future<void> _downloadArchive(FortniteBuildDownloadOptions options, Completer st
throw _genericError; throw _genericError;
}, },
headers: byteStart == null || byteStart <= 0 ? { headers: byteStart == null || byteStart <= 0 ? {
"Cookie": "_c_t_c=1"
} : { } : {
"Cookie": "_c_t_c=1",
"Range": "bytes=${byteStart}-" "Range": "bytes=${byteStart}-"
}, },
) )

View File

@@ -7,7 +7,7 @@ environment:
sdk: ">=3.0.0 <=4.0.0" sdk: ">=3.0.0 <=4.0.0"
dependencies: dependencies:
dio: ^5.3.2 dio: ^5.7.0
win32: 3.0.0 win32: 3.0.0
ffi: ^2.1.0 ffi: ^2.1.0
path: ^1.8.3 path: ^1.8.3

View File

@@ -368,5 +368,7 @@
"automaticGameServerDialogStart": "Start server", "automaticGameServerDialogStart": "Start server",
"gameResetDefaultsName": "Reset", "gameResetDefaultsName": "Reset",
"gameResetDefaultsDescription": "Resets the game's settings to their default values", "gameResetDefaultsDescription": "Resets the game's settings to their default values",
"gameResetDefaultsContent": "Reset" "gameResetDefaultsContent": "Reset",
"selectFile": "Select a file",
"reset": "Reset"
} }

View File

@@ -109,7 +109,9 @@ class DllController extends GetxController {
duration: null duration: null
); );
} }
timestamp.value = await downloadRebootDll(url.text); final result = downloadRebootDll(url.text);
timestamp.value = await Future.wait([result, Future.delayed(const Duration(seconds: 1))], eagerError: false)
.then((_) => result);
status.value = UpdateStatus.success; status.value = UpdateStatus.success;
infoBarEntry?.close(); infoBarEntry?.close();
if(!silent) { if(!silent) {
@@ -126,15 +128,18 @@ class DllController extends GetxController {
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error; error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
error = error.toLowerCase(); error = error.toLowerCase();
status.value = UpdateStatus.error; status.value = UpdateStatus.error;
showRebootInfoBar( infoBarEntry = showRebootInfoBar(
translations.downloadDllError("reboot.dll", error.toString()), translations.downloadDllError(error.toString(), "reboot.dll"),
duration: infoBarLongDuration, duration: infoBarLongDuration,
severity: InfoBarSeverity.error, severity: InfoBarSeverity.error,
action: Button( action: Button(
onPressed: () => updateGameServerDll( onPressed: () async {
infoBarEntry?.close();
updateGameServerDll(
force: true, force: true,
silent: silent silent: silent
), );
},
child: Text(translations.downloadDllRetry), child: Text(translations.downloadDllRetry),
) )
); );
@@ -215,7 +220,7 @@ class DllController extends GetxController {
error = error.toLowerCase(); error = error.toLowerCase();
final completer = Completer(); final completer = Completer();
await showRebootInfoBar( await showRebootInfoBar(
translations.downloadDllError(fileName, error.toString()), translations.downloadDllError(error.toString(), fileName),
duration: infoBarLongDuration, duration: infoBarLongDuration,
severity: InfoBarSeverity.error, severity: InfoBarSeverity.error,
onDismissed: () => completer.complete(null), onDismissed: () => completer.complete(null),

View File

@@ -18,9 +18,12 @@ void onError(Object exception, StackTrace? stackTrace, bool framework) {
} }
lastError = exception.toString(); lastError = exception.toString();
final route = ModalRoute.of(pageKey.currentContext!); if(inDialog){
if(route != null && !route.isCurrent){ final context = pageKey.currentContext;
Navigator.of(pageKey.currentContext!).pop(false); if(context != null) {
Navigator.of(context).pop(false);
inDialog = false;
}
} }
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showRebootDialog( WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showRebootDialog(

View File

@@ -5,10 +5,13 @@ import 'dart:isolate';
import 'package:fluent_ui/fluent_ui.dart'; import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.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/widget/file_selector.dart'; import 'package:reboot_launcher/src/widget/file_selector.dart';
import 'package:universal_disk_space/universal_disk_space.dart'; import 'package:universal_disk_space/universal_disk_space.dart';
import 'package:windows_taskbar/windows_taskbar.dart'; import 'package:windows_taskbar/windows_taskbar.dart';
@@ -41,6 +44,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
SendPort? _downloadPort; SendPort? _downloadPort;
Object? _error; Object? _error;
StackTrace? _stackTrace; StackTrace? _stackTrace;
bool _selecting = false;
@override @override
void initState() { void initState() {
@@ -102,7 +106,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
return ErrorDialog( return ErrorDialog(
exception: _error ?? Exception(translations.unknownError), exception: _error ?? Exception(translations.unknownError),
stackTrace: _stackTrace, stackTrace: _stackTrace,
errorMessageBuilder: (exception) => translations.downloadVersionError(exception.toString()) errorMessageBuilder: (exception) {
var error = exception.toString();
error = error.after("Error: ")?.replaceAll(":", ",") ?? error.after(": ") ?? error;
error = error.toLowerCase();
return translations.downloadVersionError(error);
}
); );
case _DownloadStatus.done: case _DownloadStatus.done:
return InfoDialog( return InfoDialog(
@@ -274,7 +283,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
); );
} }
Widget _buildFormBody(List<FortniteBuild> builds) => Column( Widget _buildFormBody(List<FortniteBuild> builds) {
return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@@ -292,7 +302,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle, windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle,
controller: _pathController, controller: _pathController,
validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination, validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination,
folder: true folder: true,
allowNavigator: true
), ),
const SizedBox( const SizedBox(
@@ -300,6 +311,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
) )
], ],
); );
}
String? _checkGameFolder(text) { String? _checkGameFolder(text) {
if (text == null || text.isEmpty) { if (text == null || text.isEmpty) {

View File

@@ -153,9 +153,9 @@ class _BackendPageState extends RebootPageState<BackendPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_backendController.detached.value ? translations.on : translations.off _backendController.detached.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),

View File

@@ -298,8 +298,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
}); });
} }
Widget _buildBody() { Widget _buildBody() => Expanded(
return Expanded(
child: Padding( child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: HomePage.kDefaultPadding, left: HomePage.kDefaultPadding,
@@ -339,7 +338,6 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
) )
), ),
); );
}
Widget _buildBodyContent() => PageView.builder( Widget _buildBodyContent() => PageView.builder(
controller: _pageController, controller: _pageController,

View File

@@ -175,9 +175,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null, contentWidth: null,
content: Obx(() => Row( content: Obx(() => Row(
children: [ children: [
Text( Obx(() => Text(
_hostingController.discoverable.value ? translations.on : translations.off _hostingController.discoverable.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),
@@ -238,9 +238,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_hostingController.autoRestart.value ? translations.on : translations.off _hostingController.autoRestart.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),
@@ -278,12 +278,19 @@ class _HostingPageState extends RebootPageState<HostPage> {
title: Text(translations.settingsServerName), title: Text(translations.settingsServerName),
subtitle: Text(translations.settingsServerSubtitle), subtitle: Text(translations.settingsServerSubtitle),
children: [ children: [
SettingTile( _internalFilesServerType,
_internalFilesUpdateTimer,
_internalFilesServerSource
],
);
Widget get _internalFilesServerType => SettingTile(
icon: Icon( icon: Icon(
FluentIcons.timer_24_regular FluentIcons.games_24_regular
), ),
title: Text(translations.settingsServerTypeName), title: Text(translations.settingsServerTypeName),
subtitle: Text(translations.settingsServerTypeDescription), subtitle: Text(translations.settingsServerTypeDescription),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Obx(() => DropDownButton( content: Obx(() => DropDownButton(
onOpen: () => inDialog = true, onOpen: () => inDialog = true,
onClose: () => inDialog = false, onClose: () => inDialog = false,
@@ -309,41 +316,28 @@ class _HostingPageState extends RebootPageState<HostPage> {
} }
)).toList() )).toList()
)) ))
),
Obx(() {
if(!_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
return createFileSetting(
title: translations.settingsServerFileName,
description: translations.settingsServerFileDescription,
controller: _dllController.gameServerDll,
onReset: () {
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
_dllController.gameServerDll.text = path;
_dllController.downloadCriticalDllInteractive(path);
}
); );
}),
Obx(() {
if(_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
Widget get _internalFilesServerSource => Obx(() {
if(!_dllController.customGameServer.value) {
return SettingTile( return SettingTile(
icon: Icon( icon: Icon(
FluentIcons.globe_24_regular FluentIcons.globe_24_regular
), ),
title: Text(translations.settingsServerMirrorName), title: Text(translations.settingsServerMirrorName),
subtitle: Text(translations.settingsServerMirrorDescription), subtitle: Text(translations.settingsServerMirrorDescription),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Row( content: Row(
children: [ children: [
Expanded( Expanded(
child: TextFormBox( child: TextFormBox(
placeholder: translations.settingsServerMirrorPlaceholder, placeholder: translations.settingsServerMirrorPlaceholder,
controller: _dllController.url, controller: _dllController.url,
validator: _checkUpdateUrl onChanged: (value) {
if(Uri.tryParse(value) != null) {
_dllController.updateGameServerDll(force: true);
}
},
), ),
), ),
const SizedBox(width: 8.0), const SizedBox(width: 8.0),
@@ -351,7 +345,23 @@ class _HostingPageState extends RebootPageState<HostPage> {
style: ButtonStyle( style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero) padding: ButtonState.all(EdgeInsets.zero)
), ),
onPressed: () => _dllController.url.text = kRebootDownloadUrl, onPressed: () => _dllController.updateGameServerDll(force: true),
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_download_24_regular
),
)
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () {
_dllController.url.text = kRebootDownloadUrl;
_dllController.updateGameServerDll(force: true);
},
child: SizedBox.square( child: SizedBox.square(
dimension: 30, dimension: 30,
child: Icon( child: Icon(
@@ -362,8 +372,21 @@ class _HostingPageState extends RebootPageState<HostPage> {
], ],
) )
); );
}), }else {
Obx(() { return createFileSetting(
title: translations.settingsServerFileName,
description: translations.settingsServerFileDescription,
controller: _dllController.gameServerDll,
onReset: () {
final path = _dllController.getDefaultDllPath(InjectableDll.reboot);
_dllController.gameServerDll.text = path;
_dllController.downloadCriticalDllInteractive(path);
}
);
}
});
Widget get _internalFilesUpdateTimer => Obx(() {
if(_dllController.customGameServer.value) { if(_dllController.customGameServer.value) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
@@ -374,10 +397,8 @@ class _HostingPageState extends RebootPageState<HostPage> {
), ),
title: Text(translations.settingsServerTimerName), title: Text(translations.settingsServerTimerName),
subtitle: Text(translations.settingsServerTimerSubtitle), subtitle: Text(translations.settingsServerTimerSubtitle),
content: Row( contentWidth: SettingTile.kDefaultContentWidth + 30,
children: [ content: Obx(() => DropDownButton(
Expanded(
child: Obx(() => DropDownButton(
onOpen: () => inDialog = true, onOpen: () => inDialog = true,
onClose: () => inDialog = false, onClose: () => inDialog = false,
leading: Text(_dllController.timer.value.text), leading: Text(_dllController.timer.value.text),
@@ -391,38 +412,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
); );
} }
)).toList() )).toList()
)), ))
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () {
_dllController.updateGameServerDll(force: true);
},
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_download_24_regular
),
)
)
],
)
); );
}) });
],
);
String? _checkUpdateUrl(String? text) {
if (text == null || text.isEmpty) {
return translations.emptyURL;
}
return null;
}
SettingTile get _share => SettingTile( SettingTile get _share => SettingTile(
icon: Icon( icon: Icon(

View File

@@ -110,9 +110,9 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
contentWidth: null, contentWidth: null,
content: Row( content: Row(
children: [ children: [
Text( Obx(() => Text(
_settingsController.debug.value ? translations.on : translations.off _settingsController.debug.value ? translations.on : translations.off
), )),
const SizedBox( const SizedBox(
width: 16.0 width: 16.0
), ),

View File

@@ -6,3 +6,14 @@ extension IterableExtension<E> on Iterable<E> {
return null; return null;
} }
} }
extension StringExtension on String {
String? after(String leading) {
final index = indexOf(leading);
if(index == -1) {
return null;
}
return substring(index + leading.length);
}
}

View File

@@ -19,10 +19,10 @@ class FileSelector extends StatefulWidget {
required this.controller, required this.controller,
required this.validator, required this.validator,
required this.folder, required this.folder,
required this.allowNavigator,
this.label, this.label,
this.extension, this.extension,
this.validatorMode, this.validatorMode,
this.allowNavigator = true,
Key? key}) Key? key})
: assert(folder || extension != null, "Missing extension for file selector"), : assert(folder || extension != null, "Missing extension for file selector"),
super(key: key); super(key: key);

View File

@@ -1,21 +1,29 @@
import 'dart:io'; import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons; import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
import 'package:fluent_ui/fluent_ui.dart' as fluentIcons show FluentIcons;
import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:fluentui_system_icons/fluentui_system_icons.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.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/widget/file_selector.dart'; import 'package:reboot_launcher/src/widget/file_selector.dart';
import 'package:reboot_launcher/src/widget/setting_tile.dart'; import 'package:reboot_launcher/src/widget/setting_tile.dart';
const double _kButtonDimensions = 30;
const double _kButtonSpacing = 8;
SettingTile createFileSetting({required String title, required String description, required TextEditingController controller, required void Function() onReset}) { SettingTile createFileSetting({required String title, required String description, required TextEditingController controller, required void Function() onReset}) {
final obx = RxString(controller.text); final obx = RxString(controller.text);
controller.addListener(() => obx.value = controller.text); controller.addListener(() => obx.value = controller.text);
final selecting = RxBool(false);
return SettingTile( return SettingTile(
icon: Icon( icon: Icon(
FluentIcons.document_24_regular FluentIcons.document_24_regular
), ),
title: Text(title), title: Text(title),
subtitle: Text(description), subtitle: Text(description),
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions * 2 + _kButtonSpacing * 2,
content: Row( content: Row(
children: [ children: [
Expanded( Expanded(
@@ -26,30 +34,72 @@ SettingTile createFileSetting({required String title, required String descriptio
validator: _checkDll, validator: _checkDll,
extension: "dll", extension: "dll",
folder: false, folder: false,
validatorMode: AutovalidateMode.always validatorMode: AutovalidateMode.always,
allowNavigator: false,
), ),
), ),
const SizedBox(width: 8.0), const SizedBox(width: _kButtonSpacing),
Obx(() => Padding( Obx(() => Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0 bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
), ),
child: Tooltip(
message: translations.selectFile,
child: Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () => _onPressed(selecting, controller),
child: SizedBox.square(
dimension: _kButtonDimensions,
child: Icon(
fluentIcons.FluentIcons.open_folder_horizontal
),
)
),
),
)),
const SizedBox(width: _kButtonSpacing),
Obx(() => Padding(
padding: EdgeInsets.only(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
),
child: Tooltip(
message: translations.reset,
child: Button( child: Button(
style: ButtonStyle( style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero) padding: ButtonState.all(EdgeInsets.zero)
), ),
onPressed: onReset, onPressed: onReset,
child: SizedBox.square( child: SizedBox.square(
dimension: 30, dimension: _kButtonDimensions,
child: Icon( child: Icon(
FluentIcons.arrow_reset_24_regular FluentIcons.arrow_reset_24_regular
), ),
) )
), ),
),
)) ))
], ],
) )
); );
}
void _onPressed(RxBool selecting, TextEditingController controller) {
if(selecting.value){
return;
}
selecting.value = true;
compute(openFilePicker, "dll")
.then((value) => _updateText(controller, value))
.then((_) => selecting.value = false);
}
void _updateText(TextEditingController controller, String? value) {
final text = value ?? controller.text;
controller.text = text;
controller.selection = TextSelection.collapsed(offset: text.length);
} }
String? _checkDll(String? text) { String? _checkDll(String? text) {

View File

@@ -24,7 +24,9 @@ class InfoBarAreaState extends State<InfoBarArea> {
} }
@override @override
Widget build(BuildContext context) => Obx(() => Padding( Widget build(BuildContext context) => StreamBuilder(
stream: pagesController.stream,
builder: (context, _) => Obx(() => Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: hasPageButton ? 72.0 : 16.0 bottom: hasPageButton ? 72.0 : 16.0
), ),
@@ -38,5 +40,6 @@ class InfoBarAreaState extends State<InfoBarArea> {
child: child child: child
)).toList(growable: false) )).toList(growable: false)
), ),
)); ))
);
} }

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: "9.2.6" version: "9.2.7"
publish_to: 'none' publish_to: 'none'