diff --git a/gui/lib/l10n/reboot_en.arb b/gui/lib/l10n/reboot_en.arb index ade64df..9560ed0 100644 --- a/gui/lib/l10n/reboot_en.arb +++ b/gui/lib/l10n/reboot_en.arb @@ -145,6 +145,7 @@ "defaultServerName": "Reboot Game Server", "defaultServerDescription": "Just another server", "downloadingDll": "Downloading {name} dll...", + "dllAlreadyExists": "The {name} was already downloaded", "downloadDllSuccess": "The {name} dll was downloaded successfully", "downloadDllError": "An error occurred while downloading {name}: {error}", "downloadDllRetry": "Retry", @@ -360,7 +361,7 @@ "promptBackendDetachedActionLabel": "Next", "promptInfoTabText": "The Info tab contains useful links to report bugs and receive support", "promptInfoTabActionLabel": "Next", - "promptSettingsTabText": "The Settings tab contains options to customize and reset the launcher", + "promptSettingsTabText": "The Settings tab contains options to customize the launcher", "promptSettingsTabActionLabel": "Done", "automaticGameServerDialogContent": "The launcher detected that you are not running a game server, but that your matchmaker is set to your local machine. If you don't want to join another player's server, you should start a game server. This is necessary to be able to play!", "automaticGameServerDialogIgnore": "Ignore", diff --git a/gui/lib/src/controller/dll_controller.dart b/gui/lib/src/controller/dll_controller.dart index 3e3b0e8..fd67697 100644 --- a/gui/lib/src/controller/dll_controller.dart +++ b/gui/lib/src/controller/dll_controller.dart @@ -28,7 +28,6 @@ class DllController extends GetxController { late final TextEditingController url; late final RxBool customGameServer; late final RxnInt timestamp; - late final Map> _operations; late final Rx status; InfoBarEntry? infoBarEntry; Future? _updater; @@ -51,20 +50,19 @@ class DllController extends GetxController { customGameServer.listen((value) => _storage?.write("custom_game_server", value)); timestamp = RxnInt(_storage?.read("ts")); timestamp.listen((value) => _storage?.write("ts", value)); - _operations = {}; } TextEditingController _createController(String key, InjectableDll dll) { - final controller = TextEditingController(text: _storage?.read(key) ?? _getDefaultPath(dll)); + final controller = TextEditingController(text: _storage?.read(key) ?? getDefaultDllPath(dll)); controller.addListener(() => _storage?.write(key, controller.text)); return controller; } void resetGame() { - gameServerDll.text = _getDefaultPath(InjectableDll.reboot); - unrealEngineConsoleDll.text = _getDefaultPath(InjectableDll.console); - backendDll.text = _getDefaultPath(InjectableDll.cobalt); - memoryLeakDll.text = _getDefaultPath(InjectableDll.memory); + gameServerDll.text = getDefaultDllPath(InjectableDll.reboot); + unrealEngineConsoleDll.text = getDefaultDllPath(InjectableDll.console); + backendDll.text = getDefaultDllPath(InjectableDll.cobalt); + memoryLeakDll.text = getDefaultDllPath(InjectableDll.memory); } void resetServer() { @@ -147,7 +145,7 @@ class DllController extends GetxController { } (File, bool) getInjectableData(InjectableDll dll) { - final defaultPath = canonicalize(_getDefaultPath(dll)); + final defaultPath = canonicalize(getDefaultDllPath(dll)); switch(dll){ case InjectableDll.reboot: if(customGameServer.value) { @@ -170,23 +168,10 @@ class DllController extends GetxController { } } - String _getDefaultPath(InjectableDll dll) => "${dllsDirectory.path}\\${dll.name}.dll"; + String getDefaultDllPath(InjectableDll dll) => "${dllsDirectory.path}\\${dll.name}.dll"; - Future downloadCriticalDllInteractive(String filePath, {bool silent = false}) { + Future downloadCriticalDllInteractive(String filePath, {bool silent = false, bool force = false}) async { log("[DLL] Asking for $filePath(silent: $silent)"); - final old = _operations[filePath]; - if(old != null) { - log("[DLL] Download task already exists"); - return old; - } - - log("[DLL] Creating new download task..."); - final newRun = _downloadCriticalDllInteractive(filePath, silent); - _operations[filePath] = newRun; - return newRun; - } - - Future _downloadCriticalDllInteractive(String filePath, bool silent) async { final fileName = basename(filePath).toLowerCase(); log("[DLL] File name: $fileName"); InfoBarEntry? entry; @@ -198,7 +183,7 @@ class DllController extends GetxController { ); } - if(File(filePath).existsSync()) { + if(!force && File(filePath).existsSync()) { log("[DLL] File already exists"); return true; } @@ -225,7 +210,8 @@ class DllController extends GetxController { log("[DLL] Error: $message"); entry?.close(); var error = message.toString(); - error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error; + error = + error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error; error = error.toLowerCase(); final completer = Completer(); await showRebootInfoBar( @@ -243,8 +229,6 @@ class DllController extends GetxController { ); await completer.future; return false; - }finally { - _operations.remove(fileName); } } } diff --git a/gui/lib/src/controller/settings_controller.dart b/gui/lib/src/controller/settings_controller.dart index 862f4b2..4286310 100644 --- a/gui/lib/src/controller/settings_controller.dart +++ b/gui/lib/src/controller/settings_controller.dart @@ -21,6 +21,7 @@ class SettingsController extends GetxController { late final RxString language; late final Rx themeMode; late final RxBool firstRun; + late final RxBool debug; late double width; late double height; late double? offsetX; @@ -38,6 +39,7 @@ class SettingsController extends GetxController { language.listen((value) => _storage?.write("language", value)); firstRun = RxBool(_storage?.read("first_run_tutorial") ?? true); firstRun.listen((value) => _storage?.write("first_run_tutorial", value)); + debug = RxBool(false); } void saveWindowSize(Size size) { diff --git a/gui/lib/src/messenger/abstract/info_bar.dart b/gui/lib/src/messenger/abstract/info_bar.dart index 0887531..9f1dcc6 100644 --- a/gui/lib/src/messenger/abstract/info_bar.dart +++ b/gui/lib/src/messenger/abstract/info_bar.dart @@ -23,19 +23,18 @@ InfoBarEntry showRebootInfoBar(dynamic text, { Widget _buildOverlay(text, Widget? action, bool loading, InfoBarSeverity severity) => ConstrainedBox( constraints: BoxConstraints( - minWidth: double.infinity, - minHeight: _height + minHeight: _height ), child: Mica( elevation: 1, child: InfoBar( title: Row( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - if(text is Widget) - text, - if(text is String) - Text(text), + Expanded( + child: text is Widget ? text : Text(text) + ), if(action != null) action ], diff --git a/gui/lib/src/page/abstract/page.dart b/gui/lib/src/page/abstract/page.dart index f0aabe6..69350db 100644 --- a/gui/lib/src/page/abstract/page.dart +++ b/gui/lib/src/page/abstract/page.dart @@ -1,5 +1,10 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:get/get.dart'; +import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; +import 'package:reboot_launcher/src/controller/settings_controller.dart'; +import 'package:reboot_launcher/src/messenger/implementation/onboard.dart'; import 'package:reboot_launcher/src/page/abstract/page_type.dart'; +import 'package:reboot_launcher/src/util/translations.dart'; abstract class RebootPage extends StatefulWidget { const RebootPage({super.key}); @@ -19,32 +24,110 @@ abstract class RebootPage extends StatefulWidget { } abstract class RebootPageState extends State with AutomaticKeepAliveClientMixin { + final SettingsController _settingsController = Get.find(); + @override Widget build(BuildContext context) { super.build(context); var buttonWidget = button; if(buttonWidget == null) { - return _listView; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildFirstLaunchInfo(), + _buildDebugInfo(), + Expanded( + child: _listView + ) + ], + ); } return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + _buildFirstLaunchInfo(), + _buildDebugInfo(), Expanded( - child: _listView, + child: Column( + children: [ + Expanded( + child: _listView, + ), + const SizedBox( + height: 8.0, + ), + ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 1000 + ), + child: buttonWidget + ) + ], + ), ), - const SizedBox( - height: 8.0, - ), - ConstrainedBox( - constraints: const BoxConstraints( - maxWidth: 1000 - ), - child: buttonWidget - ) ], ); } + Widget _buildFirstLaunchInfo() => Obx(() { + if(!_settingsController.firstRun.value) { + return const SizedBox.shrink(); + } + + return Padding( + padding: const EdgeInsets.only( + bottom: 8.0 + ), + child: SizedBox( + width: double.infinity, + child: InfoBar( + title: Text(translations.welcomeTitle), + severity: InfoBarSeverity.warning, + isLong: true, + content: SizedBox( + width: double.infinity, + child: Text(translations.welcomeDescription) + ), + action: Button( + child: Text(translations.welcomeAction), + onPressed: () => startOnboarding(), + ), + onClose: () => _settingsController.firstRun.value = false + ), + ), + ); + }); + + Widget _buildDebugInfo() => Obx(() { + if(!_settingsController.debug.value) { + return const SizedBox.shrink(); + } + + return Padding( + padding: const EdgeInsets.only( + bottom: 8.0 + ), + child: SizedBox( + width: double.infinity, + child: InfoBar( + title: Text("Debug mode is enabled"), + severity: InfoBarSeverity.warning, + isLong: true, + content: SizedBox( + width: double.infinity, + child: Text( "• Automatic dll injection is disabled\n" + "• The game server cannot start automatically\n" + "• The game server runs in a normal window") + ), + onClose: () { + _settingsController.debug.value = false; + }, + ), + ) + ); + }); + ListView get _listView => ListView.builder( itemCount: settings.length, itemBuilder: (context, index) => settings[index], diff --git a/gui/lib/src/page/implementation/host_page.dart b/gui/lib/src/page/implementation/host_page.dart index 5a508eb..351b14f 100644 --- a/gui/lib/src/page/implementation/host_page.dart +++ b/gui/lib/src/page/implementation/host_page.dart @@ -221,11 +221,12 @@ class _HostingPageState extends RebootPageState { content: Obx(() => DropDownButton( onOpen: () => inDialog = true, onClose: () => inDialog = false, - leading: Text(_hostingController.type.value.translatedName), + leading: Text(_settingsController.debug.value ? GameServerType.window.translatedName : _hostingController.type.value.translatedName), items: GameServerType.values.map((entry) => MenuFlyoutItem( text: Text(entry.translatedName), onPressed: () => _hostingController.type.value = entry - )).toList() + )).toList(), + disabled: _settingsController.debug.value )), ), SettingTile( @@ -317,7 +318,12 @@ class _HostingPageState extends RebootPageState { return createFileSetting( title: translations.settingsServerFileName, description: translations.settingsServerFileDescription, - controller: _dllController.gameServerDll + controller: _dllController.gameServerDll, + onReset: () { + final path = _dllController.getDefaultDllPath(InjectableDll.reboot); + _dllController.gameServerDll.text = path; + _dllController.downloadCriticalDllInteractive(path); + } ); }), Obx(() { @@ -331,10 +337,29 @@ class _HostingPageState extends RebootPageState { ), title: Text(translations.settingsServerMirrorName), subtitle: Text(translations.settingsServerMirrorDescription), - content: TextFormBox( - placeholder: translations.settingsServerMirrorPlaceholder, - controller: _dllController.url, - validator: _checkUpdateUrl + 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 + ), + ) + ) + ], ) ); }), @@ -343,29 +368,50 @@ class _HostingPageState extends RebootPageState { return const SizedBox.shrink(); } - return SettingTile( + return SettingTile( icon: Icon( FluentIcons.timer_24_regular ), title: Text(translations.settingsServerTimerName), subtitle: Text(translations.settingsServerTimerSubtitle), - 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() - )) + 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); + }, + child: SizedBox.square( + dimension: 30, + child: Icon( + FluentIcons.arrow_download_24_regular + ), + ) + ) + ], + ) ); - }), + }) ], ); diff --git a/gui/lib/src/page/implementation/play_page.dart b/gui/lib/src/page/implementation/play_page.dart index b7d983b..e26e7da 100644 --- a/gui/lib/src/page/implementation/play_page.dart +++ b/gui/lib/src/page/implementation/play_page.dart @@ -1,6 +1,7 @@ import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; import 'package:get/get.dart'; +import 'package:reboot_common/common.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/settings_controller.dart'; @@ -40,48 +41,6 @@ class _PlayPageState extends RebootPageState { final SettingsController _settingsController = Get.find(); final GameController _gameController = Get.find(); final DllController _dllController = Get.find(); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildFirstLaunchInfo(), - Expanded( - child: super.build(context), - ) - ], - ); - } - - Widget _buildFirstLaunchInfo() => Obx(() { - if(!_settingsController.firstRun.value) { - return const SizedBox.shrink(); - } - - return Padding( - padding: const EdgeInsets.only( - bottom: 8.0 - ), - child: SizedBox( - width: double.infinity, - child: InfoBar( - title: Text(translations.welcomeTitle), - severity: InfoBarSeverity.warning, - isLong: true, - content: SizedBox( - width: double.infinity, - child: Text(translations.welcomeDescription) - ), - action: Button( - child: Text(translations.welcomeAction), - onPressed: () => startOnboarding(), - ), - onClose: () => _settingsController.firstRun.value = false - ), - ), - ); - }); @override Widget? get button => LaunchButton( @@ -110,17 +69,32 @@ class _PlayPageState extends RebootPageState { createFileSetting( title: translations.settingsClientConsoleName, description: translations.settingsClientConsoleDescription, - controller: _dllController.unrealEngineConsoleDll + controller: _dllController.unrealEngineConsoleDll, + onReset: () { + final path = _dllController.getDefaultDllPath(InjectableDll.console); + _dllController.unrealEngineConsoleDll.text = path; + _dllController.downloadCriticalDllInteractive(path, force: true); + } ), createFileSetting( title: translations.settingsClientAuthName, description: translations.settingsClientAuthDescription, - controller: _dllController.backendDll + controller: _dllController.backendDll, + onReset: () { + final path = _dllController.getDefaultDllPath(InjectableDll.cobalt); + _dllController.backendDll.text = path; + _dllController.downloadCriticalDllInteractive(path, force: true); + } ), createFileSetting( title: translations.settingsClientMemoryName, description: translations.settingsClientMemoryDescription, - controller: _dllController.memoryLeakDll + controller: _dllController.memoryLeakDll, + onReset: () { + final path = _dllController.getDefaultDllPath(InjectableDll.memory); + _dllController.memoryLeakDll.text = path; + _dllController.downloadCriticalDllInteractive(path, force: true); + } ), ], ); diff --git a/gui/lib/src/page/implementation/settings_page.dart b/gui/lib/src/page/implementation/settings_page.dart index 7f18cc5..9d50a42 100644 --- a/gui/lib/src/page/implementation/settings_page.dart +++ b/gui/lib/src/page/implementation/settings_page.dart @@ -42,7 +42,8 @@ class _SettingsPageState extends RebootPageState { List get settings => [ _language, _theme, - _installationDirectory + _debugMode, + _installationDirectory, ]; SettingTile get _language => SettingTile( @@ -99,6 +100,29 @@ class _SettingsPageState extends RebootPageState { child: Text(translations.settingsUtilsInstallationDirectoryContent), ) ); + + SettingTile get _debugMode => SettingTile( + icon: Icon( + FluentIcons.developer_board_24_regular + ), + title: Text("Debug mode"), + subtitle: Text("Whether the launcher should disable automatic features for troubleshooting"), + contentWidth: null, + content: Row( + children: [ + Text( + _settingsController.debug.value ? translations.on : translations.off + ), + const SizedBox( + width: 16.0 + ), + Obx(() => ToggleSwitch( + checked: _settingsController.debug.value, + onChanged: (value) => _settingsController.debug.value = value + )) + ], + ) + ); } extension _ThemeModeExtension on ThemeMode { diff --git a/gui/lib/src/util/matchmaker.dart b/gui/lib/src/util/matchmaker.dart index 872efa4..23349ae 100644 --- a/gui/lib/src/util/matchmaker.dart +++ b/gui/lib/src/util/matchmaker.dart @@ -36,7 +36,6 @@ Future pingGameServer(String address, {Duration? timeout}) async { } } - final start = DateTime.now(); var firstTime = true; final split = address.split(":"); diff --git a/gui/lib/src/widget/file_setting_tile.dart b/gui/lib/src/widget/file_setting_tile.dart index c2cf55b..8d33e6b 100644 --- a/gui/lib/src/widget/file_setting_tile.dart +++ b/gui/lib/src/widget/file_setting_tile.dart @@ -2,26 +2,55 @@ import 'dart:io'; import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons; import 'package:fluentui_system_icons/fluentui_system_icons.dart'; +import 'package:get/get.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'; -SettingTile createFileSetting({required String title, required String description, required TextEditingController controller}) => SettingTile( +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: FileSelector( - placeholder: translations.selectPathPlaceholder, - windowTitle: translations.selectPathWindowTitle, - controller: controller, - validator: _checkDll, - extension: "dll", - folder: false, - validatorMode: AutovalidateMode.always + content: Row( + children: [ + Expanded( + child: FileSelector( + placeholder: translations.selectPathPlaceholder, + windowTitle: translations.selectPathWindowTitle, + controller: controller, + validator: _checkDll, + extension: "dll", + folder: false, + validatorMode: AutovalidateMode.always + ), + ), + const SizedBox(width: 8.0), + Obx(() => Padding( + padding: EdgeInsets.only( + bottom: _checkDll(obx.value) == null ? 0.0 : 20.0 + ), + child: Button( + style: ButtonStyle( + padding: ButtonState.all(EdgeInsets.zero) + ), + onPressed: onReset, + child: SizedBox.square( + dimension: 30, + child: Icon( + FluentIcons.arrow_reset_24_regular + ), + ) + ), + )) + ], ) ); +} String? _checkDll(String? text) { if (text == null || text.isEmpty) { diff --git a/gui/lib/src/widget/game_start_button.dart b/gui/lib/src/widget/game_start_button.dart index 1895d84..19ba49e 100644 --- a/gui/lib/src/widget/game_start_button.dart +++ b/gui/lib/src/widget/game_start_button.dart @@ -41,10 +41,12 @@ class _LaunchButtonState extends State { final HostingController _hostingController = Get.find(); final BackendController _backendController = Get.find(); final DllController _dllController = Get.find(); + final SettingsController _settingsController = Get.find(); InfoBarEntry? _gameClientInfoBar; InfoBarEntry? _gameServerInfoBar; CancelableOperation? _operation; + CancelableOperation? _pingOperation; IVirtualDesktop? _virtualDesktop; @override @@ -94,10 +96,6 @@ class _LaunchButtonState extends State { log("[${host ? 'HOST' : 'GAME'}] Checking dlls: ${InjectableDll.values}"); for (final injectable in InjectableDll.values) { if(await _getDllFileOrStop(injectable, host) == null) { - _onStop( - reason: _StopReason.missingCustomDllError, - error: injectable.name, - ); return; } } @@ -123,7 +121,7 @@ class _LaunchButtonState extends State { return; } log("[${host ? 'HOST' : 'GAME'}] Backend works"); - final serverType = _hostingController.type.value; + final serverType = _settingsController.debug.value ? GameServerType.window : _hostingController.type.value; log("[${host ? 'HOST' : 'GAME'}] Implicit game server metadata: headless($serverType)"); final linkedHostingInstance = await _startMatchMakingServer(version, host, serverType, false); log("[${host ? 'HOST' : 'GAME'}] Implicit game server result: $linkedHostingInstance"); @@ -139,6 +137,12 @@ class _LaunchButtonState extends State { }else { _showLaunchingGameServerWidget(); } + } on ProcessException catch (exception, stackTrace) { + _onStop( + reason: _StopReason.corruptedVersionError, + error: exception.toString(), + stackTrace: stackTrace + ); } catch (exception, stackTrace) { _onStop( reason: _StopReason.unknownError, @@ -155,6 +159,11 @@ class _LaunchButtonState extends State { return null; } + if(_settingsController.debug.value) { + log("[${host ? 'HOST' : 'GAME'}] The user is on debug mode, not asking for auto server"); + return null; + } + if(!forceLinkedHosting && _backendController.type.value == ServerType.embedded && !isLocalHost(_backendController.gameServerAddress.text)) { log("[${host ? 'HOST' : 'GAME'}] Backend is not set to embedded and/or not pointing to the local game server"); return null; @@ -271,9 +280,17 @@ class _LaunchButtonState extends State { line: line, host: host, onShutdown: () => _onStop(reason: _StopReason.normal), - onTokenError: () => _onStop(reason: _StopReason.tokenError), + onTokenError: () { + if(_settingsController.debug.value) { + log("[PROCESS] Ignoring token error because debug mode is on"); + }else { + _onStop(reason: _StopReason.tokenError); + } + }, onBuildCorrupted: () { - if(instance?.launched == false) { + if(instance == null) { + return; + }else if(!instance.launched) { _onStop(reason: _StopReason.corruptedVersionError); }else { _onStop(reason: _StopReason.crash); @@ -432,10 +449,11 @@ class _LaunchButtonState extends State { duration: null ); final gameServerPort = _dllController.gameServerPort.text; - final localPingResult = await pingGameServer( + this._pingOperation = await CancelableOperation.fromFuture(pingGameServer( "127.0.0.1:$gameServerPort", timeout: const Duration(minutes: 2) - ); + )); + final localPingResult = (await _pingOperation?.value) ?? false; _gameServerInfoBar?.close(); if (!localPingResult) { showRebootInfoBar( @@ -478,16 +496,18 @@ class _LaunchButtonState extends State { duration: null ); final publicIp = await Ipify.ipv4(); - final externalResult = await pingGameServer("$publicIp:$gameServerPort"); + this._pingOperation = CancelableOperation.fromFuture(pingGameServer("$publicIp:$gameServerPort")); + final externalResult = (await _pingOperation?.value) ?? false; if (externalResult) { return true; } _gameServerInfoBar?.close(); - final future = pingGameServer( + this._pingOperation = CancelableOperation.fromFuture(pingGameServer( "$publicIp:$gameServerPort", timeout: const Duration(days: 365) - ); + )); + final future = await _pingOperation?.value ?? false; _gameServerInfoBar = showRebootInfoBar( translations.checkGameServerFixMessage(gameServerPort), action: Button( @@ -506,6 +526,8 @@ class _LaunchButtonState extends State { Future _onStop({required _StopReason reason, bool? host, String? error, StackTrace? stackTrace}) async { if(host == null) { + await _pingOperation?.cancel(); + _pingOperation = null; await _operation?.cancel(); _operation = null; _backendController.cancelInteractive(); @@ -513,6 +535,10 @@ class _LaunchButtonState extends State { host = host ?? widget.host; final instance = host ? _hostingController.instance.value : _gameController.instance.value; + if(instance == null) { + return; + } + if(host){ _hostingController.instance.value = null; }else { @@ -534,19 +560,17 @@ class _LaunchButtonState extends State { _hostingController.discardServer(); } - if(instance != null) { - if(reason == _StopReason.normal) { - instance.launched = true; - } + if(reason == _StopReason.normal) { + instance.launched = true; + } - instance.kill(); - final child = instance.child; - if(child != null) { - await _onStop( - reason: reason, - host: child.serverType != null - ); - } + instance.kill(); + final child = instance.child; + if(child != null) { + await _onStop( + reason: reason, + host: child.serverType != null + ); } _setStarted(host, false); @@ -578,7 +602,7 @@ class _LaunchButtonState extends State { ); break; case _StopReason.exitCode: - if(instance != null && !instance.launched) { + if(!instance.launched) { showRebootInfoBar( translations.corruptedVersionError, severity: InfoBarSeverity.error, @@ -614,7 +638,7 @@ class _LaunchButtonState extends State { case _StopReason.tokenError: _backendController.stop(); showRebootInfoBar( - translations.tokenError(instance?.injectedDlls.map((element) => element.name).join(", ") ?? translations.none), + translations.tokenError(instance.injectedDlls.map((element) => element.name).join(", ")), severity: InfoBarSeverity.error, duration: infoBarLongDuration, action: Button( @@ -663,6 +687,10 @@ class _LaunchButtonState extends State { } log("[${hosting ? 'HOST' : 'GAME'}] Trying to inject ${injectable.name}..."); + if(_settingsController.debug.value) { + return; + } + await injectDll(gameProcess, dllPath); instance.injectedDlls.add(injectable); log("[${hosting ? 'HOST' : 'GAME'}] Injected ${injectable.name}"); @@ -687,13 +715,17 @@ class _LaunchButtonState extends State { } log("[${host ? 'HOST' : 'GAME'}] Path doesn't exist"); - if(customDll || isRetry) { + if(customDll) { log("[${host ? 'HOST' : 'GAME'}] Custom dll -> no recovery"); + _onStop( + reason: _StopReason.missingCustomDllError, + error: injectable.name, + ); return null; } log("[${host ? 'HOST' : 'GAME'}] Path does not exist, downloading critical dll again..."); - await _dllController.downloadCriticalDllInteractive(file.path); + await _dllController.downloadCriticalDllInteractive(file.path, force: true); log("[${host ? 'HOST' : 'GAME'}] Downloaded dll again, retrying check..."); return _getDllFileOrStop(injectable, host, true); } @@ -710,7 +742,7 @@ class _LaunchButtonState extends State { loading: true, duration: null, action: Obx(() { - if(_hostingController.started.value || linkedHosting) { + if(_settingsController.debug.value || _hostingController.started.value || linkedHosting) { return const SizedBox.shrink(); } diff --git a/gui/pubspec.yaml b/gui/pubspec.yaml index 87c4c42..5f5bafb 100644 --- a/gui/pubspec.yaml +++ b/gui/pubspec.yaml @@ -1,6 +1,6 @@ name: reboot_launcher description: Graphical User Interface for Project Reboot -version: "9.2.5" +version: "9.2.6" publish_to: 'none'