mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
Stuff
This commit is contained in:
@@ -24,7 +24,7 @@ import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/dialog/implementation/error.dart';
|
||||
import 'package:reboot_launcher/src/dialog/implementation/server.dart';
|
||||
import 'package:reboot_launcher/src/page/implementation/home_page.dart';
|
||||
import 'package:reboot_launcher/src/util/info.dart';
|
||||
import 'package:reboot_launcher/src/page/implementation/info_page.dart';
|
||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||
import 'package:reboot_launcher/src/util/os.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
@@ -35,65 +35,102 @@ import 'package:version/version.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
const double kDefaultWindowWidth = 1536;
|
||||
const double kDefaultWindowHeight = 1024;
|
||||
const double kDefaultWindowHeight = 1224;
|
||||
const String kCustomUrlSchema = "Reboot";
|
||||
|
||||
Version? appVersion;
|
||||
|
||||
class _MyHttpOverrides extends HttpOverrides {
|
||||
@override
|
||||
HttpClient createHttpClient(SecurityContext? context){
|
||||
return super.createHttpClient(context)
|
||||
..badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
|
||||
void main() => runZonedGuarded(
|
||||
() => _startApp(),
|
||||
(error, stack) => onError(error, stack, false),
|
||||
zoneSpecification: ZoneSpecification(
|
||||
handleUncaughtError: (self, parent, zone, error, stacktrace) => onError(error, stacktrace, false)
|
||||
)
|
||||
);
|
||||
|
||||
Future<void> _startApp() async {
|
||||
final errors = <Object>[];
|
||||
try {
|
||||
final pathError = await _initPath();
|
||||
if(pathError != null) {
|
||||
errors.add(pathError);
|
||||
}
|
||||
|
||||
final databaseError = await _initDatabase();
|
||||
if(databaseError != null) {
|
||||
errors.add(databaseError);
|
||||
}
|
||||
|
||||
final notificationsError = await _initNotifications();
|
||||
if(notificationsError != null) {
|
||||
errors.add(notificationsError);
|
||||
}
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
_initWindow();
|
||||
|
||||
final tilesError = InfoPage.initInfoTiles();
|
||||
if(tilesError != null) {
|
||||
errors.add(tilesError);
|
||||
}
|
||||
|
||||
final versionError = await _initVersion();
|
||||
if(versionError != null) {
|
||||
errors.add(versionError);
|
||||
}
|
||||
|
||||
final storageError = await _initStorage();
|
||||
if(storageError != null) {
|
||||
errors.add(storageError);
|
||||
}
|
||||
|
||||
final urlError = await _initUrlHandler();
|
||||
if(urlError != null) {
|
||||
errors.add(urlError);
|
||||
}
|
||||
|
||||
_checkGameServer();
|
||||
}catch(uncaughtError) {
|
||||
errors.add(uncaughtError);
|
||||
} finally{
|
||||
runApp(const RebootApplication());
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => _handleErrors(errors));
|
||||
}
|
||||
}
|
||||
|
||||
void main() => runZonedGuarded(
|
||||
() async {
|
||||
HttpOverrides.global = _MyHttpOverrides();
|
||||
final errors = <Object>[];
|
||||
try {
|
||||
await installationDirectory.create(recursive: true);
|
||||
await Supabase.initialize(
|
||||
url: supabaseUrl,
|
||||
anonKey: supabaseAnonKey
|
||||
);
|
||||
await localNotifier.setup(
|
||||
appName: 'Reboot Launcher',
|
||||
shortcutPolicy: ShortcutPolicy.ignore
|
||||
);
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await SystemTheme.accentColor.load();
|
||||
_initWindow();
|
||||
initInfoTiles();
|
||||
final versionError = await _initVersion();
|
||||
if(versionError != null) {
|
||||
errors.add(versionError);
|
||||
}
|
||||
Future<Object?> _initNotifications() async {
|
||||
try {
|
||||
await localNotifier.setup(
|
||||
appName: 'Reboot Launcher',
|
||||
shortcutPolicy: ShortcutPolicy.ignore
|
||||
);
|
||||
return null;
|
||||
}catch(error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
final storageError = await _initStorage();
|
||||
if(storageError != null) {
|
||||
errors.add(storageError);
|
||||
}
|
||||
Future<Object?> _initDatabase() async {
|
||||
try {
|
||||
await Supabase.initialize(
|
||||
url: supabaseUrl,
|
||||
anonKey: supabaseAnonKey
|
||||
);
|
||||
return null;
|
||||
}catch(error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
final urlError = await _initUrlHandler();
|
||||
if(urlError != null) {
|
||||
errors.add(urlError);
|
||||
}
|
||||
|
||||
_checkGameServer();
|
||||
}catch(uncaughtError) {
|
||||
errors.add(uncaughtError);
|
||||
} finally{
|
||||
runApp(const RebootApplication());
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => _handleErrors(errors));
|
||||
}
|
||||
},
|
||||
(error, stack) => onError(error, stack, false),
|
||||
zoneSpecification: ZoneSpecification(
|
||||
handleUncaughtError: (self, parent, zone, error, stacktrace) => onError(error, stacktrace, false)
|
||||
)
|
||||
);
|
||||
Future<Object?> _initPath() async {
|
||||
try {
|
||||
await installationDirectory.create(recursive: true);
|
||||
return null;
|
||||
}catch(error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
void _handleErrors(List<Object?> errors) {
|
||||
errors.where((element) => element != null).forEach((element) => onError(element!, null, false));
|
||||
@@ -170,6 +207,7 @@ void _joinServer(Uri uri) {
|
||||
String _parseCustomUrl(Uri uri) => uri.host;
|
||||
|
||||
void _initWindow() => doWhenWindowReady(() async {
|
||||
await SystemTheme.accentColor.load();
|
||||
await windowManager.ensureInitialized();
|
||||
await Window.initialize();
|
||||
var settingsController = Get.find<SettingsController>();
|
||||
|
||||
@@ -26,7 +26,7 @@ class SettingsController extends GetxController {
|
||||
gameServerDll = _createController("game_server", "reboot.dll");
|
||||
unrealEngineConsoleDll = _createController("unreal_engine_console", "console.dll");
|
||||
backendDll = _createController("backend", "cobalt.dll");
|
||||
memoryLeakDll = _createController("memory_leak", "memoryleak.dll");
|
||||
memoryLeakDll = _createController("memory_leak", "memoryFix.dll");
|
||||
gameServerPort = TextEditingController(text: _storage.read("game_server_port") ?? kDefaultGameServerPort);
|
||||
gameServerPort.addListener(() => _storage.write("game_server_port", gameServerPort.text));
|
||||
width = _storage.read("width") ?? kDefaultWindowWidth;
|
||||
@@ -67,5 +67,5 @@ class SettingsController extends GetxController {
|
||||
firstRun.value = true;
|
||||
}
|
||||
|
||||
String _controllerDefaultPath(String name) => "${assetsDirectory.path}\\dlls\\$name";
|
||||
String _controllerDefaultPath(String name) => "${dllsDirectory.path}\\$name";
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ extension ServerControllerDialog on BackendController {
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
case ServerResultType.startError:
|
||||
print(event.stackTrace);
|
||||
return showInfoBar(
|
||||
type.value == ServerType.local ? translations.localServerError(event.error ?? translations.unknownError) : translations.startServerError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
|
||||
@@ -46,16 +46,26 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
@override
|
||||
void initState() {
|
||||
windowManager.addListener(this);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_updateController.notifyLauncherUpdate();
|
||||
_updateController.updateReboot();
|
||||
watchDlls().listen((filePath) => showDllDeletedDialog(() {
|
||||
downloadCriticalDllInteractive(filePath);
|
||||
}));
|
||||
});
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _checkUpdates());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _checkUpdates() {
|
||||
_updateController.notifyLauncherUpdate();
|
||||
|
||||
if(!dllsDirectory.existsSync()) {
|
||||
dllsDirectory.createSync(recursive: true);
|
||||
}
|
||||
|
||||
for(final injectable in InjectableDll.values) {
|
||||
downloadCriticalDllInteractive("${injectable.name}.dll");
|
||||
}
|
||||
|
||||
watchDlls().listen((filePath) => showDllDeletedDialog(() {
|
||||
downloadCriticalDllInteractive(filePath);
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowClose() {
|
||||
exit(0); // Force closing
|
||||
|
||||
@@ -1,15 +1,53 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
import 'package:reboot_launcher/src/page/pages.dart';
|
||||
import 'package:reboot_launcher/src/util/info.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_launcher/src/widget/info_tile.dart';
|
||||
|
||||
class InfoPage extends RebootPage {
|
||||
static late final List<InfoTile> _infoTiles;
|
||||
static Object? initInfoTiles() {
|
||||
try {
|
||||
final directory = Directory("${assetsDirectory.path}\\info\\$currentLocale");
|
||||
final map = SplayTreeMap<int, InfoTile>();
|
||||
for(final entry in directory.listSync()) {
|
||||
if(entry is File) {
|
||||
final name = Uri.decodeQueryComponent(path.basename(entry.path));
|
||||
final splitter = name.indexOf(".");
|
||||
if(splitter == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final index = int.tryParse(name.substring(0, splitter));
|
||||
if(index == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final questionName = Uri.decodeQueryComponent(name.substring(splitter + 2));
|
||||
map[index] = InfoTile(
|
||||
title: Text(questionName),
|
||||
content: Text(entry.readAsStringSync())
|
||||
);
|
||||
}
|
||||
}
|
||||
_infoTiles = map.values.toList(growable: false);
|
||||
return null;
|
||||
}catch(error) {
|
||||
_infoTiles = [];
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
const InfoPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -30,7 +68,7 @@ class InfoPage extends RebootPage {
|
||||
|
||||
class _InfoPageState extends RebootPageState<InfoPage> {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
RxInt _counter = RxInt(180);
|
||||
RxInt _counter = RxInt(kDebugMode ? 0 : 180);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -48,7 +86,7 @@ class _InfoPageState extends RebootPageState<InfoPage> {
|
||||
}
|
||||
|
||||
@override
|
||||
List<Widget> get settings => infoTiles;
|
||||
List<Widget> get settings => InfoPage._infoTiles;
|
||||
|
||||
@override
|
||||
Widget? get button => Obx(() {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/update_controller.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
@@ -68,3 +70,26 @@ Future<void> _downloadCriticalDllInteractive(String filePath) async {
|
||||
_operations.remove(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
extension InjectableDllExtension on InjectableDll {
|
||||
String get path {
|
||||
final SettingsController settingsController = Get.find<SettingsController>();
|
||||
switch(this){
|
||||
case InjectableDll.reboot:
|
||||
if(_updateController.customGameServer.value) {
|
||||
final file = File(settingsController.gameServerDll.text);
|
||||
if(file.existsSync()) {
|
||||
return file.path;
|
||||
}
|
||||
}
|
||||
|
||||
return rebootDllFile.path;
|
||||
case InjectableDll.console:
|
||||
return settingsController.unrealEngineConsoleDll.text;
|
||||
case InjectableDll.cobalt:
|
||||
return settingsController.backendDll.text;
|
||||
case InjectableDll.memoryFix:
|
||||
return settingsController.memoryLeakDll.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/util/log.dart';
|
||||
import 'package:reboot_launcher/src/util/translations.dart';
|
||||
import 'package:reboot_launcher/src/widget/info_tile.dart';
|
||||
|
||||
final _entries = SplayTreeMap<int, InfoTile>();
|
||||
|
||||
void initInfoTiles() {
|
||||
try {
|
||||
final directory = Directory("${assetsDirectory.path}\\info\\$currentLocale");
|
||||
for(final entry in directory.listSync()) {
|
||||
if(entry is File) {
|
||||
final name = Uri.decodeQueryComponent(path.basename(entry.path));
|
||||
final splitter = name.indexOf(".");
|
||||
if(splitter == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final index = int.tryParse(name.substring(0, splitter));
|
||||
if(index == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final questionName = Uri.decodeQueryComponent(name.substring(splitter + 2));
|
||||
_entries[index] = InfoTile(
|
||||
title: Text(questionName),
|
||||
content: Text(entry.readAsStringSync())
|
||||
);
|
||||
}
|
||||
}
|
||||
}catch(error) {
|
||||
log("[INFO] Error occurred while initializing info tiles: $error");
|
||||
}
|
||||
}
|
||||
|
||||
List<InfoTile> get infoTiles => _entries.values.toList(growable: false);
|
||||
@@ -103,8 +103,8 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
log("[${widget.host ? 'HOST' : 'GAME'}] Setting started...");
|
||||
_setStarted(widget.host, true);
|
||||
log("[${widget.host ? 'HOST' : 'GAME'}] Set started");
|
||||
log("[${widget.host ? 'HOST' : 'GAME'}] Checking dlls: ${_Injectable.values}");
|
||||
for (final injectable in _Injectable.values) {
|
||||
log("[${widget.host ? 'HOST' : 'GAME'}] Checking dlls: ${InjectableDll.values}");
|
||||
for (final injectable in InjectableDll.values) {
|
||||
if(await _getDllFileOrStop(injectable, widget.host) == null) {
|
||||
return;
|
||||
}
|
||||
@@ -239,7 +239,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}else{
|
||||
_gameController.instance.value = instance;
|
||||
}
|
||||
await _injectOrShowError(_Injectable.sslBypassV2, host);
|
||||
await _injectOrShowError(InjectableDll.cobalt, host);
|
||||
log("[${host ? 'HOST' : 'GAME'}] Finished creating game instance");
|
||||
return instance;
|
||||
}
|
||||
@@ -319,46 +319,33 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
_onStop(
|
||||
reason: _StopReason.normal
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(kCorruptedBuildErrors.any((element) => line.contains(element))){
|
||||
}else if(kCorruptedBuildErrors.any((element) => line.contains(element))){
|
||||
_onStop(
|
||||
reason: _StopReason.corruptedVersionError
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(kCannotConnectErrors.any((element) => line.contains(element))){
|
||||
}else if(kCannotConnectErrors.any((element) => line.contains(element))){
|
||||
_onStop(
|
||||
reason: _StopReason.tokenError
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(kLoggedInLines.every((entry) => line.contains(entry))) {
|
||||
}else if(kLoggedInLines.every((entry) => line.contains(entry))) {
|
||||
final instance = host ? _hostingController.instance.value : _gameController.instance.value;
|
||||
if(instance != null && !instance.launched) {
|
||||
instance.launched = true;
|
||||
instance.tokenError = false;
|
||||
await _injectOrShowError(_Injectable.memoryFix, host);
|
||||
await _injectOrShowError(InjectableDll.memoryFix, host);
|
||||
if(!host){
|
||||
await _injectOrShowError(_Injectable.console, host);
|
||||
await _injectOrShowError(InjectableDll.console, host);
|
||||
_onGameClientInjected();
|
||||
}else {
|
||||
final gameServerPort = int.tryParse(_settingsController.gameServerPort.text);
|
||||
if(gameServerPort != null) {
|
||||
await killProcessByPort(gameServerPort);
|
||||
}
|
||||
await _injectOrShowError(_Injectable.reboot, host);
|
||||
await _injectOrShowError(InjectableDll.reboot, host);
|
||||
_onGameServerInjected();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(line.contains(kGameFinishedLine) && host) {
|
||||
}else if(line.contains(kGameFinishedLine) && host) {
|
||||
if(_hostingController.autoRestart.value) {
|
||||
final notification = LocalNotification(
|
||||
title: translations.gameServerEnd,
|
||||
@@ -378,10 +365,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
_onStop(reason: _StopReason.normal, host: true);
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(line.contains("Display") && host && virtualDesktop) {
|
||||
}else if(line.contains(kDisplayInitializedLine) && host && virtualDesktop) {
|
||||
final hostingInstance = _hostingController.instance.value;
|
||||
if(hostingInstance != null && !hostingInstance.movedToVirtualDesktop) {
|
||||
hostingInstance.movedToVirtualDesktop = true;
|
||||
@@ -615,7 +599,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _injectOrShowError(_Injectable injectable, bool hosting) async {
|
||||
Future<void> _injectOrShowError(InjectableDll injectable, bool hosting) async {
|
||||
final instance = hosting ? _hostingController.instance.value : _gameController.instance.value;
|
||||
if (instance == null) {
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] No instance found to inject ${injectable.name}");
|
||||
@@ -637,7 +621,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] Trying to inject ${injectable.name}...");
|
||||
await injectDll(gameProcess, dllPath.path);
|
||||
await injectDll(gameProcess, dllPath);
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] Injected ${injectable.name}");
|
||||
} catch (error, stackTrace) {
|
||||
log("[${hosting ? 'HOST' : 'GAME'}] Cannot inject ${injectable.name}: $error $stackTrace");
|
||||
@@ -650,29 +634,9 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
}
|
||||
|
||||
String _getDllPath(_Injectable injectable) {
|
||||
switch(injectable){
|
||||
case _Injectable.reboot:
|
||||
if(_updateController.customGameServer.value) {
|
||||
final file = File(_settingsController.gameServerDll.text);
|
||||
if(file.existsSync()) {
|
||||
return file.path;
|
||||
}
|
||||
}
|
||||
|
||||
return rebootDllFile.path;
|
||||
case _Injectable.console:
|
||||
return _settingsController.unrealEngineConsoleDll.text;
|
||||
case _Injectable.sslBypassV2:
|
||||
return _settingsController.backendDll.text;
|
||||
case _Injectable.memoryFix:
|
||||
return _settingsController.memoryLeakDll.text;
|
||||
}
|
||||
}
|
||||
|
||||
Future<File?> _getDllFileOrStop(_Injectable injectable, bool host) async {
|
||||
Future<File?> _getDllFileOrStop(InjectableDll injectable, bool host) async {
|
||||
log("[${host ? 'HOST' : 'GAME'}] Checking dll ${injectable}...");
|
||||
final path = _getDllPath(injectable);
|
||||
final path = injectable.path;
|
||||
log("[${host ? 'HOST' : 'GAME'}] Path: $path");
|
||||
final file = File(path);
|
||||
if(await file.exists()) {
|
||||
@@ -712,11 +676,4 @@ enum _StopReason {
|
||||
exitCode;
|
||||
|
||||
bool get isError => name.contains("Error");
|
||||
}
|
||||
|
||||
enum _Injectable {
|
||||
console,
|
||||
sslBypassV2,
|
||||
reboot,
|
||||
memoryFix,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user