mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-14 03:32:23 +01:00
Switched to getx for state management
Fixed last remaining bug
This commit is contained in:
12
lib/src/controller/build_controller.dart
Normal file
12
lib/src/controller/build_controller.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
||||||
|
|
||||||
|
class BuildController extends GetxController {
|
||||||
|
List<FortniteBuild>? builds;
|
||||||
|
FortniteBuild? _selectedBuild;
|
||||||
|
|
||||||
|
FortniteBuild get selectedBuild => _selectedBuild ?? builds!.elementAt(0);
|
||||||
|
|
||||||
|
set selectedBuild(FortniteBuild build) => _selectedBuild = build;
|
||||||
|
}
|
||||||
90
lib/src/controller/game_controller.dart
Normal file
90
lib/src/controller/game_controller.dart
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/model/fortnite_version.dart';
|
||||||
|
|
||||||
|
class GameController extends GetxController {
|
||||||
|
late final GetStorage _storage;
|
||||||
|
late final TextEditingController username;
|
||||||
|
late final TextEditingController version;
|
||||||
|
late final Rx<List<FortniteVersion>> versions;
|
||||||
|
late final Rxn<FortniteVersion> _selectedVersion;
|
||||||
|
late final RxBool host;
|
||||||
|
late final RxBool started;
|
||||||
|
Process? gameProcess;
|
||||||
|
Process? launcherProcess;
|
||||||
|
Process? eacProcess;
|
||||||
|
|
||||||
|
GameController() {
|
||||||
|
_storage = GetStorage("game");
|
||||||
|
|
||||||
|
username = TextEditingController(text: _storage.read("username"));
|
||||||
|
username.addListener(() async {
|
||||||
|
await _storage.write("username", username.text);
|
||||||
|
});
|
||||||
|
|
||||||
|
Iterable decodedVersionsJson =
|
||||||
|
jsonDecode(_storage.read("versions") ?? "[]");
|
||||||
|
var decodedVersions = decodedVersionsJson
|
||||||
|
.map((entry) => FortniteVersion.fromJson(entry))
|
||||||
|
.toList();
|
||||||
|
versions = Rx(decodedVersions);
|
||||||
|
versions.listen((data) => saveVersions());
|
||||||
|
|
||||||
|
var decodedSelectedVersionName = _storage.read("version");
|
||||||
|
var decodedSelectedVersion = decodedVersions.firstWhereOrNull(
|
||||||
|
(element) => element.name == decodedSelectedVersionName);
|
||||||
|
_selectedVersion = Rxn(decodedSelectedVersion);
|
||||||
|
|
||||||
|
host = RxBool(_storage.read("host") ?? false);
|
||||||
|
host.listen((value) => _storage.write("host", value));
|
||||||
|
|
||||||
|
started = RxBool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void kill() {
|
||||||
|
gameProcess?.kill(ProcessSignal.sigabrt);
|
||||||
|
launcherProcess?.kill(ProcessSignal.sigabrt);
|
||||||
|
eacProcess?.kill(ProcessSignal.sigabrt);
|
||||||
|
}
|
||||||
|
|
||||||
|
FortniteVersion? getVersionByName(String name) {
|
||||||
|
return versions.value.firstWhereOrNull((element) => element.name == name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addVersion(FortniteVersion version) {
|
||||||
|
versions.update((val) => val?.add(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
FortniteVersion removeVersionByName(String versionName) {
|
||||||
|
var version =
|
||||||
|
versions.value.firstWhere((element) => element.name == versionName);
|
||||||
|
removeVersion(version);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeVersion(FortniteVersion version) {
|
||||||
|
versions.update((val) => val?.remove(version));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future saveVersions() async {
|
||||||
|
var serialized =
|
||||||
|
jsonEncode(versions.value.map((entry) => entry.toJson()).toList());
|
||||||
|
await _storage.write("versions", serialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasVersions => versions.value.isNotEmpty;
|
||||||
|
|
||||||
|
bool get hasNoVersions => versions.value.isEmpty;
|
||||||
|
|
||||||
|
Rxn<FortniteVersion> get selectedVersionObs => _selectedVersion;
|
||||||
|
|
||||||
|
set selectedVersion(FortniteVersion? version) {
|
||||||
|
_selectedVersion(version);
|
||||||
|
_storage.write("version", version?.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
34
lib/src/controller/server_controller.dart
Normal file
34
lib/src/controller/server_controller.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:get_storage/get_storage.dart';
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/util/binary.dart';
|
||||||
|
|
||||||
|
class ServerController extends GetxController {
|
||||||
|
late final TextEditingController host;
|
||||||
|
late final TextEditingController port;
|
||||||
|
late final RxBool embedded;
|
||||||
|
late final RxBool started;
|
||||||
|
Process? process;
|
||||||
|
|
||||||
|
ServerController() {
|
||||||
|
var storage = GetStorage("server");
|
||||||
|
host = TextEditingController(text: storage.read("host"));
|
||||||
|
host.addListener(() => storage.write("host", host.text));
|
||||||
|
|
||||||
|
port = TextEditingController(text: storage.read("port"));
|
||||||
|
port.addListener(() => storage.write("port", port.text));
|
||||||
|
|
||||||
|
embedded = RxBool(storage.read("embedded") ?? true);
|
||||||
|
embedded.listen((value) => storage.write("embedded", value));
|
||||||
|
|
||||||
|
started = RxBool(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future kill() async {
|
||||||
|
var release = await loadBinary("release.bat", false);
|
||||||
|
return Process.run(release.path, []);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lib/src/controller/warning_controller.dart
Normal file
7
lib/src/controller/warning_controller.dart
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
||||||
|
|
||||||
|
class WarningController extends GetxController {
|
||||||
|
RxBool warning = RxBool(false);
|
||||||
|
}
|
||||||
29
lib/src/util/binary.dart
Normal file
29
lib/src/util/binary.dart
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
Future<File> loadBinary(String binary, bool safe) async{
|
||||||
|
var safeBinary = File("$safeBinariesDirectory/$binary");
|
||||||
|
if(await safeBinary.exists()){
|
||||||
|
return safeBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
var internal = _locateInternalBinary(binary);
|
||||||
|
if(!safe){
|
||||||
|
return internal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(await internal.exists()){
|
||||||
|
await internal.copy(safeBinary.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeBinary;
|
||||||
|
}
|
||||||
|
|
||||||
|
File _locateInternalBinary(String binary){
|
||||||
|
return File("$internalBinariesDirectory\\$binary");
|
||||||
|
}
|
||||||
|
|
||||||
|
String get internalBinariesDirectory =>
|
||||||
|
"${File(Platform.resolvedExecutable).parent.path}\\data\\flutter_assets\\assets\\binaries";
|
||||||
|
|
||||||
|
String get safeBinariesDirectory =>
|
||||||
|
"${Platform.environment["UserProfile"]}\\.reboot_launcher";
|
||||||
142
lib/src/util/build.dart
Normal file
142
lib/src/util/build.dart
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:reboot_launcher/src/util/version.dart' as parser;
|
||||||
|
import 'package:html/parser.dart' show parse;
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
||||||
|
|
||||||
|
import 'package:process_run/shell.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/binary.dart';
|
||||||
|
|
||||||
|
const _userAgent =
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36";
|
||||||
|
|
||||||
|
final _cookieRegex = RegExp("cookie=\"(.*?);");
|
||||||
|
final _manifestSourceUrl = Uri.parse(
|
||||||
|
"https://github.com/VastBlast/FortniteManifestArchive/blob/main/README.md");
|
||||||
|
final _archiveCookieUrl = Uri.parse("http://allinstaller.xyz/rel");
|
||||||
|
final _archiveSourceUrl = Uri.parse("http://allinstaller.xyz/rel?i=1");
|
||||||
|
|
||||||
|
Future<List<FortniteBuild>> fetchBuilds() async => [
|
||||||
|
...await _fetchArchives(),
|
||||||
|
...await _fetchManifests()
|
||||||
|
]..sort((first, second) => first.version.compareTo(second.version));
|
||||||
|
|
||||||
|
Future<List<FortniteBuild>> _fetchArchives() async {
|
||||||
|
var cookieResponse = await http.get(_archiveCookieUrl);
|
||||||
|
var cookie = _cookieRegex.firstMatch(cookieResponse.body)?.group(1)?.trim();
|
||||||
|
var response =
|
||||||
|
await http.get(_archiveSourceUrl, headers: {"Cookie": cookie!});
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception("Erroneous status code: ${response.statusCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var document = parse(response.body);
|
||||||
|
var results = <FortniteBuild>[];
|
||||||
|
for (var build in document.querySelectorAll("a[href^='https']")) {
|
||||||
|
var version = parser.tryParse(build.text.replaceAll("Build ", ""));
|
||||||
|
if (version == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.add(FortniteBuild(
|
||||||
|
version: version, link: build.attributes["href"]!, hasManifest: false));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<FortniteBuild>> _fetchManifests() async {
|
||||||
|
var response = await http.get(_manifestSourceUrl);
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception("Erroneous status code: ${response.statusCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var document = parse(response.body);
|
||||||
|
var table = document.querySelector("table");
|
||||||
|
if (table == null) {
|
||||||
|
throw Exception("Missing data table");
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = <FortniteBuild>[];
|
||||||
|
for (var tableEntry in table.querySelectorAll("tbody > tr")) {
|
||||||
|
var children = tableEntry.querySelectorAll("td");
|
||||||
|
|
||||||
|
var name = children[0].text;
|
||||||
|
var separator = name.indexOf("-") + 1;
|
||||||
|
var version = parser
|
||||||
|
.tryParse(name.substring(separator, name.indexOf("-", separator)));
|
||||||
|
if (version == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = children[2].firstChild!.attributes["href"]!;
|
||||||
|
results.add(FortniteBuild(version: version, link: link, hasManifest: true));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Process> downloadManifestBuild(
|
||||||
|
String manifestUrl, String destination, Function(double) onProgress) async {
|
||||||
|
var buildExe = await loadBinary("build.exe", false);
|
||||||
|
var process = await Process.start(buildExe.path, [manifestUrl, destination]);
|
||||||
|
|
||||||
|
process.errLines
|
||||||
|
.where((message) => message.contains("%"))
|
||||||
|
.forEach((message) => onProgress(double.parse(message.split("%")[0])));
|
||||||
|
|
||||||
|
return process;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> downloadArchiveBuild(String archiveUrl, String destination,
|
||||||
|
Function(double) onProgress, Function() onRar) async {
|
||||||
|
var tempFile = File(
|
||||||
|
"${Platform.environment["Temp"]}\\FortniteBuild${Random.secure().nextInt(1000000)}.rar");
|
||||||
|
try {
|
||||||
|
var client = http.Client();
|
||||||
|
var request = http.Request("GET", Uri.parse(archiveUrl));
|
||||||
|
request.headers["User-Agent"] = _userAgent;
|
||||||
|
var response = await client.send(request);
|
||||||
|
if (response.statusCode != 200) {
|
||||||
|
throw Exception("Erroneous status code: ${response.statusCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = response.contentLength!;
|
||||||
|
var received = 0;
|
||||||
|
var sink = tempFile.openWrite();
|
||||||
|
await response.stream.map((s) {
|
||||||
|
received += s.length;
|
||||||
|
onProgress((received / length) * 100);
|
||||||
|
return s;
|
||||||
|
}).pipe(sink);
|
||||||
|
onRar();
|
||||||
|
|
||||||
|
var output = Directory(destination);
|
||||||
|
await output.create(recursive: true);
|
||||||
|
var shell = Shell(workingDirectory: internalBinariesDirectory);
|
||||||
|
await shell.run("./winrar.exe x ${tempFile.path} *.* \"${output.path}\"");
|
||||||
|
var children = output.listSync();
|
||||||
|
if(children.isEmpty){
|
||||||
|
throw Exception("Missing extracted directory"); // No content
|
||||||
|
}
|
||||||
|
|
||||||
|
if(children.length != 1){
|
||||||
|
return; // Already in the correct schema
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract directories from wrapper directory
|
||||||
|
var wrapper = Directory(children[0].path);
|
||||||
|
for(var entry in wrapper.listSync()){
|
||||||
|
await entry.rename("${output.path}/${path.basename(entry.path)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
await wrapper.delete();
|
||||||
|
} finally {
|
||||||
|
if (await tempFile.exists()) {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
lib/src/widget/restart_warning.dart
Normal file
15
lib/src/widget/restart_warning.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
class RestartWarning extends StatelessWidget {
|
||||||
|
const RestartWarning({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const InfoBar(
|
||||||
|
title: Text('Node Installation'),
|
||||||
|
content: Text('Restart the launcher to run the server'),
|
||||||
|
isLong: true,
|
||||||
|
severity: InfoBarSeverity.warning
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user