This commit is contained in:
Alessandro Autiero
2024-06-15 17:57:17 +02:00
parent 2bf084d120
commit e24f4e97b3
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...");
var executable = version.gameExecutable;
var executable = version.shippingExecutable;
if(executable == null){
throw Exception("Missing game executable at: ${version.location.path}");
}

View File

@@ -12,7 +12,7 @@ Future<void> startGame() async {
await _startLauncherProcess(version);
await _startEacProcess(version);
var executable = await version.gameExecutable;
var executable = await version.shippingExecutable;
if (executable == null) {
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';
extension FortniteVersionExtension on FortniteVersion {
static DateTime _marker = DateTime.fromMicrosecondsSinceEpoch(0);
static File? findExecutable(Directory directory, String name) {
try{
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 headlessGameExecutable async {
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) {
Future<File?> get shippingExecutable async {
final result = findExecutable(location, "FortniteClient-Win64-Shipping.exe");
if(result == null) {
return null;
}
final output = File("${original.parent.path}\\FortniteClient-Win64-Shipping-Headless.exe");
await original.copy(output.path);
await Isolate.run(() => patchHeadless(output));
return output;
final lastModified = await result.lastModified();
if(lastModified != _marker) {
print("Applying patch");
await Isolate.run(() => patchHeadless(result));
await result.setLastModified(_marker);
}
return result;
}
File? get launcherExecutable => findExecutable(location, "FortniteLauncher.exe");

View File

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

View File

@@ -9,7 +9,7 @@ class GameInstance {
final int? launcherPid;
final int? eacPid;
final List<InjectableDll> injectedDlls;
bool hosting;
final GameServerType? serverType;
bool launched;
bool movedToVirtualDesktop;
bool tokenError;
@@ -20,7 +20,7 @@ class GameInstance {
required this.gamePid,
required this.launcherPid,
required this.eacPid,
required this.hosting,
required this.serverType,
required this.child
}): tokenError = false, launched = false, movedToVirtualDesktop = false, injectedDlls = [];
@@ -37,7 +37,7 @@ class GameInstance {
bool get nestedHosting {
GameInstance? child = this;
while(child != null) {
if(child.hosting) {
if(child.serverType != null) {
return true;
}
@@ -46,4 +46,10 @@ class GameInstance {
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 response = _downloadArchive(options, tempFile, startTime);
final response = _downloadArchive(options, stopped, tempFile, startTime);
await Future.any([stopped.future, response]);
if(!stopped.isCompleted) {
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;
try {
await _dio.download(
options.build.link,
tempFile.path,
onReceiveProgress: (data, length) {
if(stopped.isCompleted) {
throw StateError("Download interrupted");
}
received = data;
final percentage = (received / length) * 100;
_onProgress(startTime, percentage < 1 ? null : DateTime.now().millisecondsSinceEpoch, percentage, false, options);
@@ -116,12 +120,16 @@ Future<void> _downloadArchive(FortniteBuildDownloadOptions options, File tempFil
)
);
}catch(error) {
if(stopped.isCompleted) {
return;
}
if(errorsCount > _maxErrors || error.toString().contains(_deniedConnectionError) || error.toString().contains(_unavailableError)) {
_onError(error, options);
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]);
process.kill(ProcessSignal.sigabrt);
}
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 {
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"));
if(response.statusCode != 200) {
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
]);
// Not used right now
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
]);
@@ -18,7 +19,7 @@ final Uint8List _patchedMatchmaking = Uint8List.fromList([
]);
Future<bool> patchHeadless(File file) async =>
_patch(file, _originalHeadless, _patchedHeadless);
await _patch(file, _originalHeadless, _patchedHeadless);
Future<bool> patchMatchmaking(File file) async =>
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");
}
final read = await file.readAsBytes();
final length = await file.length();
final source = await file.readAsBytes();
var readOffset = 0;
var patchOffset = -1;
var patchCount = 0;
while(readOffset < length){
if(read[readOffset] == original[patchCount]){
while(readOffset < source.length){
if(source[readOffset] == original[patchCount]){
if(patchOffset == -1) {
patchOffset = readOffset;
}
if(++patchCount == original.length) {
if(readOffset - patchOffset + 1 == original.length) {
break;
}
patchCount++;
}else {
patchOffset = -1;
patchCount = 0;
}
readOffset++;
@@ -55,10 +58,10 @@ Future<bool> _patch(File file, Uint8List original, Uint8List patched) async {
}
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;
}catch(_){
return false;

View File

@@ -104,9 +104,9 @@ Future<bool> startElevatedProcess({required String executable, required String a
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 ?? [];
if(wrapProcess) {
if(useTempBatch) {
final tempScriptDirectory = await tempDirectory.createTemp("reboot_launcher_process");
final tempScriptFile = File("${tempScriptDirectory.path}/process.bat");
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;
}
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) {
username = '${_parseUsername(username, host)}@projectreboot.dev';
}
@@ -223,12 +223,18 @@ List<String> createRebootArgs(String username, String password, bool host, bool
"-AUTH_TYPE=epic"
];
if(host && headless){
if(log) {
args.add("-log");
}
if(host) {
args.addAll([
"-nullrhi",
"-nosplash",
"-nosound",
"-nosound"
]);
if(hostType == GameServerType.headless){
args.add("-nullrhi");
}
}
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