This commit is contained in:
Alessandro Autiero
2022-12-16 21:08:57 +01:00
parent ef7f34e0e3
commit 966b4b33fd
91 changed files with 388682 additions and 1005 deletions

View File

@@ -0,0 +1,49 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';
Future<Map<String, dynamic>> getControllerJson(String name) async {
var folder = await _getWindowsPath(FOLDERID_Documents);
if(folder == null){
throw Exception("Missing documents folder");
}
var file = File("$folder/$name.gs");
if(!file.existsSync()){
return HashMap();
}
return jsonDecode(file.readAsStringSync());
}
Future<String?> _getWindowsPath(String folderID) {
final Pointer<Pointer<Utf16>> pathPtrPtr = calloc<Pointer<Utf16>>();
final Pointer<GUID> knownFolderID = calloc<GUID>()..ref.setGUID(folderID);
try {
final int hr = SHGetKnownFolderPath(
knownFolderID,
KF_FLAG_DEFAULT,
NULL,
pathPtrPtr,
);
if (FAILED(hr)) {
if (hr == E_INVALIDARG || hr == E_FAIL) {
throw WindowsException(hr);
}
return Future<String?>.value();
}
final String path = pathPtrPtr.value.toDartString();
return Future<String>.value(path);
} finally {
calloc.free(pathPtrPtr);
calloc.free(knownFolderID);
}
}

52
lib/src/cli/config.dart Normal file
View File

@@ -0,0 +1,52 @@
import 'dart:convert';
import 'package:args/args.dart';
import '../model/fortnite_version.dart';
import '../model/game_type.dart';
import '../model/server_type.dart';
Iterable<String> getGameTypes() => GameType.values.map((entry) => entry.id);
Iterable<String> getServerTypes() => ServerType.values.map((entry) => entry.id);
String getDefaultServerType(Map<String, dynamic> json) {
var type = ServerType.values.elementAt(json["type"] ?? 0);
return type.id;
}
GameType getGameType(ArgResults result) {
var type = GameType.of(result["type"]);
if(type == null){
throw Exception("Unknown game type: $result. Use --type only with ${getGameTypes().join(", ")}");
}
return type;
}
ServerType getServerType(ArgResults result) {
var type = ServerType.of(result["server-type"]);
if(type == null){
throw Exception("Unknown server type: $result. Use --server-type only with ${getServerTypes().join(", ")}");
}
return type;
}
String getDefaultGameType(Map<String, dynamic> json){
var type = GameType.values.elementAt(json["type"] ?? 0);
switch(type){
case GameType.client:
return "client";
case GameType.server:
return "server";
case GameType.headlessServer:
return "headless_server";
}
}
List<FortniteVersion> getVersions(Map<String, dynamic> gameJson) {
Iterable iterable = jsonDecode(gameJson["versions"] ?? "[]");
return iterable.map((entry) => FortniteVersion.fromJson(entry))
.toList();
}

137
lib/src/cli/game.dart Normal file
View File

@@ -0,0 +1,137 @@
import 'dart:io';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/cli.dart';
import 'package:win32_suspend_process/win32_suspend_process.dart';
import '../model/fortnite_version.dart';
import '../model/game_type.dart';
import '../util/injector.dart';
import '../util/os.dart';
import '../util/server.dart';
final List<String> _errorStrings = [
"port 3551 failed: Connection refused",
"Unable to login to Fortnite servers",
"HTTP 400 response from ",
"Network failure when attempting to check platform restrictions",
"UOnlineAccountCommon::ForceLogout"
];
Process? _gameProcess;
Process? _launcherProcess;
Process? _eacProcess;
Future<void> startGame() async {
await _startLauncherProcess(version);
await _startEacProcess(version);
var gamePath = version.executable?.path;
if (gamePath == null) {
throw Exception("${version.location
.path} no longer contains a Fortnite executable, did you delete or move it?");
}
var hosting = type != GameType.client;
if (username == null) {
username = "Reboot${hosting ? 'Host' : 'Player'}";
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
}
_gameProcess = await Process.start(gamePath, createRebootArgs(username!, type))
..exitCode.then((_) => _onClose())
..outLines.forEach((line) => _onGameOutput(line, dll, hosting, verbose));
_injectOrShowError("craniumv2.dll");
}
Future<void> _startLauncherProcess(FortniteVersion dummyVersion) async {
if (dummyVersion.launcher == null) {
return;
}
_launcherProcess = await Process.start(dummyVersion.launcher!.path, []);
Win32Process(_launcherProcess!.pid).suspend();
}
Future<void> _startEacProcess(FortniteVersion dummyVersion) async {
if (dummyVersion.eacExecutable == null) {
return;
}
_eacProcess = await Process.start(dummyVersion.eacExecutable!.path, []);
Win32Process(_eacProcess!.pid).suspend();
}
void _onGameOutput(String line, String dll, bool hosting, bool verbose) {
if(verbose) {
stdout.writeln(line);
}
if(line.contains("Platform has ")){
_injectOrShowError("craniumv2.dll");
return;
}
if (line.contains("FOnlineSubsystemGoogleCommon::Shutdown()")) {
_onClose();
return;
}
if(_errorStrings.any((element) => line.contains(element))){
stderr.writeln("The backend doesn't work! Token expired");
_onClose();
return;
}
if(line.contains("Region ")){
_injectRequiredDLLs(hosting, dll);
}
}
void _injectRequiredDLLs(bool host, String rebootDll) {
if(host) {
_injectOrShowError(rebootDll, false);
}else {
_injectOrShowError("console.dll");
}
_injectOrShowError("leakv2.dll");
}
void _kill() {
_gameProcess?.kill(ProcessSignal.sigabrt);
_launcherProcess?.kill(ProcessSignal.sigabrt);
_eacProcess?.kill(ProcessSignal.sigabrt);
}
Future<void> _injectOrShowError(String binary, [bool locate = true]) async {
if (_gameProcess == null) {
return;
}
try {
stdout.writeln("Injecting $binary...");
var dll = locate ? await loadBinary(binary, true) : File(binary);
if(!dll.existsSync()){
throw Exception("Cannot inject $dll: missing file");
}
await injectDll(_gameProcess!.pid, dll.path);
} catch (exception) {
throw Exception("Cannot inject binary: $binary");
}
}
void _onClose() {
_kill();
sleep(const Duration(seconds: 3));
stdout.writeln("The game was closed");
if(autoRestart){
stdout.writeln("Restarting automatically game");
startGame();
return;
}
exit(0);
}

59
lib/src/cli/reboot.dart Normal file
View File

@@ -0,0 +1,59 @@
import 'dart:io';
import 'package:archive/archive_io.dart';
import '../util/os.dart';
import 'package:http/http.dart' as http;
const String _baseDownload = "https://cdn.discordapp.com/attachments/1009257632315494520/1051137082766131250/Cranium.dll";
const String _consoleDownload = "https://cdn.discordapp.com/attachments/1026121175878881290/1031230848005046373/console.dll";
const String _memoryFixDownload = "https://cdn.discordapp.com/attachments/1013220721494863872/1033484506633617500/MemoryLeakFixer.dll";
const String _embeddedConfigDownload = "https://cdn.discordapp.com/attachments/1026121175878881290/1040679319351066644/embedded.zip";
Future<void> downloadRequiredDLLs() async {
stdout.writeln("Downloading necessary components...");
var consoleDll = await loadBinary("console.dll", true);
if(!consoleDll.existsSync()){
var response = await http.get(Uri.parse(_consoleDownload));
if(response.statusCode != 200){
throw Exception("Cannot download console.dll");
}
await consoleDll.writeAsBytes(response.bodyBytes);
}
var craniumDll = await loadBinary("craniumv2.dll", true);
if(!craniumDll.existsSync()){
var response = await http.get(Uri.parse(_baseDownload));
if(response.statusCode != 200){
throw Exception("Cannot download craniumv2.dll");
}
await craniumDll.writeAsBytes(response.bodyBytes);
}
var memoryFixDll = await loadBinary("leakv2.dll", true);
if(!memoryFixDll.existsSync()){
var response = await http.get(Uri.parse(_memoryFixDownload));
if(response.statusCode != 200){
throw Exception("Cannot download leakv2.dll");
}
await memoryFixDll.writeAsBytes(response.bodyBytes);
}
var config = loadEmbedded("config/");
var profiles = loadEmbedded("profiles/");
var responses = loadEmbedded("responses/");
if(!config.existsSync() || !profiles.existsSync() || !responses.existsSync()){
var response = await http.get(Uri.parse(_embeddedConfigDownload));
if(response.statusCode != 200){
throw Exception("Cannot download embedded server config");
}
var tempZip = File("${tempDirectory.path}/reboot_config.zip");
await tempZip.writeAsBytes(response.bodyBytes);
await extractFileToDisk(tempZip.path, "$safeBinariesDirectory\\backend\\cli");
}
}

88
lib/src/cli/server.dart Normal file
View File

@@ -0,0 +1,88 @@
import 'dart:io';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/embedded/server.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_proxy/shelf_proxy.dart';
import '../model/server_type.dart';
import '../util/server.dart';
import 'game.dart';
Future<bool> startServer(String? host, String? port, ServerType type, String? matchmakingIp) async {
stdout.writeln("Starting backend server...");
switch(type){
case ServerType.local:
var result = await ping(host ?? "127.0.0.1", port ?? "3551");
if(result == null){
throw Exception("Local backend server is not running");
}
stdout.writeln("Detected local backend server");
return true;
case ServerType.embedded:
stdout.writeln("Starting an embedded server...");
await startEmbeddedServer(
() => matchmakingIp ?? "127.0.0.1"
);
await startEmbeddedMatchmaker();
var result = await ping(host ?? "127.0.0.1", port ?? "3551");
if(result == null){
throw Exception("Cannot start embedded server");
}
return true;
case ServerType.remote:
if(host == null){
throw Exception("Missing host for remote server");
}
if(port == null){
throw Exception("Missing host for remote server");
}
stdout.writeln("Starting a reverse proxy to $host:$port");
return await _changeReverseProxyState(host, port) != null;
}
}
Future<HttpServer?> _changeReverseProxyState(String host, String port) async {
host = host.trim();
if(host.isEmpty){
throw Exception("Missing host name");
}
port = port.trim();
if(port.isEmpty){
throw Exception("Missing port");
}
if(int.tryParse(port) == null){
throw Exception("Invalid port, use only numbers");
}
try{
var uri = await ping(host, port);
if(uri == null){
return null;
}
return await shelf_io.serve(proxyHandler(uri), "127.0.0.1", 3551);
}catch(error){
throw Exception("Cannot start reverse proxy");
}
}
void kill() async {
var shell = Shell(
commandVerbose: false,
commentVerbose: false,
verbose: false
);
try {
await shell.run("taskkill /f /im FortniteLauncher.exe");
await shell.run("taskkill /f /im FortniteClient-Win64-Shipping_EAC.exe");
}catch(_){
}
}