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

@@ -368,5 +368,7 @@
"automaticGameServerDialogStart": "Start server",
"gameResetDefaultsName": "Reset",
"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
);
}
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;
infoBarEntry?.close();
if(!silent) {
@@ -126,15 +128,18 @@ class DllController extends GetxController {
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
error = error.toLowerCase();
status.value = UpdateStatus.error;
showRebootInfoBar(
translations.downloadDllError("reboot.dll", error.toString()),
infoBarEntry = showRebootInfoBar(
translations.downloadDllError(error.toString(), "reboot.dll"),
duration: infoBarLongDuration,
severity: InfoBarSeverity.error,
action: Button(
onPressed: () => updateGameServerDll(
force: true,
silent: silent
),
onPressed: () async {
infoBarEntry?.close();
updateGameServerDll(
force: true,
silent: silent
);
},
child: Text(translations.downloadDllRetry),
)
);
@@ -215,7 +220,7 @@ class DllController extends GetxController {
error = error.toLowerCase();
final completer = Completer();
await showRebootInfoBar(
translations.downloadDllError(fileName, error.toString()),
translations.downloadDllError(error.toString(), fileName),
duration: infoBarLongDuration,
severity: InfoBarSeverity.error,
onDismissed: () => completer.complete(null),

View File

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

View File

@@ -5,10 +5,13 @@ import 'dart:isolate';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:reboot_common/common.dart';
import 'package:reboot_launcher/src/controller/game_controller.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/types.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';
@@ -41,6 +44,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
SendPort? _downloadPort;
Object? _error;
StackTrace? _stackTrace;
bool _selecting = false;
@override
void initState() {
@@ -102,7 +106,12 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
return ErrorDialog(
exception: _error ?? Exception(translations.unknownError),
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:
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,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -292,7 +302,8 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
windowTitle: _source.value == _BuildSource.local ? translations.gameFolderPlaceWindowTitle : translations.buildInstallationDirectoryWindowTitle,
controller: _pathController,
validator: _source.value == _BuildSource.local ? _checkGameFolder : _checkDownloadDestination,
folder: true
folder: true,
allowNavigator: true
),
const SizedBox(
@@ -300,6 +311,7 @@ class _AddVersionDialogState extends State<AddVersionDialog> {
)
],
);
}
String? _checkGameFolder(text) {
if (text == null || text.isEmpty) {

View File

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

View File

@@ -298,48 +298,46 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
});
}
Widget _buildBody() {
return Expanded(
child: Padding(
padding: EdgeInsets.only(
left: HomePage.kDefaultPadding,
right: HomePage.kDefaultPadding * 2,
top: HomePage.kDefaultPadding,
bottom: HomePage.kDefaultPadding * 2
),
child: Column(
children: [
Expanded(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 1000
),
child: Center(
child: Column(
children: [
_buildBodyHeader(),
const SizedBox(height: 24.0),
Expanded(
child: Stack(
fit: StackFit.loose,
children: [
_buildBodyContent(),
InfoBarArea(
key: infoBarAreaKey
)
],
)
),
],
),
Widget _buildBody() => Expanded(
child: Padding(
padding: EdgeInsets.only(
left: HomePage.kDefaultPadding,
right: HomePage.kDefaultPadding * 2,
top: HomePage.kDefaultPadding,
bottom: HomePage.kDefaultPadding * 2
),
child: Column(
children: [
Expanded(
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 1000
),
child: Center(
child: Column(
children: [
_buildBodyHeader(),
const SizedBox(height: 24.0),
Expanded(
child: Stack(
fit: StackFit.loose,
children: [
_buildBodyContent(),
InfoBarArea(
key: infoBarAreaKey
)
],
)
),
],
),
),
)
],
)
),
);
}
),
)
],
)
),
);
Widget _buildBodyContent() => PageView.builder(
controller: _pageController,

View File

@@ -82,7 +82,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
List<Widget> get settings => [
_information,
buildVersionSelector(
key: hostVersionOverlayTargetKey
key: hostVersionOverlayTargetKey
),
_options,
_internalFiles,
@@ -175,9 +175,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null,
content: Obx(() => Row(
children: [
Text(
Obx(() => Text(
_hostingController.discoverable.value ? translations.on : translations.off
),
)),
const SizedBox(
width: 16.0
),
@@ -226,7 +226,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
text: Text(entry.translatedName),
onPressed: () => _hostingController.type.value = entry
)).toList(),
disabled: _settingsController.debug.value
disabled: _settingsController.debug.value
)),
),
SettingTile(
@@ -238,9 +238,9 @@ class _HostingPageState extends RebootPageState<HostPage> {
contentWidth: null,
content: Row(
children: [
Text(
Obx(() => Text(
_hostingController.autoRestart.value ? translations.on : translations.off
),
)),
const SizedBox(
width: 16.0
),
@@ -278,151 +278,143 @@ class _HostingPageState extends RebootPageState<HostPage> {
title: Text(translations.settingsServerName),
subtitle: Text(translations.settingsServerSubtitle),
children: [
SettingTile(
icon: Icon(
FluentIcons.timer_24_regular
),
title: Text(translations.settingsServerTypeName),
subtitle: Text(translations.settingsServerTypeDescription),
content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
items: {
false: translations.settingsServerTypeEmbeddedName,
true: translations.settingsServerTypeCustomName
}.entries.map((entry) => MenuFlyoutItem(
text: Text(entry.value),
onPressed: () {
final oldValue = _dllController.customGameServer.value;
if(oldValue == entry.key) {
return;
}
_internalFilesServerType,
_internalFilesUpdateTimer,
_internalFilesServerSource
],
);
_dllController.customGameServer.value = entry.key;
_dllController.infoBarEntry?.close();
if(!entry.key) {
_dllController.updateGameServerDll(
force: true
);
}
}
)).toList()
))
Widget get _internalFilesServerType => SettingTile(
icon: Icon(
FluentIcons.games_24_regular
),
Obx(() {
if(!_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
title: Text(translations.settingsServerTypeName),
subtitle: Text(translations.settingsServerTypeDescription),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
items: {
false: translations.settingsServerTypeEmbeddedName,
true: translations.settingsServerTypeCustomName
}.entries.map((entry) => MenuFlyoutItem(
text: Text(entry.value),
onPressed: () {
final oldValue = _dllController.customGameServer.value;
if(oldValue == entry.key) {
return;
}
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();
}
_dllController.customGameServer.value = entry.key;
_dllController.infoBarEntry?.close();
if(!entry.key) {
_dllController.updateGameServerDll(
force: true
);
}
}
)).toList()
))
);
return SettingTile(
icon: Icon(
FluentIcons.globe_24_regular
),
title: Text(translations.settingsServerMirrorName),
subtitle: Text(translations.settingsServerMirrorDescription),
content: Row(
children: [
Expanded(
child: TextFormBox(
placeholder: translations.settingsServerMirrorPlaceholder,
controller: _dllController.url,
validator: _checkUpdateUrl
),
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () => _dllController.url.text = kRebootDownloadUrl,
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_reset_24_regular
),
)
)
],
)
);
}),
Obx(() {
if(_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
return SettingTile(
icon: Icon(
FluentIcons.timer_24_regular
),
title: Text(translations.settingsServerTimerName),
subtitle: Text(translations.settingsServerTimerSubtitle),
content: Row(
children: [
Expanded(
child: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.timer.value.text),
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
text: Text(entry.text),
onPressed: () {
_dllController.timer.value = entry;
_dllController.infoBarEntry?.close();
_dllController.updateGameServerDll(
force: true
);
}
)).toList()
)),
),
const SizedBox(width: 8.0),
Button(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: () {
_dllController.updateGameServerDll(force: true);
Widget get _internalFilesServerSource => Obx(() {
if(!_dllController.customGameServer.value) {
return SettingTile(
icon: Icon(
FluentIcons.globe_24_regular
),
title: Text(translations.settingsServerMirrorName),
subtitle: Text(translations.settingsServerMirrorDescription),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Row(
children: [
Expanded(
child: TextFormBox(
placeholder: translations.settingsServerMirrorPlaceholder,
controller: _dllController.url,
onChanged: (value) {
if(Uri.tryParse(value) != null) {
_dllController.updateGameServerDll(force: true);
}
},
),
),
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
),
)
)
],
)
);
})
],
);
),
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(
dimension: 30,
child: Icon(
FluentIcons.arrow_reset_24_regular
),
)
)
],
)
);
}else {
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);
}
);
}
});
String? _checkUpdateUrl(String? text) {
if (text == null || text.isEmpty) {
return translations.emptyURL;
Widget get _internalFilesUpdateTimer => Obx(() {
if(_dllController.customGameServer.value) {
return const SizedBox.shrink();
}
return null;
}
return SettingTile(
icon: Icon(
FluentIcons.timer_24_regular
),
title: Text(translations.settingsServerTimerName),
subtitle: Text(translations.settingsServerTimerSubtitle),
contentWidth: SettingTile.kDefaultContentWidth + 30,
content: Obx(() => DropDownButton(
onOpen: () => inDialog = true,
onClose: () => inDialog = false,
leading: Text(_dllController.timer.value.text),
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
text: Text(entry.text),
onPressed: () {
_dllController.timer.value = entry;
_dllController.infoBarEntry?.close();
_dllController.updateGameServerDll(
force: true
);
}
)).toList()
))
);
});
SettingTile get _share => SettingTile(
icon: Icon(

View File

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

View File

@@ -5,4 +5,15 @@ extension IterableExtension<E> on Iterable<E> {
}
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.validator,
required this.folder,
required this.allowNavigator,
this.label,
this.extension,
this.validatorMode,
this.allowNavigator = true,
Key? key})
: assert(folder || extension != null, "Missing extension for file selector"),
super(key: key);

View File

@@ -1,70 +1,120 @@
import 'dart:io';
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:flutter/foundation.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/widget/file_selector.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}) {
final obx = RxString(controller.text);
controller.addListener(() => obx.value = controller.text);
return SettingTile(
icon: Icon(
FluentIcons.document_24_regular
),
title: Text(title),
subtitle: Text(description),
content: Row(
final selecting = RxBool(false);
return SettingTile(
icon: Icon(
FluentIcons.document_24_regular
),
title: Text(title),
subtitle: Text(description),
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions * 2 + _kButtonSpacing * 2,
content: Row(
children: [
Expanded(
child: FileSelector(
placeholder: translations.selectPathPlaceholder,
windowTitle: translations.selectPathWindowTitle,
controller: controller,
validator: _checkDll,
extension: "dll",
folder: false,
validatorMode: AutovalidateMode.always
),
Expanded(
child: FileSelector(
placeholder: translations.selectPathPlaceholder,
windowTitle: translations.selectPathWindowTitle,
controller: controller,
validator: _checkDll,
extension: "dll",
folder: false,
validatorMode: AutovalidateMode.always,
allowNavigator: false,
),
const SizedBox(width: 8.0),
Obx(() => Padding(
padding: EdgeInsets.only(
bottom: _checkDll(obx.value) == null ? 0.0 : 20.0
),
const SizedBox(width: _kButtonSpacing),
Obx(() => Padding(
padding: EdgeInsets.only(
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(
style: ButtonStyle(
padding: ButtonState.all(EdgeInsets.zero)
),
onPressed: onReset,
child: SizedBox.square(
dimension: 30,
child: Icon(
FluentIcons.arrow_reset_24_regular
),
dimension: _kButtonDimensions,
child: Icon(
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) {
if (text == null || text.isEmpty) {
return translations.invalidDllPath;
}
if (text == null || text.isEmpty) {
return translations.invalidDllPath;
}
final file = File(text);
if (!file.existsSync()) {
return translations.dllDoesNotExist;
}
final file = File(text);
if (!file.existsSync()) {
return translations.dllDoesNotExist;
}
if (!text.endsWith(".dll")) {
return translations.invalidDllExtension;
}
if (!text.endsWith(".dll")) {
return translations.invalidDllExtension;
}
return null;
return null;
}

View File

@@ -24,19 +24,22 @@ class InfoBarAreaState extends State<InfoBarArea> {
}
@override
Widget build(BuildContext context) => Obx(() => Padding(
padding: EdgeInsets.only(
bottom: hasPageButton ? 72.0 : 16.0
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: _children.value.map((child) => Padding(
padding: EdgeInsets.only(
top: 12.0
),
child: child
)).toList(growable: false)
),
));
Widget build(BuildContext context) => StreamBuilder(
stream: pagesController.stream,
builder: (context, _) => Obx(() => Padding(
padding: EdgeInsets.only(
bottom: hasPageButton ? 72.0 : 16.0
),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: _children.value.map((child) => Padding(
padding: EdgeInsets.only(
top: 12.0
),
child: child
)).toList(growable: false)
),
))
);
}

View File

@@ -1,6 +1,6 @@
name: reboot_launcher
description: Graphical User Interface for Project Reboot
version: "9.2.6"
version: "9.2.7"
publish_to: 'none'