mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
Released 9.2.5
This commit is contained in:
@@ -35,7 +35,7 @@ express.use(require("./structure/matchmaking.js"));
|
||||
express.use(require("./structure/cloudstorage.js"));
|
||||
express.use(require("./structure/mcp.js"));
|
||||
|
||||
const port = process.env.PORT || 3551;
|
||||
const port = 3551;
|
||||
express.listen(port, () => {
|
||||
console.log("LawinServer started listening on port", port);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const List<String> kCorruptedBuildErrors = [
|
||||
"Critical error",
|
||||
"when 0 bytes remain",
|
||||
"Pak chunk signature verification failed!",
|
||||
"Couldn't find pak signature file"
|
||||
"LogWindows:Error: Fatal error!"
|
||||
];
|
||||
const List<String> kCannotConnectErrors = [
|
||||
"port 3551 failed: Connection refused",
|
||||
|
||||
@@ -9,9 +9,22 @@ extension FortniteVersionExtension on FortniteVersion {
|
||||
|
||||
static File? findFile(Directory directory, String name) {
|
||||
try{
|
||||
final result = directory.listSync(recursive: true)
|
||||
.firstWhere((element) => path.basename(element.path) == name);
|
||||
return File(result.path);
|
||||
for(final child in directory.listSync()) {
|
||||
if(child is Directory) {
|
||||
if(!path.basename(child.path).startsWith("\.")) {
|
||||
final result = findFile(child, name);
|
||||
if(result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}else if(child is File) {
|
||||
if(path.basename(child.path) == name) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}catch(_){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -15,13 +15,16 @@ final Semaphore _semaphore = Semaphore();
|
||||
String? _lastIp;
|
||||
String? _lastPort;
|
||||
|
||||
Future<Process> startEmbeddedBackend(bool detached) async {
|
||||
Future<Process> startEmbeddedBackend(bool detached, {void Function(String)? onError}) async {
|
||||
final process = await startProcess(
|
||||
executable: backendStartExecutable,
|
||||
window: detached,
|
||||
);
|
||||
process.stdOutput.listen((message) => log("[BACKEND] Message: $message"));
|
||||
process.stdError.listen((error) => log("[BACKEND] Error: $error"));
|
||||
process.stdError.listen((error) {
|
||||
log("[BACKEND] Error: $error");
|
||||
onError?.call(error);
|
||||
});
|
||||
process.exitCode.then((exitCode) => log("[BACKEND] Exit code: $exitCode"));
|
||||
return process;
|
||||
}
|
||||
|
||||
@@ -102,8 +102,7 @@ Future<bool> startElevatedProcess({required String executable, required String a
|
||||
shellInput.ref.fMask = ES_AWAYMODE_REQUIRED;
|
||||
shellInput.ref.lpVerb = "runas".toNativeUtf16();
|
||||
shellInput.ref.cbSize = sizeOf<SHELLEXECUTEINFO>();
|
||||
var shellResult = ShellExecuteEx(shellInput);
|
||||
return shellResult == 1;
|
||||
return ShellExecuteEx(shellInput) == 1;
|
||||
}
|
||||
|
||||
Future<Process> startProcess({required File executable, List<String>? args, bool useTempBatch = true, bool window = false, String? name, Map<String, String>? environment}) async {
|
||||
@@ -313,7 +312,14 @@ final class _ExtendedProcess implements Process {
|
||||
|
||||
|
||||
@override
|
||||
Future<int> get exitCode => _delegate.exitCode;
|
||||
Future<int> get exitCode {
|
||||
try {
|
||||
return _delegate.exitCode;
|
||||
}catch(_) {
|
||||
return watchProcess(_delegate.pid)
|
||||
.then((_) => -1);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool kill([ProcessSignal signal = ProcessSignal.sigterm]) => _delegate.kill(signal);
|
||||
|
||||
Binary file not shown.
@@ -118,9 +118,7 @@
|
||||
"settingsUtilsResetDefaultsName": "Reset settings",
|
||||
"settingsUtilsResetDefaultsSubtitle": "Resets the launcher's settings to their default values",
|
||||
"settingsUtilsDialogTitle": "Do you want to reset all the setting in this tab to their default values? This action is irreversible",
|
||||
"settingsUtilsResetDefaultsContent": "Reset",
|
||||
"settingsUtilsDialogSecondaryAction": "Close",
|
||||
"settingsUtilsDialogPrimaryAction": "Reset",
|
||||
"selectFortniteName": "Fortnite version",
|
||||
"selectFortniteDescription": "Select the version of Fortnite you want to use",
|
||||
"manageVersionsName": "Manage versions",
|
||||
@@ -262,6 +260,7 @@
|
||||
"missingCustomDllError": "The custom {dll}.dll doesn't exist: check your settings",
|
||||
"tokenError": "Cannot log in into Fortnite: authentication error (injected dlls: {dlls})",
|
||||
"unknownFortniteError": "An unknown error occurred while launching Fortnite: {error}",
|
||||
"fortniteCrashError": "The {name} crashed after being launched",
|
||||
"serverNoLongerAvailableUnnamed": "The previous server is no longer available",
|
||||
"noServerFound": "No server found: invalid or expired link",
|
||||
"settingsUtilsThemeName": "Theme",
|
||||
@@ -321,6 +320,7 @@
|
||||
"none": "none",
|
||||
"openLog": "Open log",
|
||||
"backendProcessError": "The backend shut down unexpectedly",
|
||||
"backendErrorMessage": "The backend reported an unexpected error",
|
||||
"welcomeTitle": "Welcome to Reboot Launcher",
|
||||
"welcomeDescription": "If you have never used a Fortnite game server, or this launcher in particular, please click on take a tour\nPlease don't ask for support on Discord without taking the tour: this helps me prioritize real bugs\nYou can always take the tour again in the Info tab",
|
||||
"welcomeAction": "Take the tour",
|
||||
@@ -364,5 +364,8 @@
|
||||
"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",
|
||||
"automaticGameServerDialogStart": "Start server"
|
||||
"automaticGameServerDialogStart": "Start server",
|
||||
"gameResetDefaultsName": "Reset",
|
||||
"gameResetDefaultsDescription": "Resets the game's settings to their default values",
|
||||
"gameResetDefaultsContent": "Reset"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:local_notifier/local_notifier.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
@@ -188,10 +189,11 @@ void _initWindow() => doWhenWindowReady(() async {
|
||||
Future<List<Object>> _initStorage() async {
|
||||
final errors = <Object>[];
|
||||
try {
|
||||
await GetStorage("game_storage", settingsDirectory.path).initStorage;
|
||||
await GetStorage("backend_storage", settingsDirectory.path).initStorage;
|
||||
await GetStorage("settings_storage", settingsDirectory.path).initStorage;
|
||||
await GetStorage("hosting_storage", settingsDirectory.path).initStorage;
|
||||
await GetStorage(GameController.storageName, settingsDirectory.path).initStorage;
|
||||
await GetStorage(BackendController.storageName, settingsDirectory.path).initStorage;
|
||||
await GetStorage(SettingsController.storageName, settingsDirectory.path).initStorage;
|
||||
await GetStorage(HostingController.storageName, settingsDirectory.path).initStorage;
|
||||
await GetStorage(DllController.storageName, settingsDirectory.path).initStorage;
|
||||
}catch(error) {
|
||||
appWithNoStorage = true;
|
||||
errors.add("The Reboot Launcher configuration in ${settingsDirectory.path} cannot be accessed: running with in memory storage");
|
||||
@@ -223,6 +225,12 @@ Future<List<Object>> _initStorage() async {
|
||||
errors.add(error);
|
||||
}
|
||||
|
||||
try {
|
||||
Get.put(DllController());
|
||||
}catch(error) {
|
||||
errors.add(error);
|
||||
}
|
||||
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,24 @@ import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/util/keyboard.dart';
|
||||
|
||||
class BackendController extends GetxController {
|
||||
late final GetStorage? storage;
|
||||
static const String storageName = "backend_storage";
|
||||
static const PhysicalKeyboardKey _kDefaultConsoleKey = PhysicalKeyboardKey(0x00070041);
|
||||
|
||||
late final GetStorage? _storage;
|
||||
late final TextEditingController host;
|
||||
late final TextEditingController port;
|
||||
late final Rx<ServerType> type;
|
||||
late final TextEditingController gameServerAddress;
|
||||
late final FocusNode gameServerAddressFocusNode;
|
||||
late final Rx<PhysicalKeyboardKey> consoleKey;
|
||||
late final RxBool started;
|
||||
late final RxBool detached;
|
||||
StreamSubscription? worker;
|
||||
@@ -22,13 +28,13 @@ class BackendController extends GetxController {
|
||||
HttpServer? remoteServer;
|
||||
|
||||
BackendController() {
|
||||
storage = appWithNoStorage ? null : GetStorage("backend_storage");
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
started = RxBool(false);
|
||||
type = Rx(ServerType.values.elementAt(storage?.read("type") ?? 0));
|
||||
type = Rx(ServerType.values.elementAt(_storage?.read("type") ?? 0));
|
||||
type.listen((value) {
|
||||
host.text = _readHost();
|
||||
port.text = _readPort();
|
||||
storage?.write("type", value.index);
|
||||
_storage?.write("type", value.index);
|
||||
if (!started.value) {
|
||||
return;
|
||||
}
|
||||
@@ -37,13 +43,13 @@ class BackendController extends GetxController {
|
||||
});
|
||||
host = TextEditingController(text: _readHost());
|
||||
host.addListener(() =>
|
||||
storage?.write("${type.value.name}_host", host.text));
|
||||
_storage?.write("${type.value.name}_host", host.text));
|
||||
port = TextEditingController(text: _readPort());
|
||||
port.addListener(() =>
|
||||
storage?.write("${type.value.name}_port", port.text));
|
||||
detached = RxBool(storage?.read("detached") ?? false);
|
||||
detached.listen((value) => storage?.write("detached", value));
|
||||
final address = storage?.read("game_server_address");
|
||||
_storage?.write("${type.value.name}_port", port.text));
|
||||
detached = RxBool(_storage?.read("detached") ?? false);
|
||||
detached.listen((value) => _storage?.write("detached", value));
|
||||
final address = _storage?.read("game_server_address");
|
||||
gameServerAddress = TextEditingController(text: address == null || address.isEmpty ? "127.0.0.1" : address);
|
||||
var lastValue = gameServerAddress.text;
|
||||
writeMatchmakingIp(lastValue);
|
||||
@@ -55,7 +61,7 @@ class BackendController extends GetxController {
|
||||
|
||||
lastValue = newValue;
|
||||
gameServerAddress.selection = TextSelection.collapsed(offset: newValue.length);
|
||||
storage?.write("game_server_address", newValue);
|
||||
_storage?.write("game_server_address", newValue);
|
||||
writeMatchmakingIp(newValue);
|
||||
});
|
||||
watchMatchmakingIp().listen((event) {
|
||||
@@ -64,6 +70,37 @@ class BackendController extends GetxController {
|
||||
}
|
||||
});
|
||||
gameServerAddressFocusNode = FocusNode();
|
||||
consoleKey = Rx(_readConsoleKey());
|
||||
_writeConsoleKey(consoleKey.value);
|
||||
consoleKey.listen((newValue) {
|
||||
_storage?.write("console_key", newValue.usbHidUsage);
|
||||
_writeConsoleKey(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
PhysicalKeyboardKey _readConsoleKey() {
|
||||
final consoleKeyValue = _storage?.read("console_key");
|
||||
if(consoleKeyValue == null) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
final consoleKeyNumber = int.tryParse(consoleKeyValue.toString());
|
||||
if(consoleKeyNumber == null) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
final consoleKey = PhysicalKeyboardKey(consoleKeyNumber);
|
||||
if(!consoleKey.isUnrealEngineKey) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
return consoleKey;
|
||||
}
|
||||
|
||||
Future<void> _writeConsoleKey(PhysicalKeyboardKey keyValue) async {
|
||||
final defaultInput = File("${backendDirectory.path}\\CloudStorage\\DefaultInput.ini");
|
||||
await defaultInput.parent.create(recursive: true);
|
||||
await defaultInput.writeAsString("[/Script/Engine.InputSettings]\n+ConsoleKeys=Tilde\n+ConsoleKeys=${keyValue.unrealEngineName}", flush: true);
|
||||
}
|
||||
|
||||
void joinLocalhost() {
|
||||
@@ -73,18 +110,19 @@ class BackendController extends GetxController {
|
||||
void reset() async {
|
||||
type.value = ServerType.values.elementAt(0);
|
||||
for (final type in ServerType.values) {
|
||||
storage?.write("${type.name}_host", null);
|
||||
storage?.write("${type.name}_port", null);
|
||||
_storage?.write("${type.name}_host", null);
|
||||
_storage?.write("${type.name}_port", null);
|
||||
}
|
||||
|
||||
host.text = type.value != ServerType.remote ? kDefaultBackendHost : "";
|
||||
port.text = kDefaultBackendPort.toString();
|
||||
gameServerAddress.text = "127.0.0.1";
|
||||
consoleKey.value = _kDefaultConsoleKey;
|
||||
detached.value = false;
|
||||
}
|
||||
|
||||
String _readHost() {
|
||||
String? value = storage?.read("${type.value.name}_host");
|
||||
String? value = _storage?.read("${type.value.name}_host");
|
||||
if (value != null && value.isNotEmpty) {
|
||||
return value;
|
||||
}
|
||||
@@ -97,9 +135,9 @@ class BackendController extends GetxController {
|
||||
}
|
||||
|
||||
String _readPort() =>
|
||||
storage?.read("${type.value.name}_port") ?? kDefaultBackendPort.toString();
|
||||
_storage?.read("${type.value.name}_port") ?? kDefaultBackendPort.toString();
|
||||
|
||||
Stream<ServerResult> start() async* {
|
||||
Stream<ServerResult> start({required void Function() onExit, required void Function(String) onError}) async* {
|
||||
try {
|
||||
if(started.value) {
|
||||
return;
|
||||
@@ -144,7 +182,18 @@ class BackendController extends GetxController {
|
||||
|
||||
switch(serverType){
|
||||
case ServerType.embedded:
|
||||
final process = await startEmbeddedBackend(detached.value);
|
||||
final process = await startEmbeddedBackend(detached.value, onError: (errorMessage) {
|
||||
if(started.value) {
|
||||
started.value = false;
|
||||
onError(errorMessage);
|
||||
}
|
||||
});
|
||||
watchProcess(process.pid).then((_) {
|
||||
if(started.value) {
|
||||
started.value = false;
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
embeddedProcessPid = process.pid;
|
||||
break;
|
||||
case ServerType.remote:
|
||||
@@ -237,11 +286,14 @@ class BackendController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ServerResult> toggle() async* {
|
||||
Stream<ServerResult> toggle({required void Function() onExit, required void Function(String) onError}) async* {
|
||||
if(started()) {
|
||||
yield* stop();
|
||||
}else {
|
||||
yield* start();
|
||||
yield* start(
|
||||
onExit: onExit,
|
||||
onError: onError
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
265
gui/lib/src/controller/dll_controller.dart
Normal file
265
gui/lib/src/controller/dll_controller.dart
Normal file
@@ -0,0 +1,265 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class DllController extends GetxController {
|
||||
static const String storageName = "dll_storage";
|
||||
|
||||
late final GetStorage? _storage;
|
||||
late final String originalDll;
|
||||
late final TextEditingController gameServerDll;
|
||||
late final TextEditingController unrealEngineConsoleDll;
|
||||
late final TextEditingController backendDll;
|
||||
late final TextEditingController memoryLeakDll;
|
||||
late final TextEditingController gameServerPort;
|
||||
late final Rx<UpdateTimer> timer;
|
||||
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;
|
||||
|
||||
DllController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
gameServerDll = _createController("game_server", InjectableDll.reboot);
|
||||
unrealEngineConsoleDll = _createController("unreal_engine_console", InjectableDll.console);
|
||||
backendDll = _createController("backend", InjectableDll.cobalt);
|
||||
memoryLeakDll = _createController("memory_leak", InjectableDll.memory);
|
||||
gameServerPort = TextEditingController(text: _storage?.read("game_server_port") ?? kDefaultGameServerPort);
|
||||
gameServerPort.addListener(() => _storage?.write("game_server_port", gameServerPort.text));
|
||||
final timerIndex = _storage?.read("timer");
|
||||
timer = Rx(timerIndex == null ? UpdateTimer.hour : UpdateTimer.values.elementAt(timerIndex));
|
||||
timer.listen((value) => _storage?.write("timer", value.index));
|
||||
url = TextEditingController(text: _storage?.read("update_url") ?? kRebootDownloadUrl);
|
||||
url.addListener(() => _storage?.write("update_url", url.text));
|
||||
status = Rx(UpdateStatus.waiting);
|
||||
customGameServer = RxBool(_storage?.read("custom_game_server") ?? false);
|
||||
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));
|
||||
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);
|
||||
}
|
||||
|
||||
void resetServer() {
|
||||
gameServerPort.text = kDefaultGameServerPort;
|
||||
timer.value = UpdateTimer.hour;
|
||||
url.text = kRebootDownloadUrl;
|
||||
status.value = UpdateStatus.waiting;
|
||||
customGameServer.value = false;
|
||||
timestamp.value = null;
|
||||
updateGameServerDll();
|
||||
}
|
||||
|
||||
Future<bool> updateGameServerDll({bool force = false, bool silent = false}) async {
|
||||
if(_updater != null) {
|
||||
return await _updater!;
|
||||
}
|
||||
|
||||
final result = _updateGameServerDll(force, silent);
|
||||
_updater = result;
|
||||
return await result;
|
||||
}
|
||||
|
||||
Future<bool> _updateGameServerDll(bool force, bool silent) async {
|
||||
try {
|
||||
if(customGameServer.value) {
|
||||
status.value = UpdateStatus.success;
|
||||
return true;
|
||||
}
|
||||
|
||||
final needsUpdate = await hasRebootDllUpdate(
|
||||
timestamp.value,
|
||||
hours: timer.value.hours,
|
||||
force: force
|
||||
);
|
||||
if(!needsUpdate) {
|
||||
status.value = UpdateStatus.success;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!silent) {
|
||||
infoBarEntry = showRebootInfoBar(
|
||||
translations.downloadingDll("reboot"),
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
timestamp.value = await downloadRebootDll(url.text);
|
||||
status.value = UpdateStatus.success;
|
||||
infoBarEntry?.close();
|
||||
if(!silent) {
|
||||
infoBarEntry = showRebootInfoBar(
|
||||
translations.downloadDllSuccess("reboot"),
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}catch(message) {
|
||||
infoBarEntry?.close();
|
||||
var error = message.toString();
|
||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||
error = error.toLowerCase();
|
||||
status.value = UpdateStatus.error;
|
||||
showRebootInfoBar(
|
||||
translations.downloadDllError("reboot.dll", error.toString()),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
action: Button(
|
||||
onPressed: () => updateGameServerDll(
|
||||
force: true,
|
||||
silent: silent
|
||||
),
|
||||
child: Text(translations.downloadDllRetry),
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}finally {
|
||||
_updater = null;
|
||||
}
|
||||
}
|
||||
|
||||
(File, bool) getInjectableData(InjectableDll dll) {
|
||||
final defaultPath = canonicalize(_getDefaultPath(dll));
|
||||
switch(dll){
|
||||
case InjectableDll.reboot:
|
||||
if(customGameServer.value) {
|
||||
final file = File(gameServerDll.text);
|
||||
if(file.existsSync()) {
|
||||
return (file, true);
|
||||
}
|
||||
}
|
||||
|
||||
return (rebootDllFile, false);
|
||||
case InjectableDll.console:
|
||||
final ue4ConsoleFile = File(unrealEngineConsoleDll.text);
|
||||
return (ue4ConsoleFile, canonicalize(ue4ConsoleFile.path) != defaultPath);
|
||||
case InjectableDll.cobalt:
|
||||
final backendFile = File(backendDll.text);
|
||||
return (backendFile, canonicalize(backendFile.path) != defaultPath);
|
||||
case InjectableDll.memory:
|
||||
final memoryLeakFile = File(memoryLeakDll.text);
|
||||
return (memoryLeakFile, canonicalize(memoryLeakFile.path) != defaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
String _getDefaultPath(InjectableDll dll) => "${dllsDirectory.path}\\${dll.name}.dll";
|
||||
|
||||
Future<bool> downloadCriticalDllInteractive(String filePath, {bool silent = false}) {
|
||||
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;
|
||||
try {
|
||||
if (fileName == "reboot.dll") {
|
||||
log("[DLL] Downloading reboot.dll...");
|
||||
return await updateGameServerDll(
|
||||
silent: silent
|
||||
);
|
||||
}
|
||||
|
||||
if(File(filePath).existsSync()) {
|
||||
log("[DLL] File already exists");
|
||||
return true;
|
||||
}
|
||||
|
||||
final fileNameWithoutExtension = basenameWithoutExtension(filePath);
|
||||
if(!silent) {
|
||||
entry = showRebootInfoBar(
|
||||
translations.downloadingDll(fileNameWithoutExtension),
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
await downloadCriticalDll(fileName, filePath);
|
||||
entry?.close();
|
||||
if(!silent) {
|
||||
entry = await showRebootInfoBar(
|
||||
translations.downloadDllSuccess(fileNameWithoutExtension),
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}catch(message) {
|
||||
log("[DLL] Error: $message");
|
||||
entry?.close();
|
||||
var error = message.toString();
|
||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||
error = error.toLowerCase();
|
||||
final completer = Completer();
|
||||
await showRebootInfoBar(
|
||||
translations.downloadDllError(fileName, error.toString()),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
onDismissed: () => completer.complete(null),
|
||||
action: Button(
|
||||
onPressed: () async {
|
||||
await downloadCriticalDllInteractive(filePath);
|
||||
completer.complete(null);
|
||||
},
|
||||
child: Text(translations.downloadDllRetry),
|
||||
)
|
||||
);
|
||||
await completer.future;
|
||||
return false;
|
||||
}finally {
|
||||
_operations.remove(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _UpdateTimerExtension on UpdateTimer {
|
||||
int get hours {
|
||||
switch(this) {
|
||||
case UpdateTimer.never:
|
||||
return -1;
|
||||
case UpdateTimer.hour:
|
||||
return 1;
|
||||
case UpdateTimer.day:
|
||||
return 24;
|
||||
case UpdateTimer.week:
|
||||
return 24 * 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/util/keyboard.dart';
|
||||
|
||||
import '../../main.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
|
||||
class GameController extends GetxController {
|
||||
static const PhysicalKeyboardKey _kDefaultConsoleKey = PhysicalKeyboardKey(0x00070041);
|
||||
static const String storageName = "game_storage";
|
||||
|
||||
late final GetStorage? _storage;
|
||||
late final TextEditingController username;
|
||||
@@ -22,10 +18,9 @@ class GameController extends GetxController {
|
||||
late final Rxn<FortniteVersion> _selectedVersion;
|
||||
late final RxBool started;
|
||||
late final Rxn<GameInstance> instance;
|
||||
late final Rx<PhysicalKeyboardKey> consoleKey;
|
||||
|
||||
GameController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage("game_storage");
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
Iterable decodedVersionsJson = jsonDecode(_storage?.read("versions") ?? "[]");
|
||||
final decodedVersions = decodedVersionsJson
|
||||
.map((entry) => FortniteVersion.fromJson(entry))
|
||||
@@ -44,37 +39,6 @@ class GameController extends GetxController {
|
||||
customLaunchArgs.addListener(() => _storage?.write("custom_launch_args", customLaunchArgs.text));
|
||||
started = RxBool(false);
|
||||
instance = Rxn();
|
||||
consoleKey = Rx(_readConsoleKey());
|
||||
_writeConsoleKey(consoleKey.value);
|
||||
consoleKey.listen((newValue) {
|
||||
_storage?.write("console_key", newValue.usbHidUsage);
|
||||
_writeConsoleKey(newValue);
|
||||
});
|
||||
}
|
||||
|
||||
PhysicalKeyboardKey _readConsoleKey() {
|
||||
final consoleKeyValue = _storage?.read("console_key");
|
||||
if(consoleKeyValue == null) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
final consoleKeyNumber = int.tryParse(consoleKeyValue.toString());
|
||||
if(consoleKeyNumber == null) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
final consoleKey = PhysicalKeyboardKey(consoleKeyNumber);
|
||||
if(!consoleKey.isUnrealEngineKey) {
|
||||
return _kDefaultConsoleKey;
|
||||
}
|
||||
|
||||
return consoleKey;
|
||||
}
|
||||
|
||||
Future<void> _writeConsoleKey(PhysicalKeyboardKey keyValue) async {
|
||||
final defaultInput = File("${backendDirectory.path}\\CloudStorage\\DefaultInput.ini");
|
||||
await defaultInput.parent.create(recursive: true);
|
||||
await defaultInput.writeAsString("[/Script/Engine.InputSettings]\n+ConsoleKeys=Tilde\n+ConsoleKeys=${keyValue.unrealEngineName}", flush: true);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
@@ -82,6 +46,7 @@ class GameController extends GetxController {
|
||||
password.text = "";
|
||||
customLaunchArgs.text = "";
|
||||
versions.value = [];
|
||||
_selectedVersion.value = null;
|
||||
instance.value = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import 'package:sync/semaphore.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class HostingController extends GetxController {
|
||||
static const String storageName = "hosting_storage";
|
||||
|
||||
late final GetStorage? _storage;
|
||||
late final String uuid;
|
||||
late final TextEditingController name;
|
||||
@@ -32,7 +34,7 @@ class HostingController extends GetxController {
|
||||
late final Semaphore _semaphore;
|
||||
|
||||
HostingController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage("hosting_storage");
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
uuid = _storage?.read("uuid") ?? const Uuid().v4();
|
||||
_storage?.write("uuid", uuid);
|
||||
name = TextEditingController(text: _storage?.read("name"));
|
||||
@@ -138,10 +140,10 @@ class HostingController extends GetxController {
|
||||
description.text = "";
|
||||
showPassword.value = false;
|
||||
discoverable.value = false;
|
||||
started.value = false;
|
||||
instance.value = null;
|
||||
type.value = GameServerType.headless;
|
||||
autoRestart.value = true;
|
||||
customLaunchArgs.text = "";
|
||||
}
|
||||
|
||||
FortniteServer? findServerById(String uuid) {
|
||||
|
||||
@@ -15,37 +15,19 @@ import 'package:version/version.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class SettingsController extends GetxController {
|
||||
static const String storageName = "settings_storage";
|
||||
|
||||
late final GetStorage? _storage;
|
||||
late final String originalDll;
|
||||
late final TextEditingController gameServerDll;
|
||||
late final TextEditingController unrealEngineConsoleDll;
|
||||
late final TextEditingController backendDll;
|
||||
late final TextEditingController memoryLeakDll;
|
||||
late final TextEditingController gameServerPort;
|
||||
late final RxString language;
|
||||
late final Rx<ThemeMode> themeMode;
|
||||
late final RxnInt timestamp;
|
||||
late final Rx<UpdateStatus> status;
|
||||
late final Rx<UpdateTimer> timer;
|
||||
late final TextEditingController url;
|
||||
late final RxBool customGameServer;
|
||||
late final RxBool firstRun;
|
||||
late final Map<String, Future<bool>> _operations;
|
||||
late double width;
|
||||
late double height;
|
||||
late double? offsetX;
|
||||
late double? offsetY;
|
||||
InfoBarEntry? infoBarEntry;
|
||||
Future<bool>? _updater;
|
||||
|
||||
SettingsController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage("settings_storage");
|
||||
gameServerDll = _createController("game_server", InjectableDll.reboot);
|
||||
unrealEngineConsoleDll = _createController("unreal_engine_console", InjectableDll.console);
|
||||
backendDll = _createController("backend", InjectableDll.cobalt);
|
||||
memoryLeakDll = _createController("memory_leak", InjectableDll.memory);
|
||||
gameServerPort = TextEditingController(text: _storage?.read("game_server_port") ?? kDefaultGameServerPort);
|
||||
gameServerPort.addListener(() => _storage?.write("game_server_port", gameServerPort.text));
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
width = _storage?.read("width") ?? kDefaultWindowWidth;
|
||||
height = _storage?.read("height") ?? kDefaultWindowHeight;
|
||||
offsetX = _storage?.read("offset_x");
|
||||
@@ -54,25 +36,8 @@ class SettingsController extends GetxController {
|
||||
themeMode.listen((value) => _storage?.write("theme", value.index));
|
||||
language = RxString(_storage?.read("language") ?? currentLocale);
|
||||
language.listen((value) => _storage?.write("language", value));
|
||||
timestamp = RxnInt(_storage?.read("ts"));
|
||||
timestamp.listen((value) => _storage?.write("ts", value));
|
||||
final timerIndex = _storage?.read("timer");
|
||||
timer = Rx(timerIndex == null ? UpdateTimer.hour : UpdateTimer.values.elementAt(timerIndex));
|
||||
timer.listen((value) => _storage?.write("timer", value.index));
|
||||
url = TextEditingController(text: _storage?.read("update_url") ?? kRebootDownloadUrl);
|
||||
url.addListener(() => _storage?.write("update_url", url.text));
|
||||
status = Rx(UpdateStatus.waiting);
|
||||
customGameServer = RxBool(_storage?.read("custom_game_server") ?? false);
|
||||
customGameServer.listen((value) => _storage?.write("custom_game_server", value));
|
||||
firstRun = RxBool(_storage?.read("first_run_tutorial") ?? true);
|
||||
firstRun.listen((value) => _storage?.write("first_run_tutorial", value));
|
||||
_operations = {};
|
||||
}
|
||||
|
||||
TextEditingController _createController(String key, InjectableDll dll) {
|
||||
final controller = TextEditingController(text: _storage?.read(key) ?? _getDefaultPath(dll));
|
||||
controller.addListener(() => _storage?.write(key, controller.text));
|
||||
return controller;
|
||||
}
|
||||
|
||||
void saveWindowSize(Size size) {
|
||||
@@ -87,32 +52,18 @@ class SettingsController extends GetxController {
|
||||
_storage?.write("offset_y", offsetY);
|
||||
}
|
||||
|
||||
void reset(){
|
||||
gameServerDll.text = _getDefaultPath(InjectableDll.reboot);
|
||||
unrealEngineConsoleDll.text = _getDefaultPath(InjectableDll.console);
|
||||
backendDll.text = _getDefaultPath(InjectableDll.cobalt);
|
||||
memoryLeakDll.text = _getDefaultPath(InjectableDll.memory);
|
||||
gameServerPort.text = kDefaultGameServerPort;
|
||||
timestamp.value = null;
|
||||
timer.value = UpdateTimer.never;
|
||||
url.text = kRebootDownloadUrl;
|
||||
status.value = UpdateStatus.waiting;
|
||||
customGameServer.value = false;
|
||||
updateReboot();
|
||||
}
|
||||
|
||||
Future<void> notifyLauncherUpdate() async {
|
||||
if(appVersion == null) {
|
||||
if (appVersion == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final pubspec = await _getPubspecYaml();
|
||||
if(pubspec == null) {
|
||||
if (pubspec == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final latestVersion = Version.parse(pubspec["version"]);
|
||||
if(latestVersion <= appVersion) {
|
||||
if (latestVersion <= appVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -125,7 +76,8 @@ class SettingsController extends GetxController {
|
||||
child: Text(translations.updateAvailableAction),
|
||||
onPressed: () {
|
||||
infoBar.close();
|
||||
launchUrl(Uri.parse("https://github.com/Auties00/reboot_launcher/releases"));
|
||||
launchUrl(Uri.parse(
|
||||
"https://github.com/Auties00/reboot_launcher/releases"));
|
||||
},
|
||||
)
|
||||
);
|
||||
@@ -133,201 +85,16 @@ class SettingsController extends GetxController {
|
||||
|
||||
Future<dynamic> _getPubspecYaml() async {
|
||||
try {
|
||||
final pubspecResponse = await http.get(Uri.parse("https://raw.githubusercontent.com/Auties00/reboot_launcher/master/gui/pubspec.yaml"));
|
||||
if(pubspecResponse.statusCode != 200) {
|
||||
final pubspecResponse = await http.get(Uri.parse(
|
||||
"https://raw.githubusercontent.com/Auties00/reboot_launcher/master/gui/pubspec.yaml"));
|
||||
if (pubspecResponse.statusCode != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return loadYaml(pubspecResponse.body);
|
||||
}catch(error) {
|
||||
} catch (error) {
|
||||
log("[UPDATER] Cannot check for updates: $error");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> updateReboot({bool force = false, bool silent = false}) async {
|
||||
if(_updater != null) {
|
||||
return await _updater!;
|
||||
}
|
||||
|
||||
final result = _updateReboot(force, silent);
|
||||
_updater = result;
|
||||
return await result;
|
||||
}
|
||||
|
||||
Future<bool> _updateReboot(bool force, bool silent) async {
|
||||
try {
|
||||
if(customGameServer.value) {
|
||||
status.value = UpdateStatus.success;
|
||||
return true;
|
||||
}
|
||||
|
||||
final needsUpdate = await hasRebootDllUpdate(
|
||||
timestamp.value,
|
||||
hours: timer.value.hours,
|
||||
force: force
|
||||
);
|
||||
if(!needsUpdate) {
|
||||
status.value = UpdateStatus.success;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!silent) {
|
||||
infoBarEntry = showRebootInfoBar(
|
||||
translations.downloadingDll("reboot"),
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
timestamp.value = await downloadRebootDll(url.text);
|
||||
status.value = UpdateStatus.success;
|
||||
infoBarEntry?.close();
|
||||
if(!silent) {
|
||||
infoBarEntry = showRebootInfoBar(
|
||||
translations.downloadDllSuccess("reboot"),
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}catch(message) {
|
||||
infoBarEntry?.close();
|
||||
var error = message.toString();
|
||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||
error = error.toLowerCase();
|
||||
status.value = UpdateStatus.error;
|
||||
showRebootInfoBar(
|
||||
translations.downloadDllError("reboot.dll", error.toString()),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
action: Button(
|
||||
onPressed: () => updateReboot(
|
||||
force: true,
|
||||
silent: silent
|
||||
),
|
||||
child: Text(translations.downloadDllRetry),
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}finally {
|
||||
_updater = null;
|
||||
}
|
||||
}
|
||||
|
||||
(File, bool) getInjectableData(InjectableDll dll) {
|
||||
final defaultPath = canonicalize(_getDefaultPath(dll));
|
||||
switch(dll){
|
||||
case InjectableDll.reboot:
|
||||
if(customGameServer.value) {
|
||||
final file = File(gameServerDll.text);
|
||||
if(file.existsSync()) {
|
||||
return (file, true);
|
||||
}
|
||||
}
|
||||
|
||||
return (rebootDllFile, false);
|
||||
case InjectableDll.console:
|
||||
final ue4ConsoleFile = File(unrealEngineConsoleDll.text);
|
||||
return (ue4ConsoleFile, canonicalize(ue4ConsoleFile.path) != defaultPath);
|
||||
case InjectableDll.cobalt:
|
||||
final backendFile = File(backendDll.text);
|
||||
return (backendFile, canonicalize(backendFile.path) != defaultPath);
|
||||
case InjectableDll.memory:
|
||||
final memoryLeakFile = File(memoryLeakDll.text);
|
||||
return (memoryLeakFile, canonicalize(memoryLeakFile.path) != defaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
String _getDefaultPath(InjectableDll dll) => "${dllsDirectory.path}\\${dll.name}.dll";
|
||||
|
||||
Future<bool> downloadCriticalDllInteractive(String filePath, {bool silent = false}) {
|
||||
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;
|
||||
try {
|
||||
if (fileName == "reboot.dll") {
|
||||
log("[DLL] Downloading reboot.dll...");
|
||||
return await updateReboot(
|
||||
silent: silent
|
||||
);
|
||||
}
|
||||
|
||||
if(File(filePath).existsSync()) {
|
||||
log("[DLL] File already exists");
|
||||
return true;
|
||||
}
|
||||
|
||||
final fileNameWithoutExtension = basenameWithoutExtension(filePath);
|
||||
if(!silent) {
|
||||
entry = showRebootInfoBar(
|
||||
translations.downloadingDll(fileNameWithoutExtension),
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
await downloadCriticalDll(fileName, filePath);
|
||||
entry?.close();
|
||||
if(!silent) {
|
||||
entry = await showRebootInfoBar(
|
||||
translations.downloadDllSuccess(fileNameWithoutExtension),
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}catch(message) {
|
||||
log("[DLL] Error: $message");
|
||||
entry?.close();
|
||||
var error = message.toString();
|
||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||
error = error.toLowerCase();
|
||||
final completer = Completer();
|
||||
await showRebootInfoBar(
|
||||
translations.downloadDllError(fileName, error.toString()),
|
||||
duration: infoBarLongDuration,
|
||||
severity: InfoBarSeverity.error,
|
||||
onDismissed: () => completer.complete(null),
|
||||
action: Button(
|
||||
onPressed: () async {
|
||||
await downloadCriticalDllInteractive(filePath);
|
||||
completer.complete(null);
|
||||
},
|
||||
child: Text(translations.downloadDllRetry),
|
||||
)
|
||||
);
|
||||
await completer.future;
|
||||
return false;
|
||||
}finally {
|
||||
_operations.remove(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension _UpdateTimerExtension on UpdateTimer {
|
||||
int get hours {
|
||||
switch(this) {
|
||||
case UpdateTimer.never:
|
||||
return -1;
|
||||
case UpdateTimer.hour:
|
||||
return 1;
|
||||
case UpdateTimer.day:
|
||||
return 24;
|
||||
case UpdateTimer.week:
|
||||
return 24 * 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import 'package:reboot_launcher/src/page/pages.dart';
|
||||
import 'package:reboot_launcher/src/util/cryptography.dart';
|
||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
final List<InfoBarEntry> _infoBars = [];
|
||||
|
||||
@@ -27,7 +28,27 @@ extension ServerControllerDialog on BackendController {
|
||||
|
||||
Future<bool> toggleInteractive() async {
|
||||
cancelInteractive();
|
||||
final stream = toggle();
|
||||
final stream = toggle(
|
||||
onExit: () {
|
||||
cancelInteractive();
|
||||
_showRebootInfoBar(
|
||||
translations.backendProcessError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
},
|
||||
onError: (errorMessage) {
|
||||
cancelInteractive();
|
||||
_showRebootInfoBar(
|
||||
translations.backendErrorMessage,
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration,
|
||||
action: Button(
|
||||
onPressed: () => launchUrl(launcherLogFile.uri),
|
||||
child: Text(translations.openLog),
|
||||
)
|
||||
);
|
||||
}
|
||||
);
|
||||
final completer = Completer<bool>();
|
||||
InfoBarEntry? entry;
|
||||
worker = stream.listen((event) {
|
||||
@@ -54,19 +75,6 @@ extension ServerControllerDialog on BackendController {
|
||||
duration: null
|
||||
);
|
||||
case ServerResultType.startSuccess:
|
||||
final embeddedProcessPid = this.embeddedProcessPid;
|
||||
if(embeddedProcessPid != null) {
|
||||
watchProcess(embeddedProcessPid).then((_) {
|
||||
if(started.value) {
|
||||
started.value = false;
|
||||
_showRebootInfoBar(
|
||||
translations.backendProcessError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return _showRebootInfoBar(
|
||||
type.value == ServerType.local ? translations.checkedServer : translations.startedServer,
|
||||
severity: InfoBarSeverity.success
|
||||
|
||||
@@ -43,7 +43,6 @@ class BackendPage extends RebootPage {
|
||||
}
|
||||
|
||||
class _BackendPageState extends RebootPageState<BackendPage> {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final BackendController _backendController = Get.find<BackendController>();
|
||||
|
||||
InfoBarEntry? _infoBarEntry;
|
||||
@@ -56,7 +55,7 @@ class _BackendPageState extends RebootPageState<BackendPage> {
|
||||
}
|
||||
|
||||
if(keyEvent.physicalKey.isUnrealEngineKey) {
|
||||
_gameController.consoleKey.value = keyEvent.physicalKey;
|
||||
_backendController.consoleKey.value = keyEvent.physicalKey;
|
||||
}
|
||||
|
||||
_infoBarEntry?.close();
|
||||
@@ -194,7 +193,7 @@ class _BackendPageState extends RebootPageState<BackendPage> {
|
||||
duration: null
|
||||
);
|
||||
},
|
||||
child: Text(_gameController.consoleKey.value.unrealEnginePrettyName ?? ""),
|
||||
child: Text(_backendController.consoleKey.value.unrealEnginePrettyName ?? ""),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:flutter/material.dart' show MaterialPage;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/dialog.dart';
|
||||
@@ -44,6 +45,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
final BackendController _backendController = Get.find<BackendController>();
|
||||
final HostingController _hostingController = Get.find<HostingController>();
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final DllController _dllController = Get.find<DllController>();
|
||||
final GlobalKey _searchKey = GlobalKey();
|
||||
final FocusNode _searchFocusNode = FocusNode();
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
@@ -134,9 +136,9 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
}
|
||||
|
||||
for(final injectable in InjectableDll.values) {
|
||||
final (file, custom) = _settingsController.getInjectableData(injectable);
|
||||
final (file, custom) = _dllController.getInjectableData(injectable);
|
||||
if(!custom) {
|
||||
_settingsController.downloadCriticalDllInteractive(
|
||||
_dllController.downloadCriticalDllInteractive(
|
||||
file.path,
|
||||
silent: true
|
||||
);
|
||||
@@ -144,7 +146,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
}
|
||||
|
||||
watchDlls().listen((filePath) => showDllDeletedDialog(() {
|
||||
_settingsController.downloadCriticalDllInteractive(filePath);
|
||||
_dllController.downloadCriticalDllInteractive(filePath);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
@@ -53,6 +54,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final HostingController _hostingController = Get.find<HostingController>();
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final DllController _dllController = Get.find<DllController>();
|
||||
|
||||
late final RxBool _showPasswordTrailing = RxBool(_hostingController.password.text.isNotEmpty);
|
||||
|
||||
@@ -257,7 +259,7 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
contentWidth: 64,
|
||||
content: TextFormBox(
|
||||
placeholder: translations.settingsServerPortName,
|
||||
controller: _settingsController.gameServerPort,
|
||||
controller: _dllController.gameServerPort,
|
||||
keyboardType: TextInputType.number,
|
||||
textAlign: TextAlign.center,
|
||||
inputFormatters: [
|
||||
@@ -284,22 +286,22 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_settingsController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
|
||||
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 = _settingsController.customGameServer.value;
|
||||
final oldValue = _dllController.customGameServer.value;
|
||||
if(oldValue == entry.key) {
|
||||
return;
|
||||
}
|
||||
|
||||
_settingsController.customGameServer.value = entry.key;
|
||||
_settingsController.infoBarEntry?.close();
|
||||
_dllController.customGameServer.value = entry.key;
|
||||
_dllController.infoBarEntry?.close();
|
||||
if(!entry.key) {
|
||||
_settingsController.updateReboot(
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
@@ -308,18 +310,18 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
))
|
||||
),
|
||||
Obx(() {
|
||||
if(!_settingsController.customGameServer.value) {
|
||||
if(!_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return createFileSetting(
|
||||
title: translations.settingsServerFileName,
|
||||
description: translations.settingsServerFileDescription,
|
||||
controller: _settingsController.gameServerDll
|
||||
controller: _dllController.gameServerDll
|
||||
);
|
||||
}),
|
||||
Obx(() {
|
||||
if(_settingsController.customGameServer.value) {
|
||||
if(_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -331,13 +333,13 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
subtitle: Text(translations.settingsServerMirrorDescription),
|
||||
content: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _settingsController.url,
|
||||
controller: _dllController.url,
|
||||
validator: _checkUpdateUrl
|
||||
)
|
||||
);
|
||||
}),
|
||||
Obx(() {
|
||||
if(_settingsController.customGameServer.value) {
|
||||
if(_dllController.customGameServer.value) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
@@ -350,13 +352,13 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_settingsController.timer.value.text),
|
||||
leading: Text(_dllController.timer.value.text),
|
||||
items: UpdateTimer.values.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.text),
|
||||
onPressed: () {
|
||||
_settingsController.timer.value = entry;
|
||||
_settingsController.infoBarEntry?.close();
|
||||
_settingsController.updateReboot(
|
||||
_dllController.timer.value = entry;
|
||||
_dllController.infoBarEntry?.close();
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
}
|
||||
@@ -431,7 +433,10 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
title: Text(translations.hostResetName),
|
||||
subtitle: Text(translations.hostResetDescription),
|
||||
content: Button(
|
||||
onPressed: () => showResetDialog(_hostingController.reset),
|
||||
onPressed: () => showResetDialog(() {
|
||||
_hostingController.reset();
|
||||
_dllController.resetServer();
|
||||
}),
|
||||
child: Text(translations.hostResetContent),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/abstract/overlay.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/data.dart';
|
||||
import 'package:reboot_launcher/src/messenger/implementation/onboard.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
@@ -37,6 +39,7 @@ class PlayPage extends RebootPage {
|
||||
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) {
|
||||
@@ -94,6 +97,7 @@ class _PlayPageState extends RebootPageState<PlayPage> {
|
||||
),
|
||||
_options,
|
||||
_internalFiles,
|
||||
_resetDefaults
|
||||
];
|
||||
|
||||
SettingTile get _internalFiles => SettingTile(
|
||||
@@ -106,17 +110,17 @@ class _PlayPageState extends RebootPageState<PlayPage> {
|
||||
createFileSetting(
|
||||
title: translations.settingsClientConsoleName,
|
||||
description: translations.settingsClientConsoleDescription,
|
||||
controller: _settingsController.unrealEngineConsoleDll
|
||||
controller: _dllController.unrealEngineConsoleDll
|
||||
),
|
||||
createFileSetting(
|
||||
title: translations.settingsClientAuthName,
|
||||
description: translations.settingsClientAuthDescription,
|
||||
controller: _settingsController.backendDll
|
||||
controller: _dllController.backendDll
|
||||
),
|
||||
createFileSetting(
|
||||
title: translations.settingsClientMemoryName,
|
||||
description: translations.settingsClientMemoryDescription,
|
||||
controller: _settingsController.memoryLeakDll
|
||||
controller: _dllController.memoryLeakDll
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -141,4 +145,19 @@ class _PlayPageState extends RebootPageState<PlayPage> {
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
SettingTile get _resetDefaults => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
title: Text(translations.gameResetDefaultsName),
|
||||
subtitle: Text(translations.gameResetDefaultsDescription),
|
||||
content: Button(
|
||||
onPressed: () => showResetDialog(() {
|
||||
_gameController.reset();
|
||||
_dllController.resetGame();
|
||||
}),
|
||||
child: Text(translations.gameResetDefaultsContent),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -42,7 +42,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
List<Widget> get settings => [
|
||||
_language,
|
||||
_theme,
|
||||
_resetDefaults,
|
||||
_installationDirectory
|
||||
];
|
||||
|
||||
@@ -89,18 +88,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
))
|
||||
);
|
||||
|
||||
SettingTile get _resetDefaults => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
title: Text(translations.settingsUtilsResetDefaultsName),
|
||||
subtitle: Text(translations.settingsUtilsResetDefaultsSubtitle),
|
||||
content: Button(
|
||||
onPressed: () => showResetDialog(_settingsController.reset),
|
||||
child: Text(translations.settingsUtilsResetDefaultsContent),
|
||||
)
|
||||
);
|
||||
|
||||
SettingTile get _installationDirectory => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.folder_24_regular
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:local_notifier/local_notifier.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
@@ -39,7 +40,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
final HostingController _hostingController = Get.find<HostingController>();
|
||||
final BackendController _backendController = Get.find<BackendController>();
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final DllController _dllController = Get.find<DllController>();
|
||||
|
||||
InfoBarEntry? _gameClientInfoBar;
|
||||
InfoBarEntry? _gameServerInfoBar;
|
||||
@@ -263,15 +264,21 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
"OPENSSL_ia32cap": "~0x20000000"
|
||||
}
|
||||
);
|
||||
final instance = host ? _hostingController.instance.value : _gameController.instance.value;
|
||||
void onGameOutput(String line, bool error) {
|
||||
log("[${host ? 'HOST' : 'GAME'}] ${error ? '[ERROR]' : '[MESSAGE]'} $line");
|
||||
|
||||
handleGameOutput(
|
||||
line: line,
|
||||
host: host,
|
||||
onShutdown: () => _onStop(reason: _StopReason.normal),
|
||||
onTokenError: () => _onStop(reason: _StopReason.tokenError),
|
||||
onBuildCorrupted: () => _onStop(reason: _StopReason.corruptedVersionError),
|
||||
onBuildCorrupted: () {
|
||||
if(instance?.launched == false) {
|
||||
_onStop(reason: _StopReason.corruptedVersionError);
|
||||
}else {
|
||||
_onStop(reason: _StopReason.crash);
|
||||
}
|
||||
},
|
||||
onLoggedIn: () =>_onLoggedIn(host),
|
||||
onMatchEnd: () => _onMatchEnd(version),
|
||||
onDisplayAttached: () => _onDisplayAttached(host, hostType, version)
|
||||
@@ -391,7 +398,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
await _injectOrShowError(InjectableDll.console, host);
|
||||
_onGameClientInjected();
|
||||
}else {
|
||||
final gameServerPort = int.tryParse(_settingsController.gameServerPort.text);
|
||||
final gameServerPort = int.tryParse(_dllController.gameServerPort.text);
|
||||
if(gameServerPort != null) {
|
||||
await killProcessByPort(gameServerPort);
|
||||
}
|
||||
@@ -424,7 +431,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
final gameServerPort = _settingsController.gameServerPort.text;
|
||||
final gameServerPort = _dllController.gameServerPort.text;
|
||||
final localPingResult = await pingGameServer(
|
||||
"127.0.0.1:$gameServerPort",
|
||||
timeout: const Duration(minutes: 2)
|
||||
@@ -605,6 +612,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
);
|
||||
break;
|
||||
case _StopReason.tokenError:
|
||||
_backendController.stop();
|
||||
showRebootInfoBar(
|
||||
translations.tokenError(instance?.injectedDlls.map((element) => element.name).join(", ") ?? translations.none),
|
||||
severity: InfoBarSeverity.error,
|
||||
@@ -615,6 +623,13 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
)
|
||||
);
|
||||
break;
|
||||
case _StopReason.crash:
|
||||
showRebootInfoBar(
|
||||
translations.fortniteCrashError(host ? "game server" : "client"),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration,
|
||||
);
|
||||
break;
|
||||
case _StopReason.unknownError:
|
||||
showRebootInfoBar(
|
||||
translations.unknownFortniteError(error ?? translations.unknownError),
|
||||
@@ -664,7 +679,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
|
||||
Future<File?> _getDllFileOrStop(InjectableDll injectable, bool host, [bool isRetry = false]) async {
|
||||
log("[${host ? 'HOST' : 'GAME'}] Checking dll ${injectable}...");
|
||||
final (file, customDll) = _settingsController.getInjectableData(injectable);
|
||||
final (file, customDll) = _dllController.getInjectableData(injectable);
|
||||
log("[${host ? 'HOST' : 'GAME'}] Path: ${file.path}, custom: $customDll");
|
||||
if(await file.exists()) {
|
||||
log("[${host ? 'HOST' : 'GAME'}] Path exists");
|
||||
@@ -678,7 +693,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
|
||||
log("[${host ? 'HOST' : 'GAME'}] Path does not exist, downloading critical dll again...");
|
||||
await _settingsController.downloadCriticalDllInteractive(file.path);
|
||||
await _dllController.downloadCriticalDllInteractive(file.path);
|
||||
log("[${host ? 'HOST' : 'GAME'}] Downloaded dll again, retrying check...");
|
||||
return _getDllFileOrStop(injectable, host, true);
|
||||
}
|
||||
@@ -731,7 +746,8 @@ enum _StopReason {
|
||||
matchmakerError,
|
||||
tokenError,
|
||||
unknownError,
|
||||
exitCode;
|
||||
exitCode,
|
||||
crash;
|
||||
|
||||
bool get isError => name.contains("Error");
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
name: reboot_launcher
|
||||
description: Graphical User Interface for Project Reboot
|
||||
version: "9.2.4"
|
||||
version: "9.2.5"
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user