mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
<feat: New project structure>
<feat: New release>
This commit is contained in:
24
gui/lib/src/controller/authenticator_controller.dart
Normal file
24
gui/lib/src/controller/authenticator_controller.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
||||
|
||||
class AuthenticatorController extends ServerController {
|
||||
AuthenticatorController() : super();
|
||||
|
||||
@override
|
||||
String get controllerName => "authenticator";
|
||||
|
||||
@override
|
||||
String get storageName => "reboot_authenticator";
|
||||
|
||||
@override
|
||||
String get defaultHost => kDefaultAuthenticatorHost;
|
||||
|
||||
@override
|
||||
String get defaultPort => kDefaultAuthenticatorPort;
|
||||
|
||||
@override
|
||||
Future<bool> get isPortFree => isAuthenticatorPortFree();
|
||||
|
||||
@override
|
||||
Future<bool> freePort() => freeAuthenticatorPort();
|
||||
}
|
||||
24
gui/lib/src/controller/build_controller.dart
Normal file
24
gui/lib/src/controller/build_controller.dart
Normal file
@@ -0,0 +1,24 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
class BuildController extends GetxController {
|
||||
List<FortniteBuild>? _builds;
|
||||
Rxn<FortniteBuild> selectedBuild;
|
||||
|
||||
BuildController() : selectedBuild = Rxn();
|
||||
|
||||
List<FortniteBuild>? get builds => _builds;
|
||||
|
||||
set builds(List<FortniteBuild>? builds) {
|
||||
_builds = builds;
|
||||
if(builds == null || builds.isEmpty){
|
||||
return;
|
||||
}
|
||||
selectedBuild.value = builds[0];
|
||||
}
|
||||
|
||||
void reset(){
|
||||
_builds = null;
|
||||
selectedBuild.value = null;
|
||||
}
|
||||
}
|
||||
125
gui/lib/src/controller/game_controller.dart
Normal file
125
gui/lib/src/controller/game_controller.dart
Normal file
@@ -0,0 +1,125 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class GameController extends GetxController {
|
||||
late final String uuid;
|
||||
late final GetStorage _storage;
|
||||
late final TextEditingController username;
|
||||
late final TextEditingController password;
|
||||
late final TextEditingController customLaunchArgs;
|
||||
late final Rx<List<FortniteVersion>> versions;
|
||||
late final Rxn<FortniteVersion> _selectedVersion;
|
||||
late final RxBool started;
|
||||
late final RxBool autoStartGameServer;
|
||||
late final Rxn<Set<Map<String, dynamic>>> servers;
|
||||
late final Rxn<GameInstance> instance;
|
||||
|
||||
GameController() {
|
||||
_storage = GetStorage("reboot_game");
|
||||
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);
|
||||
uuid = _storage.read("uuid") ?? const Uuid().v4();
|
||||
_storage.write("uuid", uuid);
|
||||
_selectedVersion = Rxn(decodedSelectedVersion);
|
||||
username = TextEditingController(text: _storage.read("username") ?? kDefaultPlayerName);
|
||||
username.addListener(() => _storage.write("username", username.text));
|
||||
password = TextEditingController(text: _storage.read("password") ?? "");
|
||||
password.addListener(() => _storage.write("password", password.text));
|
||||
customLaunchArgs = TextEditingController(text: _storage.read("custom_launch_args") ?? "");
|
||||
customLaunchArgs.addListener(() => _storage.write("custom_launch_args", customLaunchArgs.text));
|
||||
started = RxBool(false);
|
||||
autoStartGameServer = RxBool(_storage.read("auto_game_server") ?? true);
|
||||
autoStartGameServer.listen((value) => _storage.write("auto_game_server", value));
|
||||
var supabase = Supabase.instance.client;
|
||||
servers = Rxn();
|
||||
supabase.from('hosts')
|
||||
.stream(primaryKey: ['id'])
|
||||
.map((event) => event.where((element) => element["ip"] != null).toSet())
|
||||
.listen((event) {
|
||||
if(servers.value == null) {
|
||||
servers.value = event;
|
||||
}else {
|
||||
servers.value?.addAll(event);
|
||||
}
|
||||
});
|
||||
var serializedInstance = _storage.read("instance");
|
||||
instance = Rxn(serializedInstance != null ? GameInstance.fromJson(jsonDecode(serializedInstance)) : null);
|
||||
instance.listen((value) => _storage.write("instance", jsonEncode(value?.toJson())));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
username.text = kDefaultPlayerName;
|
||||
password.text = "";
|
||||
customLaunchArgs.text = "";
|
||||
versions.value = [];
|
||||
autoStartGameServer.value = true;
|
||||
instance.value = null;
|
||||
}
|
||||
|
||||
FortniteVersion? getVersionByName(String name) {
|
||||
return versions.value.firstWhereOrNull((element) => element.name == name);
|
||||
}
|
||||
|
||||
void addVersion(FortniteVersion version) {
|
||||
var empty = versions.value.isEmpty;
|
||||
versions.update((val) => val?.add(version));
|
||||
if(empty){
|
||||
selectedVersion = 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));
|
||||
if (selectedVersion?.name == version.name || hasNoVersions) {
|
||||
selectedVersion = null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _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;
|
||||
|
||||
FortniteVersion? get selectedVersion => _selectedVersion();
|
||||
|
||||
set selectedVersion(FortniteVersion? version) {
|
||||
_selectedVersion.value = version;
|
||||
_storage.write("version", version?.name);
|
||||
}
|
||||
|
||||
void updateVersion(FortniteVersion version, Function(FortniteVersion) function) {
|
||||
versions.update((val) => function(version));
|
||||
}
|
||||
|
||||
Map<String, dynamic>? findServerById(String uuid) {
|
||||
try {
|
||||
print(uuid);
|
||||
return servers.value?.firstWhere((element) => element["id"] == uuid);
|
||||
} on StateError catch(_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
gui/lib/src/controller/hosting_controller.dart
Normal file
46
gui/lib/src/controller/hosting_controller.dart
Normal file
@@ -0,0 +1,46 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
const String kDefaultServerName = "Reboot Game Server";
|
||||
const String kDefaultDescription = "Just another server";
|
||||
|
||||
class HostingController extends GetxController {
|
||||
late final GetStorage _storage;
|
||||
late final TextEditingController name;
|
||||
late final TextEditingController description;
|
||||
late final TextEditingController password;
|
||||
late final RxBool showPassword;
|
||||
late final RxBool discoverable;
|
||||
late final RxBool started;
|
||||
late final Rxn<GameInstance> instance;
|
||||
|
||||
HostingController() {
|
||||
_storage = GetStorage("reboot_hosting");
|
||||
name = TextEditingController(text: _storage.read("name") ?? kDefaultServerName);
|
||||
name.addListener(() => _storage.write("name", name.text));
|
||||
description = TextEditingController(text: _storage.read("description") ?? kDefaultDescription);
|
||||
description.addListener(() => _storage.write("description", description.text));
|
||||
password = TextEditingController(text: _storage.read("password") ?? "");
|
||||
password.addListener(() => _storage.write("password", password.text));
|
||||
discoverable = RxBool(_storage.read("discoverable") ?? true);
|
||||
discoverable.listen((value) => _storage.write("discoverable", value));
|
||||
started = RxBool(false);
|
||||
showPassword = RxBool(false);
|
||||
var serializedInstance = _storage.read("instance");
|
||||
instance = Rxn(serializedInstance != null ? GameInstance.fromJson(jsonDecode(serializedInstance)) : null);
|
||||
instance.listen((value) => _storage.write("instance", jsonEncode(value?.toJson())));
|
||||
}
|
||||
|
||||
void reset() {
|
||||
name.text = kDefaultServerName;
|
||||
description.text = kDefaultDescription;
|
||||
showPassword.value = false;
|
||||
discoverable.value = false;
|
||||
started.value = false;
|
||||
instance.value = null;
|
||||
}
|
||||
}
|
||||
30
gui/lib/src/controller/matchmaker_controller.dart
Normal file
30
gui/lib/src/controller/matchmaker_controller.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
||||
|
||||
class MatchmakerController extends ServerController {
|
||||
late final TextEditingController gameServerAddress;
|
||||
|
||||
MatchmakerController() : super() {
|
||||
gameServerAddress = TextEditingController(text: storage.read("game_server_address") ?? kDefaultMatchmakerHost);
|
||||
gameServerAddress.addListener(() => storage.write("game_server_address", gameServerAddress.text));
|
||||
}
|
||||
|
||||
@override
|
||||
String get controllerName => "matchmaker";
|
||||
|
||||
@override
|
||||
String get storageName => "reboot_matchmaker";
|
||||
|
||||
@override
|
||||
String get defaultHost => kDefaultMatchmakerHost;
|
||||
|
||||
@override
|
||||
String get defaultPort => kDefaultMatchmakerPort;
|
||||
|
||||
@override
|
||||
Future<bool> get isPortFree => isMatchmakerPortFree();
|
||||
|
||||
@override
|
||||
Future<bool> freePort() => freeMatchmakerPort();
|
||||
}
|
||||
189
gui/lib/src/controller/server_controller.dart
Normal file
189
gui/lib/src/controller/server_controller.dart
Normal file
@@ -0,0 +1,189 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:sync/semaphore.dart';
|
||||
|
||||
abstract class ServerController extends GetxController {
|
||||
late final GetStorage storage;
|
||||
late final TextEditingController host;
|
||||
late final TextEditingController port;
|
||||
late final Rx<ServerType> type;
|
||||
late final Semaphore semaphore;
|
||||
late RxBool started;
|
||||
late RxBool detached;
|
||||
Process? embeddedServer;
|
||||
HttpServer? localServer;
|
||||
HttpServer? remoteServer;
|
||||
|
||||
ServerController() {
|
||||
storage = GetStorage(storageName);
|
||||
started = RxBool(false);
|
||||
type = Rx(ServerType.values.elementAt(storage.read("type") ?? 0));
|
||||
type.listen((value) {
|
||||
host.text = _readHost();
|
||||
port.text = _readPort();
|
||||
storage.write("type", value.index);
|
||||
if (!started.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
});
|
||||
host = TextEditingController(text: _readHost());
|
||||
host.addListener(() =>
|
||||
storage.write("${type.value.name}_host", host.text));
|
||||
port = TextEditingController(text: _readPort());
|
||||
port.addListener(() =>
|
||||
storage.write("${type.value.name}_port", port.text));
|
||||
detached = RxBool(storage.read("detached") ?? false);
|
||||
detached.listen((value) => storage.write("detached", value));
|
||||
semaphore = Semaphore();
|
||||
}
|
||||
|
||||
String get controllerName;
|
||||
|
||||
String get storageName;
|
||||
|
||||
String get defaultHost;
|
||||
|
||||
String get defaultPort;
|
||||
|
||||
Future<bool> get isPortFree;
|
||||
|
||||
Future<bool> get isPortTaken async => !(await isPortFree);
|
||||
|
||||
Future<bool> freePort();
|
||||
|
||||
void reset() async {
|
||||
type.value = ServerType.values.elementAt(0);
|
||||
for (var type in ServerType.values) {
|
||||
storage.write("${type.name}_host", null);
|
||||
storage.write("${type.name}_port", null);
|
||||
}
|
||||
|
||||
host.text = type.value != ServerType.remote ? defaultHost : "";
|
||||
port.text = defaultPort;
|
||||
detached.value = false;
|
||||
}
|
||||
|
||||
String _readHost() {
|
||||
String? value = storage.read("${type.value.name}_host");
|
||||
return value != null && value.isNotEmpty ? value
|
||||
: type.value != ServerType.remote ? defaultHost : "";
|
||||
}
|
||||
|
||||
String _readPort() =>
|
||||
storage.read("${type.value.name}_port") ?? defaultPort;
|
||||
|
||||
Stream<ServerResult> start() async* {
|
||||
try {
|
||||
var host = this.host.text.trim();
|
||||
if (host.isEmpty) {
|
||||
yield ServerResult(ServerResultType.missingHostError);
|
||||
return;
|
||||
}
|
||||
|
||||
var port = this.port.text.trim();
|
||||
if (port.isEmpty) {
|
||||
yield ServerResult(ServerResultType.missingPortError);
|
||||
return;
|
||||
}
|
||||
|
||||
var portNumber = int.tryParse(port);
|
||||
if (portNumber == null) {
|
||||
yield ServerResult(ServerResultType.illegalPortError);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type() != ServerType.local && await isPortTaken) {
|
||||
yield ServerResult(ServerResultType.freeingPort);
|
||||
var result = await freePort();
|
||||
yield ServerResult(result ? ServerResultType.freePortSuccess : ServerResultType.freePortError);
|
||||
if(!result) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch(type()){
|
||||
case ServerType.embedded:
|
||||
embeddedServer = await startEmbeddedAuthenticator(detached());
|
||||
break;
|
||||
case ServerType.remote:
|
||||
yield ServerResult(ServerResultType.pingingRemote);
|
||||
var uriResult = await ping(host, port);
|
||||
if(uriResult == null) {
|
||||
yield ServerResult(ServerResultType.pingError);
|
||||
return;
|
||||
}
|
||||
|
||||
remoteServer = await startRemoteAuthenticatorProxy(uriResult);
|
||||
break;
|
||||
case ServerType.local:
|
||||
if(port != defaultPort) {
|
||||
localServer = await startRemoteAuthenticatorProxy(Uri.parse("http://$defaultHost:$defaultPort"));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
yield ServerResult(ServerResultType.pingingLocal);
|
||||
var uriResult = await pingSelf(defaultPort);
|
||||
if(uriResult == null) {
|
||||
yield ServerResult(ServerResultType.pingError);
|
||||
return;
|
||||
}
|
||||
|
||||
yield ServerResult(ServerResultType.startSuccess);
|
||||
started.value = true;
|
||||
}catch(error, stackTrace) {
|
||||
yield ServerResult(
|
||||
ServerResultType.startError,
|
||||
error: error,
|
||||
stackTrace: stackTrace
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> stop() async {
|
||||
started.value = false;
|
||||
try{
|
||||
switch(type()){
|
||||
case ServerType.embedded:
|
||||
freePort();
|
||||
break;
|
||||
case ServerType.remote:
|
||||
await remoteServer?.close(force: true);
|
||||
remoteServer = null;
|
||||
break;
|
||||
case ServerType.local:
|
||||
await localServer?.close(force: true);
|
||||
localServer = null;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}catch(_){
|
||||
started.value = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ServerResult> restart() async* {
|
||||
await resetWinNat();
|
||||
if(started()) {
|
||||
await stop();
|
||||
}
|
||||
|
||||
yield* start();
|
||||
}
|
||||
|
||||
Stream<ServerResult> toggle() async* {
|
||||
if(started()) {
|
||||
await stop();
|
||||
}else {
|
||||
yield* start();
|
||||
}
|
||||
}
|
||||
}
|
||||
63
gui/lib/src/controller/settings_controller.dart
Normal file
63
gui/lib/src/controller/settings_controller.dart
Normal file
@@ -0,0 +1,63 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_launcher/main.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
class SettingsController extends GetxController {
|
||||
static const String _kDefaultIp = "127.0.0.1";
|
||||
|
||||
late final GetStorage _storage;
|
||||
late final String originalDll;
|
||||
late final TextEditingController rebootDll;
|
||||
late final TextEditingController consoleDll;
|
||||
late final TextEditingController authDll;
|
||||
late final RxBool firstRun;
|
||||
late double width;
|
||||
late double height;
|
||||
late double? offsetX;
|
||||
late double? offsetY;
|
||||
late double scrollingDistance;
|
||||
|
||||
SettingsController() {
|
||||
_storage = GetStorage("reboot_settings");
|
||||
rebootDll = _createController("reboot", "reboot.dll");
|
||||
consoleDll = _createController("console", "console.dll");
|
||||
authDll = _createController("cobalt", "cobalt.dll");
|
||||
width = _storage.read("width") ?? kDefaultWindowWidth;
|
||||
height = _storage.read("height") ?? kDefaultWindowHeight;
|
||||
offsetX = _storage.read("offset_x");
|
||||
offsetY = _storage.read("offset_y");
|
||||
scrollingDistance = 0.0;
|
||||
firstRun = RxBool(_storage.read("first_run") ?? true);
|
||||
firstRun.listen((value) => _storage.write("first_run", value));
|
||||
}
|
||||
|
||||
TextEditingController _createController(String key, String name) {
|
||||
var controller = TextEditingController(text: _storage.read(key) ?? _controllerDefaultPath(name));
|
||||
controller.addListener(() => _storage.write(key, controller.text));
|
||||
return controller;
|
||||
}
|
||||
|
||||
void saveWindowSize() async {
|
||||
var size = await windowManager.getSize();
|
||||
_storage.write("width", size.width);
|
||||
_storage.write("height", size.height);
|
||||
}
|
||||
|
||||
void saveWindowOffset(Offset position) {
|
||||
_storage.write("offset_x", position.dx);
|
||||
_storage.write("offset_y", position.dy);
|
||||
}
|
||||
|
||||
void reset(){
|
||||
rebootDll.text = _controllerDefaultPath("reboot.dll");
|
||||
consoleDll.text = _controllerDefaultPath("console.dll");
|
||||
authDll.text = _controllerDefaultPath("cobalt.dll");
|
||||
firstRun.value = true;
|
||||
writeMatchmakingIp(_kDefaultIp);
|
||||
}
|
||||
|
||||
String _controllerDefaultPath(String name) => "${assetsDirectory.path}\\dlls\\$name";
|
||||
}
|
||||
47
gui/lib/src/controller/update_controller.dart
Normal file
47
gui/lib/src/controller/update_controller.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
class UpdateController {
|
||||
late final GetStorage _storage;
|
||||
late final RxnInt timestamp;
|
||||
late final Rx<UpdateStatus> status;
|
||||
late final Rx<UpdateTimer> timer;
|
||||
late final TextEditingController url;
|
||||
|
||||
UpdateController() {
|
||||
_storage = GetStorage("reboot_update");
|
||||
timestamp = RxnInt(_storage.read("ts"));
|
||||
timestamp.listen((value) => _storage.write("ts", value));
|
||||
var timerIndex = _storage.read("timer");
|
||||
timer = Rx(timerIndex == null ? UpdateTimer.never : UpdateTimer.values.elementAt(timerIndex));
|
||||
timer.listen((value) => _storage.write("timer", value.index));
|
||||
url = TextEditingController(text: _storage.read("update_url") ?? rebootDownloadUrl);
|
||||
url.addListener(() => _storage.write("update_url", url.text));
|
||||
status = Rx(UpdateStatus.waiting);
|
||||
}
|
||||
|
||||
Future<void> update() async {
|
||||
if(timer.value == UpdateTimer.never) {
|
||||
status.value = UpdateStatus.success;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
timestamp.value = await downloadRebootDll(url.text, timestamp.value);
|
||||
status.value = UpdateStatus.success;
|
||||
}catch(_) {
|
||||
status.value = UpdateStatus.error;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
timestamp.value = null;
|
||||
timer.value = UpdateTimer.never;
|
||||
url.text = rebootDownloadUrl;
|
||||
status.value = UpdateStatus.waiting;
|
||||
update();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user