Released 9.2.6

This commit is contained in:
Alessandro Autiero
2024-09-12 15:46:24 +02:00
parent 4c3fe9bc65
commit a9af28273a
12 changed files with 328 additions and 155 deletions

View File

@@ -28,7 +28,6 @@ class DllController extends GetxController {
late final TextEditingController url;
late final RxBool customGameServer;
late final RxnInt timestamp;
late final Map<String, Future<bool>> _operations;
late final Rx<UpdateStatus> status;
InfoBarEntry? infoBarEntry;
Future<bool>? _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<bool> downloadCriticalDllInteractive(String filePath, {bool silent = false}) {
Future<bool> 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<bool> _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);
}
}
}

View File

@@ -21,6 +21,7 @@ class SettingsController extends GetxController {
late final RxString language;
late final Rx<ThemeMode> 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) {

View File

@@ -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
],

View File

@@ -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<T extends RebootPage> extends State<T> with AutomaticKeepAliveClientMixin<T> {
final SettingsController _settingsController = Get.find<SettingsController>();
@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],

View File

@@ -221,11 +221,12 @@ class _HostingPageState extends RebootPageState<HostPage> {
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<HostPage> {
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<HostPage> {
),
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<HostPage> {
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
),
)
)
],
)
);
}),
})
],
);

View File

@@ -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<PlayPage> {
final SettingsController _settingsController = Get.find<SettingsController>();
final GameController _gameController = Get.find<GameController>();
final DllController _dllController = Get.find<DllController>();
@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<PlayPage> {
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);
}
),
],
);

View File

@@ -42,7 +42,8 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
List<Widget> get settings => [
_language,
_theme,
_installationDirectory
_debugMode,
_installationDirectory,
];
SettingTile get _language => SettingTile(
@@ -99,6 +100,29 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
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 {

View File

@@ -36,7 +36,6 @@ Future<bool> pingGameServer(String address, {Duration? timeout}) async {
}
}
final start = DateTime.now();
var firstTime = true;
final split = address.split(":");

View File

@@ -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) {

View File

@@ -41,10 +41,12 @@ class _LaunchButtonState extends State<LaunchButton> {
final HostingController _hostingController = Get.find<HostingController>();
final BackendController _backendController = Get.find<BackendController>();
final DllController _dllController = Get.find<DllController>();
final SettingsController _settingsController = Get.find<SettingsController>();
InfoBarEntry? _gameClientInfoBar;
InfoBarEntry? _gameServerInfoBar;
CancelableOperation? _operation;
CancelableOperation? _pingOperation;
IVirtualDesktop? _virtualDesktop;
@override
@@ -94,10 +96,6 @@ class _LaunchButtonState extends State<LaunchButton> {
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<LaunchButton> {
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<LaunchButton> {
}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<LaunchButton> {
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<LaunchButton> {
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<LaunchButton> {
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<LaunchButton> {
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<LaunchButton> {
Future<void> _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<LaunchButton> {
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<LaunchButton> {
_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<LaunchButton> {
);
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<LaunchButton> {
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<LaunchButton> {
}
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<LaunchButton> {
}
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<LaunchButton> {
loading: true,
duration: null,
action: Obx(() {
if(_hostingController.started.value || linkedHosting) {
if(_settingsController.debug.value || _hostingController.started.value || linkedHosting) {
return const SizedBox.shrink();
}