<feat: New project structure>

<feat: New release>
This commit is contained in:
Alessandro Autiero
2023-09-02 15:34:15 +02:00
parent 64b33102f4
commit b41e22adeb
953 changed files with 1373072 additions and 0 deletions

74
cli/lib/cli.dart Normal file
View File

@@ -0,0 +1,74 @@
import 'dart:io';
import 'package:args/args.dart';
import 'package:reboot_cli/src/game.dart';
import 'package:reboot_cli/src/reboot.dart';
import 'package:reboot_cli/src/server.dart';
import 'package:reboot_common/common.dart';
import 'package:reboot_common/src/model/fortnite_version.dart';
import 'package:reboot_common/src/util/matchmaker.dart' as matchmaker;
late String? username;
late bool host;
late bool verbose;
late String dll;
late FortniteVersion version;
late bool autoRestart;
void main(List<String> args) async {
stdout.writeln("Reboot Launcher");
stdout.writeln("Wrote by Auties00");
stdout.writeln("Version 1.0");
kill();
var parser = ArgParser()
..addOption("path", mandatory: true)
..addOption("username")
..addOption("server-type", allowed: ServerType.values.map((entry) => entry.name), defaultsTo: ServerType.embedded.name)
..addOption("server-host")
..addOption("server-port")
..addOption("matchmaking-address")
..addOption("dll", defaultsTo: rebootDllFile.path)
..addFlag("update", defaultsTo: true, negatable: true)
..addFlag("log", defaultsTo: false)
..addFlag("host", defaultsTo: false)
..addFlag("auto-restart", defaultsTo: false, negatable: true);
var result = parser.parse(args);
dll = result["dll"];
host = result["host"];
username = result["username"] ?? kDefaultPlayerName;
verbose = result["log"];
version = FortniteVersion(name: "Dummy", location: Directory(result["path"]));
await downloadRequiredDLLs();
if(result["update"]) {
stdout.writeln("Updating reboot dll...");
try {
await downloadRebootDll(rebootDownloadUrl, 0);
}catch(error){
stderr.writeln("Cannot update reboot dll: $error");
}
}
stdout.writeln("Launching game...");
var executable = await version.executable;
if(executable == null){
throw Exception("Missing game executable at: ${version.location.path}");
}
var started = await startServerCli(
result["server-host"],
result["server-port"],
ServerType.values.firstWhere((element) => element.name == result["server-type"])
);
if(!started){
stderr.writeln("Cannot start server!");
return;
}
matchmaker.writeMatchmakingIp(result["matchmaking-address"]);
autoRestart = result["auto-restart"];
await startGame();
}

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

@@ -0,0 +1,114 @@
import 'dart:convert';
import 'dart:io';
import 'package:process_run/process_run.dart';
import 'package:reboot_common/common.dart';
import 'package:reboot_cli/cli.dart';
Process? _gameProcess;
Process? _launcherProcess;
Process? _eacProcess;
Future<void> startGame() async {
await _startLauncherProcess(version);
await _startEacProcess(version);
var executable = await version.executable;
if (executable == null) {
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
}
if (username == null) {
username = "Reboot${host ? 'Host' : 'Player'}";
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
}
_gameProcess = await Process.start(executable.path, createRebootArgs(username!, "", host, ""))
..exitCode.then((_) => _onClose())
..outLines.forEach((line) => _onGameOutput(line, dll, host, verbose));
_injectOrShowError("cobalt.dll");
}
Future<void> _startLauncherProcess(FortniteVersion dummyVersion) async {
if (dummyVersion.launcher == null) {
return;
}
_launcherProcess = await Process.start(dummyVersion.launcher!.path, []);
suspend(_launcherProcess!.pid);
}
Future<void> _startEacProcess(FortniteVersion dummyVersion) async {
if (dummyVersion.eacExecutable == null) {
return;
}
_eacProcess = await Process.start(dummyVersion.eacExecutable!.path, []);
suspend(_eacProcess!.pid);
}
void _onGameOutput(String line, String dll, bool hosting, bool verbose) {
if(verbose) {
stdout.writeln(line);
}
if (line.contains(shutdownLine)) {
_onClose();
return;
}
if(cannotConnectErrors.any((element) => line.contains(element))){
stderr.writeln("The backend doesn't work! Token expired");
_onClose();
return;
}
if(line.contains("Region ")){
if(hosting) {
_injectOrShowError(dll, false);
}else {
_injectOrShowError("console.dll");
}
_injectOrShowError("memoryleak.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 ? File("${assetsDirectory.path}\\dlls\\$binary") : 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);
}

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

@@ -0,0 +1,55 @@
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:http/http.dart' as http;
import 'package:reboot_common/common.dart';
// TODO: Use github
const String _baseDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968021373169674/cobalt.dll";
const String _consoleDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968095033524234/console.dll";
const String _memoryFixDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968141556756581/memoryleak.dll";
const String _embeddedConfigDownload = "https://cdn.discordapp.com/attachments/1026121175878881290/1040679319351066644/embedded.zip";
Future<void> downloadRequiredDLLs() async {
stdout.writeln("Downloading necessary components...");
var consoleDll = File("${assetsDirectory.path}\\dlls\\console.dll");
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 = File("${assetsDirectory.path}\\dlls\\cobalt.dll");
if(!craniumDll.existsSync()){
var response = await http.get(Uri.parse(_baseDownload));
if(response.statusCode != 200){
throw Exception("Cannot download cobalt.dll");
}
await craniumDll.writeAsBytes(response.bodyBytes);
}
var memoryFixDll = File("${assetsDirectory.path}\\dlls\\memoryleak.dll");
if(!memoryFixDll.existsSync()){
var response = await http.get(Uri.parse(_memoryFixDownload));
if(response.statusCode != 200){
throw Exception("Cannot download memoryleak.dll");
}
await memoryFixDll.writeAsBytes(response.bodyBytes);
}
if(!authenticatorDirectory.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, authenticatorDirectory.path);
}
}

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

@@ -0,0 +1,74 @@
import 'dart:io';
import 'package:reboot_common/common.dart';
import 'package:reboot_common/src/util/authenticator.dart' as server;
Future<bool> startServerCli(String? host, String? port, ServerType type) async {
stdout.writeln("Starting backend server...");
switch(type){
case ServerType.local:
var result = await server.ping(host ?? kDefaultAuthenticatorHost, port ?? kDefaultAuthenticatorPort);
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 server.startEmbeddedAuthenticator(false);
var result = await server.ping(host ?? kDefaultAuthenticatorHost, port ?? kDefaultAuthenticatorPort);
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 server.ping(host, port);
if(uri == null){
return null;
}
return await server.startRemoteAuthenticatorProxy(uri);
}catch(error){
throw Exception("Cannot start reverse proxy");
}
}
void kill() async {
try {
await Process.run("taskkill", ["/f", "/im", "FortniteLauncher.exe"]);
await Process.run("taskkill", ["/f", "/im", "FortniteClient-Win64-Shipping_EAC.exe"]);
}catch(_){
}
}