From 5d8f6bf0fad9bfbe9f0383b359dd84d0a84b12e3 Mon Sep 17 00:00:00 2001 From: Alessandro Autiero Date: Sun, 23 Mar 2025 20:26:13 +0100 Subject: [PATCH] 10.0.8 --- common/lib/common.dart | 4 +- common/lib/src/constant/game.dart | 1 + common/lib/src/extension/types.dart | 15 -- common/lib/src/util/backend.dart | 4 +- common/lib/src/util/downloader.dart | 5 +- .../process.dart => util/extensions.dart} | 16 ++ common/lib/src/util/game.dart | 191 ++++++++++++++---- common/lib/src/util/log.dart | 11 +- common/pubspec.yaml | 1 + .../src/controller/backend_controller.dart | 2 +- gui/lib/src/controller/game_controller.dart | 16 ++ gui/lib/src/util/os.dart | 31 --- gui/lib/src/util/translations.dart | 1 - .../src/widget/file/file_setting_tile.dart | 1 - gui/lib/src/widget/page/browser_page.dart | 2 +- .../src/widget/version/import_version.dart | 21 +- 16 files changed, 205 insertions(+), 117 deletions(-) delete mode 100644 common/lib/src/extension/types.dart rename common/lib/src/{extension/process.dart => util/extensions.dart} (57%) diff --git a/common/lib/common.dart b/common/lib/common.dart index 9456e7c..cc83a0e 100644 --- a/common/lib/common.dart +++ b/common/lib/common.dart @@ -1,7 +1,6 @@ export 'package:reboot_common/src/constant/backend.dart'; export 'package:reboot_common/src/constant/game.dart'; export 'package:reboot_common/src/constant/supabase.dart'; -export 'package:reboot_common/src/extension/process.dart'; export 'package:reboot_common/src/model/fortnite_build.dart'; export 'package:reboot_common/src/model/fortnite_version.dart'; export 'package:reboot_common/src/model/game_instance.dart'; @@ -15,4 +14,5 @@ export 'package:reboot_common/src/util/backend.dart'; export 'package:reboot_common/src/util/downloader.dart'; export 'package:reboot_common/src/util/os.dart'; export 'package:reboot_common/src/util/log.dart'; -export 'package:reboot_common/src/util/game.dart'; \ No newline at end of file +export 'package:reboot_common/src/util/game.dart'; +export 'package:reboot_common/src/util/extensions.dart'; \ No newline at end of file diff --git a/common/lib/src/constant/game.dart b/common/lib/src/constant/game.dart index 5b3bc01..0df782e 100644 --- a/common/lib/src/constant/game.dart +++ b/common/lib/src/constant/game.dart @@ -29,4 +29,5 @@ const String kDisplayInitializedLine = "Initialized"; const String kShippingExe = "FortniteClient-Win64-Shipping.exe"; const String kLauncherExe = "FortniteLauncher.exe"; const String kEacExe = "FortniteClient-Win64-Shipping_EAC.exe"; +const String kCrashReportExe = "CrashReportClient.exe"; final Version kMaxAllowedVersion = Version.parse("30.10"); \ No newline at end of file diff --git a/common/lib/src/extension/types.dart b/common/lib/src/extension/types.dart deleted file mode 100644 index 577ca04..0000000 --- a/common/lib/src/extension/types.dart +++ /dev/null @@ -1,15 +0,0 @@ -extension StringExtension on String { - bool get isBlank { - if(isEmpty) { - return true; - } - - for(var char in this.split("")) { - if(char != " ") { - return false; - } - } - - return true; - } -} \ No newline at end of file diff --git a/common/lib/src/util/backend.dart b/common/lib/src/util/backend.dart index 0c4b36c..9dc1bf6 100644 --- a/common/lib/src/util/backend.dart +++ b/common/lib/src/util/backend.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:ini/ini.dart'; import 'package:reboot_common/common.dart'; -import 'package:reboot_common/src/extension/types.dart'; + import 'package:shelf/shelf_io.dart'; import 'package:shelf_proxy/shelf_proxy.dart'; import 'package:sync/semaphore.dart'; @@ -234,7 +234,7 @@ Future writeMatchmakingIp(String text) async { final splitIndex = text.indexOf(":"); final ip = splitIndex != -1 ? text.substring(0, splitIndex) : text; var port = splitIndex != -1 ? text.substring(splitIndex + 1) : kDefaultGameServerPort; - if(port.isBlank) { + if(port.isBlankOrEmpty) { port = kDefaultGameServerPort; } diff --git a/common/lib/src/util/downloader.dart b/common/lib/src/util/downloader.dart index 5dd2f32..5e771ff 100644 --- a/common/lib/src/util/downloader.dart +++ b/common/lib/src/util/downloader.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:isolate'; -import 'package:reboot_common/src/extension/types.dart'; import 'package:uuid/uuid.dart'; @@ -361,7 +360,7 @@ Future _extractArchive(Completer stopped, String extension, File ); }); process.stdError.listen((data) { - if(!data.isBlank) { + if(!data.isBlankOrEmpty) { _onError(data, options); } }); @@ -418,7 +417,7 @@ Future _extractArchive(Completer stopped, String extension, File ); }); process.stdError.listen((data) { - if(!data.isBlank) { + if(!data.isBlankOrEmpty) { _onError(data, options); } }); diff --git a/common/lib/src/extension/process.dart b/common/lib/src/util/extensions.dart similarity index 57% rename from common/lib/src/extension/process.dart rename to common/lib/src/util/extensions.dart index b0d2376..d2de7af 100644 --- a/common/lib/src/extension/process.dart +++ b/common/lib/src/util/extensions.dart @@ -5,4 +5,20 @@ extension ProcessExtension on Process { Stream get stdOutput => this.stdout.expand((event) => utf8.decode(event, allowMalformed: true).split("\n")); Stream get stdError => this.stderr.expand((event) => utf8.decode(event, allowMalformed: true).split("\n")); +} + +extension StringExtension on String { + bool get isBlankOrEmpty { + if(isEmpty) { + return true; + } + + for(var char in this.split("")) { + if(char != " ") { + return false; + } + } + + return true; + } } \ No newline at end of file diff --git a/common/lib/src/util/game.dart b/common/lib/src/util/game.dart index cb313e8..92114d5 100644 --- a/common/lib/src/util/game.dart +++ b/common/lib/src/util/game.dart @@ -6,6 +6,7 @@ import 'dart:typed_data'; import 'package:ffi/ffi.dart'; import 'package:reboot_common/common.dart'; import 'package:win32/win32.dart'; +import 'package:path/path.dart' as path; final DynamicLibrary _shell32 = DynamicLibrary.open('shell32.dll'); final SHGetPropertyStoreFromParsingName = @@ -32,6 +33,122 @@ final Uint8List _patchedMatchmaking = Uint8List.fromList([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]); +// https://github.com/polynite/fn-releases +const Map _buildToGameVersion = { + 2870186: "1.0.0", + 3700114: "1.7.2", + 3724489: "1.8.0", + 3729133: "1.8.1", + 3741772: "1.8.2", + 3757339: "1.9", + 3775276: "1.9.1", + 3790078: "1.10", + 3807424: "1.11", + 3825894: "2.1", + 3841827: "2.2", + 3847564: "2.3", + 3858292: "2.4", + 3870737: "2.4.2", + 3889387: "2.5", + 3901517: "3.0.0", + 3915963: "3.1", + 3917250: "3.1.1", + 3935073: "3.2", + 3942182: "3.3", + 4008490: "3.5", + 4019403: "3.6", + 4039451: "4.0", + 4053532: "4.1", + 4072250: "4.2", + 4117433: "4.4", + 4127312: "4.4.1", + 4159770: "4.5", + 4204761: "5.0", + 4214610: "5.01", + 4240749: "5.10", + 4288479: "5.21", + 4305896: "5.30", + 4352937: "5.40", + 4363240: "5.41", + 4395664: "6.0", + 4424678: "6.01", + 4461277: "6.0.2", + 4464155: "6.10", + 4476098: "6.10.1", + 4480234: "6.10.2", + 4526925: "6.21", + 4543176: "6.22", + 4573279: "6.31", + 4629139: "7.0", + 4667333: "7.10", + 4727874: "7.20", + 4834550: "7.30", + 5046157: "7.40", + 5203069: "8.00", + 5625478: "8.20", + 5793395: "8.30", + 6005771: "8.40", + 6058028: "8.50", + 6165369: "8.51", + 6337466: "9.00", + 6428087: "9.01", + 6639283: "9.10", + 6922310: "9.21", + 7095426: "9.30", + 7315705: "9.40", + 7609292: "9.41", + 7704164: "10.00", + 7955722: "10.10", + 8456527: "10.20", + 8723043: "10.31", + 9380822: "10.40", + 9603448: "11.00", + 9901083: "11.10", + 10708866: "11.30", + 10800459: "11.31", + 11265652: "11.50", + 11556442: "12.00", + 11883027: "12.10", + 12353830: "12.21", + 12905909: "12.41", + 13137020: "12.50", + 13498980: "12.61", + 14113327: "13.40", + 14211474: "14.00", + 14456520: "14.30", + 14550713: "14.40", + 14786821: "14.60", + 14835335: "15.00", + 15014719: "15.10", + 15341163: "15.30", + 15526472: "15.50", + 15913292: "16.10", + 16163563: "16.30", + 16218553: "16.40", + 16469788: "16.50", + 16745144: "17.10", + 17004569: "17.30", + 17269705: "17.40", + 17388565: "17.50", + 17468642: "18.00", + 17661844: "18.10", + 17745267: "18.20", + 17811397: "18.21", + 17882303: "18.30", + 18163738: "18.40", + 18489740: "19.01", + 18675304: "19.10", + 19458861: "20.00", + 19598943: "20.10", + 19751212: "20.20", + 19950687: "20.30", + 20244966: "20.40", + 20463113: "21.00", + 20696680: "21.10", + 21035704: "21.20", + 21657658: "21.50", +}; + Future patchHeadless(File file) async => await _patch(file, _originalHeadless, _patchedHeadless); @@ -179,8 +296,23 @@ String _parseUsername(String username, bool host) { return username; } -Future extractGameVersion(String filePath, String defaultGameVersion) => Isolate.run(() { - final filePathPtr = filePath.toNativeUtf16(); +// Parsing the version is not that easy +// Also on some versions the shipping exe has it as well, but not on all: that's why i'm using the crash report client +// ++Fortnite+Release-34.10-CL-40567068 +// 4.16.0-3700114+++Fortnite+Release-Cert +// 4.19.0-3870737+++Fortnite+Release-Next +// 4.20.0-4008490+++Fortnite+Release-3.5 +Future extractGameVersion(Directory directory) => Isolate.run(() async { + log("[VERSION] Looking for $kCrashReportExe in ${directory.path}"); + final defaultGameVersion = path.basename(directory.path); + final crashReportClients = await findFiles(directory, kCrashReportExe); + if (crashReportClients.isEmpty) { + log("[VERSION] Didn't find a unique match: $crashReportClients"); + return defaultGameVersion; + } + + log("[VERSION] Extracting game version from ${crashReportClients.last.path}(default: $defaultGameVersion)"); + final filePathPtr = crashReportClients.last.path.toNativeUtf16(); final pPropertyStore = calloc(); final iidPropertyStore = GUIDFromString(IID_IPropertyStore); final ret = SHGetPropertyStoreFromParsingName( @@ -195,8 +327,9 @@ Future extractGameVersion(String filePath, String defaultGameVersion) => calloc.free(iidPropertyStore); if (FAILED(ret)) { + log("[VERSION] Using default value"); calloc.free(pPropertyStore); - throw WindowsException(ret); + return defaultGameVersion; } final propertyStore = IPropertyStore(pPropertyStore); @@ -206,7 +339,8 @@ Future extractGameVersion(String filePath, String defaultGameVersion) => final count = countPtr.value; calloc.free(countPtr); if (FAILED(hrCount)) { - throw WindowsException(hrCount); + log("[VERSION] Using default value"); + return defaultGameVersion; } for (var i = 0; i < count; i++) { @@ -222,48 +356,20 @@ Future extractGameVersion(String filePath, String defaultGameVersion) => if (!FAILED(hrValue)) { if (pv.ref.vt == VARENUM.VT_LPWSTR) { final valueStr = pv.ref.pwszVal.toDartString(); - if (valueStr.contains("+++Fortnite")) { - var gameVersion = valueStr.substring(valueStr.lastIndexOf("-") + 1); - if(gameVersion == "Cert") { + final headerIndex = valueStr.indexOf("++Fortnite"); + if (headerIndex != -1) { + log("[VERSION] Found value string: $valueStr"); + var gameVersion = valueStr.substring(valueStr.indexOf("-", headerIndex) + 1); + log("[VERSION] Game version: $gameVersion"); + if(gameVersion == "Cert" || gameVersion == "Next") { final engineVersion = valueStr.substring(0, valueStr.indexOf("+")); + log("[VERSION] Engine version: $engineVersion"); final engineVersionParts = engineVersion.split("-"); final engineVersionBuild = int.parse(engineVersionParts[1]); - switch (engineVersionBuild) { - case 2870186: - gameVersion = "OT6.5"; - break; - case 3700114: - gameVersion = "1.7.2"; - break; - case 3724489: - gameVersion = "1.8.0"; - break; - case 3729133: - gameVersion = "1.8.1"; - break; - case 3741772: - gameVersion = "1.8.2"; - break; - case 3757339: - gameVersion = "1.9"; - break; - case 3775276: - gameVersion = "1.9.1"; - break; - case 3790078: - gameVersion = "1.10"; - break; - case 3807424: - gameVersion = "1.11"; - break; - case 3825894: - gameVersion = "2.1"; - break; - default: - gameVersion = defaultGameVersion; - break; - } + log("[VERSION] Engine build: $engineVersionBuild"); + gameVersion = _buildToGameVersion[engineVersionBuild] ?? defaultGameVersion; } + log("[VERSION] Returning $gameVersion"); return gameVersion; } } @@ -272,5 +378,6 @@ Future extractGameVersion(String filePath, String defaultGameVersion) => calloc.free(pv); } + log("[VERSION] Using default value"); return defaultGameVersion; }); \ No newline at end of file diff --git a/common/lib/src/util/log.dart b/common/lib/src/util/log.dart index f299e48..881df0e 100644 --- a/common/lib/src/util/log.dart +++ b/common/lib/src/util/log.dart @@ -1,10 +1,9 @@ import 'dart:io'; import 'package:reboot_common/common.dart'; -import 'package:sync/semaphore.dart'; +import 'package:synchronized/extension.dart'; final File launcherLogFile = _createLoggingFile(); -final Semaphore _semaphore = Semaphore(1); bool enableLoggingToConsole = true; File _createLoggingFile() { @@ -19,14 +18,14 @@ File _createLoggingFile() { void log(String message) async { try { - await _semaphore.acquire(); if(enableLoggingToConsole) { print(message); } - await launcherLogFile.writeAsString("$message\n", mode: FileMode.append, flush: true); + + launcherLogFile.synchronized(() async { + await launcherLogFile.writeAsString("$message\n", mode: FileMode.append, flush: true); + }); }catch(error) { print("[LOGGER_ERROR] An error occurred while logging: $error"); - }finally { - _semaphore.release(); } } \ No newline at end of file diff --git a/common/pubspec.yaml b/common/pubspec.yaml index 5cb2d84..7b89d51 100644 --- a/common/pubspec.yaml +++ b/common/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: uuid: ^4.5.1 shelf_web_socket: ^2.0.0 version: ^3.0.2 + synchronized: ^3.3.0+3 dev_dependencies: flutter_lints: ^5.0.0 \ No newline at end of file diff --git a/gui/lib/src/controller/backend_controller.dart b/gui/lib/src/controller/backend_controller.dart index e74ba43..a428d19 100644 --- a/gui/lib/src/controller/backend_controller.dart +++ b/gui/lib/src/controller/backend_controller.dart @@ -396,7 +396,7 @@ class BackendController extends GetxController { } final version = Get.find() - .getVersionByName(server.version.toString()); + .getVersionByGame(server.version.toString()); if(version == null) { _showRebootInfoBar( translations.cannotJoinServerVersion(server.version.toString()), diff --git a/gui/lib/src/controller/game_controller.dart b/gui/lib/src/controller/game_controller.dart index 8d85aab..1e88c72 100644 --- a/gui/lib/src/controller/game_controller.dart +++ b/gui/lib/src/controller/game_controller.dart @@ -7,6 +7,7 @@ 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:version/version.dart'; class GameController extends GetxController { static const String storageName = "v3_game_storage"; @@ -56,6 +57,21 @@ class GameController extends GetxController { return versions.value.firstWhereOrNull((element) => element.name == name); } + FortniteVersion? getVersionByGame(String gameVersion) { + gameVersion = gameVersion.trim(); + final parsedGameVersion = Version.parse(gameVersion); + return versions.value.firstWhereOrNull((element) { + final compare = element.gameVersion.trim(); + try { + final parsedCompare = Version.parse(compare); + return parsedCompare.major == parsedGameVersion.major + && parsedCompare.minor == parsedGameVersion.minor; + } on FormatException { + return compare == gameVersion; + } + }); + } + void addVersion(FortniteVersion version) { versions.update((val) => val?.add(version)); selectedVersion.value = version; diff --git a/gui/lib/src/util/os.dart b/gui/lib/src/util/os.dart index f0b2a3c..dfe1057 100644 --- a/gui/lib/src/util/os.dart +++ b/gui/lib/src/util/os.dart @@ -47,37 +47,6 @@ Future openFilePicker(String extension) async { bool get isDarkMode => SchedulerBinding.instance.platformDispatcher.platformBrightness.isDark; -class _ServiceProvider10 extends IUnknown { - static const String _CLSID = "{C2F03A33-21F5-47FA-B4BB-156362A2F239}"; - static const String _IID = "{6D5140C1-7436-11CE-8034-00AA006009FA}"; - - _ServiceProvider10._internal(Pointer ptr) : super(ptr); - - factory _ServiceProvider10.createInstance() => - _ServiceProvider10._internal(COMObject.createFromID(_CLSID, _IID)); - - Pointer queryService(String classId, String instanceId) { - final result = calloc(); - final code = (ptr.ref.vtable + 3) - .cast< - Pointer< - NativeFunction< - HRESULT Function(Pointer, Pointer, Pointer, - Pointer)>>>() - .value - .asFunction< - int Function(Pointer, Pointer, Pointer, - Pointer)>()(ptr.ref.lpVtbl, - GUIDFromString(classId), GUIDFromString(instanceId), result); - if (code != 0) { - free(result); - throw WindowsException(code); - } - - return result; - } -} - extension WindowManagerExtension on WindowManager { Future maximizeOrRestore() async => await windowManager.isMaximized() ? windowManager.restore() : windowManager.maximize(); } diff --git a/gui/lib/src/util/translations.dart b/gui/lib/src/util/translations.dart index 4a7638b..fc5c946 100644 --- a/gui/lib/src/util/translations.dart +++ b/gui/lib/src/util/translations.dart @@ -1,7 +1,6 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_gen/gen_l10n/reboot_localizations.dart'; import 'package:intl/intl.dart'; -import 'package:reboot_common/common.dart'; AppLocalizations? _translations; bool _init = false; diff --git a/gui/lib/src/widget/file/file_setting_tile.dart b/gui/lib/src/widget/file/file_setting_tile.dart index 86a3651..b26fbe8 100644 --- a/gui/lib/src/widget/file/file_setting_tile.dart +++ b/gui/lib/src/widget/file/file_setting_tile.dart @@ -38,7 +38,6 @@ SettingTile createFileSetting({ controller: controller, validator: (text) { final result = _checkDll(text); - print("Called validator: $result"); obx.value = result; return result; }, diff --git a/gui/lib/src/widget/page/browser_page.dart b/gui/lib/src/widget/page/browser_page.dart index ab6c72f..1d52202 100644 --- a/gui/lib/src/widget/page/browser_page.dart +++ b/gui/lib/src/widget/page/browser_page.dart @@ -180,7 +180,7 @@ class _BrowsePageState extends RebootPageState { case _Filter.accessible: return element.password == null; case _Filter.playable: - return _gameController.getVersionByName(element.version) != null; + return _gameController.getVersionByGame(element.version) != null; } }).toList(); final sort = _sort.value; diff --git a/gui/lib/src/widget/version/import_version.dart b/gui/lib/src/widget/version/import_version.dart index 3f7d414..1ef735d 100644 --- a/gui/lib/src/widget/version/import_version.dart +++ b/gui/lib/src/widget/version/import_version.dart @@ -147,27 +147,24 @@ class _ImportVersionDialogState extends State { final name = _nameController.text.trim(); final directory = Directory(_pathController.text.trim()); - final files = await Future.wait([ + final shippingExes = await Future.wait([ Future.delayed(const Duration(seconds: 1)).then((_) => []), - findFiles(directory, kShippingExe).then((files) async { - if(files.length == 1) { - await patchHeadless(files.first); - } - return files; - }) + findFiles(directory, kShippingExe) ]).then((values) => values.expand((entry) => entry).toList()); - if (files.isEmpty) { + if (shippingExes.isEmpty) { _validator.value = _ImportState.missingShippingExeError; return; } - if(files.length != 1) { + if(shippingExes.length != 1) { _validator.value = _ImportState.multipleShippingExesError; return; } - final gameVersion = await extractGameVersion(files.first.path, path.basename(directory.path)); + await patchHeadless(shippingExes.first); + + final gameVersion = await extractGameVersion(directory); try { if(Version.parse(gameVersion) >= kMaxAllowedVersion) { _validator.value = _ImportState.unsupportedVersionError; @@ -181,13 +178,13 @@ class _ImportVersionDialogState extends State { final version = FortniteVersion( name: name, gameVersion: gameVersion, - location: files.first.parent + location: shippingExes.first.parent ); _gameController.addVersion(version); }else { widget.version?.name = name; widget.version?.gameVersion = gameVersion; - widget.version?.location = files.first.parent; + widget.version?.location = shippingExes.first.parent; } _validator.value = _ImportState.success; }