Merge pull request #57 from Auties00/_onLoggedIn

9.1.4
This commit is contained in:
Alessandro Autiero
2024-06-15 19:57:54 +02:00
committed by GitHub
144 changed files with 442 additions and 278 deletions

4
backend/README.md Normal file
View File

@@ -0,0 +1,4 @@
# Backend
Fork of LawinV1
Awaiting rewrite in Dart
Use build.bat to generate the executable

View File

View File

View File

View File

View File

View File

View File

View File

View File

@@ -51,7 +51,7 @@ void main(List<String> args) async {
} }
stdout.writeln("Launching game..."); stdout.writeln("Launching game...");
var executable = version.gameExecutable; var executable = version.shippingExecutable;
if(executable == null){ if(executable == null){
throw Exception("Missing game executable at: ${version.location.path}"); throw Exception("Missing game executable at: ${version.location.path}");
} }

View File

@@ -12,7 +12,7 @@ Future<void> startGame() async {
await _startLauncherProcess(version); await _startLauncherProcess(version);
await _startEacProcess(version); await _startEacProcess(version);
var executable = await version.gameExecutable; var executable = await version.shippingExecutable;
if (executable == null) { if (executable == null) {
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?"); throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
} }

View File

@@ -5,6 +5,8 @@ import 'package:path/path.dart' as path;
import 'package:reboot_common/common.dart'; import 'package:reboot_common/common.dart';
extension FortniteVersionExtension on FortniteVersion { extension FortniteVersionExtension on FortniteVersion {
static DateTime _marker = DateTime.fromMicrosecondsSinceEpoch(0);
static File? findExecutable(Directory directory, String name) { static File? findExecutable(Directory directory, String name) {
try{ try{
final result = directory.listSync(recursive: true) final result = directory.listSync(recursive: true)
@@ -15,23 +17,20 @@ extension FortniteVersionExtension on FortniteVersion {
} }
} }
File? get gameExecutable => findExecutable(location, "FortniteClient-Win64-Shipping.exe"); Future<File?> get shippingExecutable async {
final result = findExecutable(location, "FortniteClient-Win64-Shipping.exe");
Future<File?> get headlessGameExecutable async { if(result == null) {
final result = findExecutable(location, "FortniteClient-Win64-Shipping-Headless.exe");
if(result != null) {
return result;
}
final original = findExecutable(location, "FortniteClient-Win64-Shipping.exe");
if(original == null) {
return null; return null;
} }
final output = File("${original.parent.path}\\FortniteClient-Win64-Shipping-Headless.exe"); final lastModified = await result.lastModified();
await original.copy(output.path); if(lastModified != _marker) {
await Isolate.run(() => patchHeadless(output)); print("Applying patch");
return output; await Isolate.run(() => patchHeadless(result));
await result.setLastModified(_marker);
}
return result;
} }
File? get launcherExecutable => findExecutable(location, "FortniteLauncher.exe"); File? get launcherExecutable => findExecutable(location, "FortniteLauncher.exe");

View File

@@ -14,4 +14,4 @@ class FortniteVersion {
'name': name, 'name': name,
'location': location.path 'location': location.path
}; };
} }

View File

@@ -9,7 +9,7 @@ class GameInstance {
final int? launcherPid; final int? launcherPid;
final int? eacPid; final int? eacPid;
final List<InjectableDll> injectedDlls; final List<InjectableDll> injectedDlls;
bool hosting; final GameServerType? serverType;
bool launched; bool launched;
bool movedToVirtualDesktop; bool movedToVirtualDesktop;
bool tokenError; bool tokenError;
@@ -20,7 +20,7 @@ class GameInstance {
required this.gamePid, required this.gamePid,
required this.launcherPid, required this.launcherPid,
required this.eacPid, required this.eacPid,
required this.hosting, required this.serverType,
required this.child required this.child
}): tokenError = false, launched = false, movedToVirtualDesktop = false, injectedDlls = []; }): tokenError = false, launched = false, movedToVirtualDesktop = false, injectedDlls = [];
@@ -37,7 +37,7 @@ class GameInstance {
bool get nestedHosting { bool get nestedHosting {
GameInstance? child = this; GameInstance? child = this;
while(child != null) { while(child != null) {
if(child.hosting) { if(child.serverType != null) {
return true; return true;
} }
@@ -46,4 +46,10 @@ class GameInstance {
return false; return false;
} }
}
enum GameServerType {
headless,
virtualWindow,
window
} }

View File

@@ -67,7 +67,7 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
} }
final startTime = DateTime.now().millisecondsSinceEpoch; final startTime = DateTime.now().millisecondsSinceEpoch;
final response = _downloadArchive(options, tempFile, startTime); final response = _downloadArchive(options, stopped, tempFile, startTime);
await Future.any([stopped.future, response]); await Future.any([stopped.future, response]);
if(!stopped.isCompleted) { if(!stopped.isCompleted) {
await _extractArchive(stopped, extension, tempFile, options); await _extractArchive(stopped, extension, tempFile, options);
@@ -79,13 +79,17 @@ Future<void> downloadArchiveBuild(FortniteBuildDownloadOptions options) async {
} }
} }
Future<void> _downloadArchive(FortniteBuildDownloadOptions options, File tempFile, int startTime, [int? byteStart = null, int errorsCount = 0]) async { Future<void> _downloadArchive(FortniteBuildDownloadOptions options, Completer stopped, File tempFile, int startTime, [int? byteStart = null, int errorsCount = 0]) async {
var received = byteStart ?? 0; var received = byteStart ?? 0;
try { try {
await _dio.download( await _dio.download(
options.build.link, options.build.link,
tempFile.path, tempFile.path,
onReceiveProgress: (data, length) { onReceiveProgress: (data, length) {
if(stopped.isCompleted) {
throw StateError("Download interrupted");
}
received = data; received = data;
final percentage = (received / length) * 100; final percentage = (received / length) * 100;
_onProgress(startTime, percentage < 1 ? null : DateTime.now().millisecondsSinceEpoch, percentage, false, options); _onProgress(startTime, percentage < 1 ? null : DateTime.now().millisecondsSinceEpoch, percentage, false, options);
@@ -116,12 +120,16 @@ Future<void> _downloadArchive(FortniteBuildDownloadOptions options, File tempFil
) )
); );
}catch(error) { }catch(error) {
if(stopped.isCompleted) {
return;
}
if(errorsCount > _maxErrors || error.toString().contains(_deniedConnectionError) || error.toString().contains(_unavailableError)) { if(errorsCount > _maxErrors || error.toString().contains(_deniedConnectionError) || error.toString().contains(_unavailableError)) {
_onError(error, options); _onError(error, options);
return; return;
} }
await _downloadArchive(options, tempFile, startTime, received, errorsCount + 1); await _downloadArchive(options, stopped, tempFile, startTime, received, errorsCount + 1);
} }
} }
@@ -225,6 +233,7 @@ Future<void> _extractArchive(Completer<dynamic> stopped, String extension, File
} }
await Future.any([stopped.future, process.exitCode]); await Future.any([stopped.future, process.exitCode]);
process.kill(ProcessSignal.sigabrt);
} }
void _onProgress(int startTime, int? now, double percentage, bool extracting, FortniteBuildDownloadOptions options) { void _onProgress(int startTime, int? now, double percentage, bool extracting, FortniteBuildDownloadOptions options) {

View File

@@ -18,7 +18,6 @@ Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force =
} }
Future<void> downloadCriticalDll(String name, String outputPath) async { Future<void> downloadCriticalDll(String name, String outputPath) async {
print("https://github.com/Auties00/reboot_launcher/raw/master/gui/dependencies/dlls/$name");
final response = await http.get(Uri.parse("https://github.com/Auties00/reboot_launcher/raw/master/gui/dependencies/dlls/$name")); final response = await http.get(Uri.parse("https://github.com/Auties00/reboot_launcher/raw/master/gui/dependencies/dlls/$name"));
if(response.statusCode != 200) { if(response.statusCode != 200) {
throw Exception("Cannot download $name: status code ${response.statusCode}"); throw Exception("Cannot download $name: status code ${response.statusCode}");

View File

@@ -9,6 +9,7 @@ final Uint8List _patchedHeadless = Uint8List.fromList([
45, 0, 108, 0, 111, 0, 103, 0, 32, 0, 45, 0, 110, 0, 111, 0, 115, 0, 112, 0, 108, 0, 97, 0, 115, 0, 104, 0, 32, 0, 45, 0, 110, 0, 111, 0, 115, 0, 111, 0, 117, 0, 110, 0, 100, 0, 32, 0, 45, 0, 110, 0, 117, 0, 108, 0, 108, 0, 114, 0, 104, 0, 105, 0, 32, 0, 45, 0, 117, 0, 115, 0, 101, 0, 111, 0, 108, 0, 100, 0, 105, 0, 116, 0, 101, 0, 109, 0, 99, 0, 97, 0, 114, 0, 100, 0, 115, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0 45, 0, 108, 0, 111, 0, 103, 0, 32, 0, 45, 0, 110, 0, 111, 0, 115, 0, 112, 0, 108, 0, 97, 0, 115, 0, 104, 0, 32, 0, 45, 0, 110, 0, 111, 0, 115, 0, 111, 0, 117, 0, 110, 0, 100, 0, 32, 0, 45, 0, 110, 0, 117, 0, 108, 0, 108, 0, 114, 0, 104, 0, 105, 0, 32, 0, 45, 0, 117, 0, 115, 0, 101, 0, 111, 0, 108, 0, 100, 0, 105, 0, 116, 0, 101, 0, 109, 0, 99, 0, 97, 0, 114, 0, 100, 0, 115, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0, 32, 0
]); ]);
// Not used right now
final Uint8List _originalMatchmaking = Uint8List.fromList([ final Uint8List _originalMatchmaking = Uint8List.fromList([
63, 0, 69, 0, 110, 0, 99, 0, 114, 0, 121, 0, 112, 0, 116, 0, 105, 0, 111, 0, 110, 0, 84, 0, 111, 0, 107, 0, 101, 0, 110, 0, 61 63, 0, 69, 0, 110, 0, 99, 0, 114, 0, 121, 0, 112, 0, 116, 0, 105, 0, 111, 0, 110, 0, 84, 0, 111, 0, 107, 0, 101, 0, 110, 0, 61
]); ]);
@@ -18,7 +19,7 @@ final Uint8List _patchedMatchmaking = Uint8List.fromList([
]); ]);
Future<bool> patchHeadless(File file) async => Future<bool> patchHeadless(File file) async =>
_patch(file, _originalHeadless, _patchedHeadless); await _patch(file, _originalHeadless, _patchedHeadless);
Future<bool> patchMatchmaking(File file) async => Future<bool> patchMatchmaking(File file) async =>
await _patch(file, _originalMatchmaking, _patchedMatchmaking); await _patch(file, _originalMatchmaking, _patchedMatchmaking);
@@ -29,22 +30,24 @@ Future<bool> _patch(File file, Uint8List original, Uint8List patched) async {
throw Exception("Cannot mutate length of binary file"); throw Exception("Cannot mutate length of binary file");
} }
final read = await file.readAsBytes(); final source = await file.readAsBytes();
final length = await file.length();
var readOffset = 0; var readOffset = 0;
var patchOffset = -1; var patchOffset = -1;
var patchCount = 0; var patchCount = 0;
while(readOffset < length){ while(readOffset < source.length){
if(read[readOffset] == original[patchCount]){ if(source[readOffset] == original[patchCount]){
if(patchOffset == -1) { if(patchOffset == -1) {
patchOffset = readOffset; patchOffset = readOffset;
} }
if(++patchCount == original.length) { if(readOffset - patchOffset + 1 == original.length) {
break; break;
} }
patchCount++;
}else { }else {
patchOffset = -1; patchOffset = -1;
patchCount = 0;
} }
readOffset++; readOffset++;
@@ -55,10 +58,10 @@ Future<bool> _patch(File file, Uint8List original, Uint8List patched) async {
} }
for(var i = 0; i < patched.length; i++) { for(var i = 0; i < patched.length; i++) {
read[patchOffset + i] = patched[i]; source[patchOffset + i] = patched[i];
} }
await file.writeAsBytes(read, flush: true); await file.writeAsBytes(source, flush: true);
return true; return true;
}catch(_){ }catch(_){
return false; return false;

View File

@@ -104,9 +104,9 @@ Future<bool> startElevatedProcess({required String executable, required String a
return shellResult == 1; return shellResult == 1;
} }
Future<Process> startProcess({required File executable, List<String>? args, bool wrapProcess = true, bool window = false, String? name}) async { Future<Process> startProcess({required File executable, List<String>? args, bool useTempBatch = true, bool window = false, String? name}) async {
final argsOrEmpty = args ?? []; final argsOrEmpty = args ?? [];
if(wrapProcess) { if(useTempBatch) {
final tempScriptDirectory = await tempDirectory.createTemp("reboot_launcher_process"); final tempScriptDirectory = await tempDirectory.createTemp("reboot_launcher_process");
final tempScriptFile = File("${tempScriptDirectory.path}/process.bat"); final tempScriptFile = File("${tempScriptDirectory.path}/process.bat");
final command = window ? 'cmd.exe /k ""${executable.path}" ${argsOrEmpty.join(" ")}"' : '"${executable.path}" ${argsOrEmpty.join(" ")}'; final command = window ? 'cmd.exe /k ""${executable.path}" ${argsOrEmpty.join(" ")}"' : '"${executable.path}" ${argsOrEmpty.join(" ")}';
@@ -202,7 +202,7 @@ Future<bool> watchProcess(int pid) async {
return await completer.future; return await completer.future;
} }
List<String> createRebootArgs(String username, String password, bool host, bool headless, String additionalArgs) { List<String> createRebootArgs(String username, String password, bool host, GameServerType hostType, bool log, String additionalArgs) {
if(password.isEmpty) { if(password.isEmpty) {
username = '${_parseUsername(username, host)}@projectreboot.dev'; username = '${_parseUsername(username, host)}@projectreboot.dev';
} }
@@ -223,12 +223,18 @@ List<String> createRebootArgs(String username, String password, bool host, bool
"-AUTH_TYPE=epic" "-AUTH_TYPE=epic"
]; ];
if(host && headless){ if(log) {
args.add("-log");
}
if(host) {
args.addAll([ args.addAll([
"-nullrhi",
"-nosplash", "-nosplash",
"-nosound", "-nosound"
]); ]);
if(hostType == GameServerType.headless){
args.add("-nullrhi");
}
} }
if(additionalArgs.isNotEmpty){ if(additionalArgs.isNotEmpty){

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

View File

@@ -0,0 +1,2 @@
No, skins don't work in Reboot.
This is because Epic asked us to remove them.

Some files were not shown because too many files have changed in this diff Show More