mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 11:12:23 +01:00
Switched to embedded lawin server if node is not available
Requested by microsoft
This commit is contained in:
@@ -7,7 +7,6 @@ import 'package:get_storage/get_storage.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/build_controller.dart';
|
import 'package:reboot_launcher/src/controller/build_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/warning_controller.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||||
@@ -21,7 +20,6 @@ void main() async {
|
|||||||
Get.put(GameController());
|
Get.put(GameController());
|
||||||
Get.put(ServerController());
|
Get.put(ServerController());
|
||||||
Get.put(BuildController());
|
Get.put(BuildController());
|
||||||
Get.put(WarningController());
|
|
||||||
SystemTheme.accentColor.load();
|
SystemTheme.accentColor.load();
|
||||||
doWhenWindowReady(() {
|
doWhenWindowReady(() {
|
||||||
const size = Size(600, 380);
|
const size = Size(600, 380);
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import 'package:get/get.dart';
|
|
||||||
|
|
||||||
import 'package:reboot_launcher/src/model/fortnite_build.dart';
|
|
||||||
|
|
||||||
class WarningController extends GetxController {
|
|
||||||
RxBool warning = RxBool(false);
|
|
||||||
}
|
|
||||||
@@ -84,8 +84,8 @@ class _HomePageState extends State<HomePage> with WindowListener {
|
|||||||
|
|
||||||
List<Widget> _createPages(bool data) {
|
List<Widget> _createPages(bool data) {
|
||||||
return [
|
return [
|
||||||
data ? LauncherPage() : _createDownloadWarning(),
|
data ? const LauncherPage() : _createDownloadWarning(),
|
||||||
ServerPage(),
|
const ServerPage(),
|
||||||
const InfoPage()
|
const InfoPage()
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class InfoPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const Expanded(
|
const Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.bottomLeft, child: Text("Version 2.3${kDebugMode ? '-DEBUG' : ''}")))
|
alignment: Alignment.bottomLeft, child: Text("Version 3.0${kDebugMode ? '-DEBUG' : ''}")))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,24 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
|
||||||
import 'package:reboot_launcher/src/widget/deployment_selector.dart';
|
import 'package:reboot_launcher/src/widget/deployment_selector.dart';
|
||||||
import 'package:reboot_launcher/src/widget/launch_button.dart';
|
import 'package:reboot_launcher/src/widget/launch_button.dart';
|
||||||
import 'package:reboot_launcher/src/widget/restart_warning.dart';
|
|
||||||
import 'package:reboot_launcher/src/widget/username_box.dart';
|
import 'package:reboot_launcher/src/widget/username_box.dart';
|
||||||
|
|
||||||
import 'package:reboot_launcher/src/widget/version_selector.dart';
|
import 'package:reboot_launcher/src/widget/version_selector.dart';
|
||||||
|
|
||||||
import 'package:reboot_launcher/src/controller/warning_controller.dart';
|
|
||||||
|
|
||||||
class LauncherPage extends StatelessWidget {
|
class LauncherPage extends StatelessWidget {
|
||||||
final WarningController _warningController = Get.find<WarningController>();
|
const LauncherPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
LauncherPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() => Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_warningController.warning.value) const RestartWarning(),
|
UsernameBox(),
|
||||||
UsernameBox(),
|
VersionSelector(),
|
||||||
VersionSelector(),
|
DeploymentSelector(enabled: true),
|
||||||
DeploymentSelector(enabled: true),
|
const LaunchButton()
|
||||||
const LaunchButton()
|
],
|
||||||
],
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,24 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
|
||||||
import 'package:reboot_launcher/src/controller/warning_controller.dart';
|
|
||||||
import 'package:reboot_launcher/src/widget/local_server_switch.dart';
|
import 'package:reboot_launcher/src/widget/local_server_switch.dart';
|
||||||
import 'package:reboot_launcher/src/widget/port_input.dart';
|
import 'package:reboot_launcher/src/widget/port_input.dart';
|
||||||
|
|
||||||
import 'package:reboot_launcher/src/widget/host_input.dart';
|
import 'package:reboot_launcher/src/widget/host_input.dart';
|
||||||
import 'package:reboot_launcher/src/widget/server_button.dart';
|
import 'package:reboot_launcher/src/widget/server_button.dart';
|
||||||
|
|
||||||
import 'package:reboot_launcher/src/widget/restart_warning.dart';
|
|
||||||
|
|
||||||
class ServerPage extends StatelessWidget {
|
class ServerPage extends StatelessWidget {
|
||||||
final WarningController _warningController = Get.find<WarningController>();
|
const ServerPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
ServerPage({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() => Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_warningController.warning.value) const RestartWarning(),
|
HostInput(),
|
||||||
HostInput(),
|
PortInput(),
|
||||||
PortInput(),
|
LocalServerSwitch(),
|
||||||
LocalServerSwitch(),
|
ServerButton()
|
||||||
ServerButton()
|
]);
|
||||||
]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ File injectLogFile = File("${Platform.environment["Temp"]}/server.txt");
|
|||||||
// This can be done easily with win32 apis but for some reason it doesn't work on all machines
|
// This can be done easily with win32 apis but for some reason it doesn't work on all machines
|
||||||
// Update: it was a missing permission error, it could be refactored now
|
// Update: it was a missing permission error, it could be refactored now
|
||||||
Future<bool> injectDll(int pid, String dll) async {
|
Future<bool> injectDll(int pid, String dll) async {
|
||||||
if(dll.contains("reboot.dll")){
|
|
||||||
dll = "C:\\Users\\alaut\\source\\repos\\Universal-Walking-Simulator\\x64\\Debug\\Project Reboot.dll";
|
|
||||||
}
|
|
||||||
|
|
||||||
var shell = Shell(workingDirectory: internalBinariesDirectory);
|
var shell = Shell(workingDirectory: internalBinariesDirectory);
|
||||||
var process = await shell.run("./injector.exe -p $pid --inject \"$dll\"");
|
var process = await shell.run("./injector.exe -p $pid --inject \"$dll\"");
|
||||||
var success = process.outText.contains("Successfully injected module");
|
var success = process.outText.contains("Successfully injected module");
|
||||||
|
|||||||
@@ -3,28 +3,36 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:archive/archive_io.dart';
|
import 'package:archive/archive_io.dart';
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
import 'package:process_run/shell.dart';
|
import 'package:process_run/shell.dart';
|
||||||
import 'package:reboot_launcher/src/controller/warning_controller.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/binary.dart';
|
import 'package:reboot_launcher/src/util/binary.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
final serverLocation = Directory("${Platform.environment["UserProfile"]}/.reboot_launcher/lawin");
|
final serverLocation = Directory("${Platform.environment["UserProfile"]}/.reboot_launcher/lawin");
|
||||||
const String _serverUrl =
|
const String _serverUrl =
|
||||||
"https://github.com/Lawin0129/LawinServer/archive/refs/heads/main.zip";
|
"https://github.com/Lawin0129/LawinServer/archive/refs/heads/main.zip";
|
||||||
|
const String _portableServerUrl =
|
||||||
|
"https://cdn.discordapp.com/attachments/998020695223193673/1019999251994005504/LawinServer.exe";
|
||||||
const String _nodeUrl =
|
const String _nodeUrl =
|
||||||
"https://nodejs.org/dist/v16.16.0/node-v16.16.0-x64.msi";
|
"https://nodejs.org/dist/v16.16.0/node-v16.16.0-x64.msi";
|
||||||
|
|
||||||
Future<void> downloadServer() async {
|
Future<bool> downloadServer(bool portable) async {
|
||||||
var response = await http.get(Uri.parse(_serverUrl));
|
if(!portable){
|
||||||
var tempZip = File("${Platform.environment["Temp"]}/lawin.zip")
|
var response = await http.get(Uri.parse(_serverUrl));
|
||||||
..writeAsBytesSync(response.bodyBytes);
|
var tempZip = File("${Platform.environment["Temp"]}/lawin.zip");
|
||||||
await extractFileToDisk(tempZip.path, serverLocation.parent.path);
|
await tempZip.writeAsBytes(response.bodyBytes);
|
||||||
var result = Directory("${serverLocation.parent.path}/LawinServer-main");
|
await extractFileToDisk(tempZip.path, serverLocation.parent.path);
|
||||||
await result.rename("${serverLocation.parent.path}/${path.basename(serverLocation.path)}");
|
var result = Directory("${serverLocation.parent.path}/LawinServer-main");
|
||||||
await updateEngineConfig();
|
await result.rename("${serverLocation.parent.path}/${path.basename(serverLocation.path)}");
|
||||||
|
await Process.run("${serverLocation.path}/install_packages.bat", [], workingDirectory: serverLocation.path);
|
||||||
|
await updateEngineConfig();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await http.get(Uri.parse(_portableServerUrl));
|
||||||
|
var server = await loadBinary("LawinServer.exe", true);
|
||||||
|
await server.writeAsBytes(response.bodyBytes);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateEngineConfig() async {
|
Future<void> updateEngineConfig() async {
|
||||||
@@ -113,55 +121,38 @@ Future<Process?> startEmbedded(BuildContext context, bool running, bool needsFre
|
|||||||
await Process.run(releaseBat.path, []);
|
await Process.run(releaseBat.path, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await serverLocation.exists())) {
|
|
||||||
await downloadServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverRunner = File("${serverLocation.path}/start.bat");
|
|
||||||
if (!(await serverRunner.exists())) {
|
|
||||||
_showNoRunnerError(context, serverRunner);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nodeProcess = await Process.run("where", ["node"]);
|
var nodeProcess = await Process.run("where", ["node"]);
|
||||||
if(nodeProcess.exitCode != 0) {
|
if(nodeProcess.exitCode == 0) {
|
||||||
var shouldInstall = await _showMissingNodeWarning(context);
|
if(!(await serverLocation.exists()) && !(await _showServerDownloadInfo(context, false))){
|
||||||
if (!shouldInstall) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await _showNodeInfo(context);
|
var serverRunner = File("${serverLocation.path}/start.bat");
|
||||||
if(result == null){
|
if (!(await serverRunner.exists())) {
|
||||||
showSnackbar(
|
_showEmbeddedError(context, serverRunner.path);
|
||||||
context,
|
|
||||||
const Snackbar(
|
|
||||||
content: Text(
|
|
||||||
"Node download cancelled"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var controller = Get.find<WarningController>();
|
var nodeModules = Directory("${serverLocation.path}/node_modules");
|
||||||
controller.warning(true);
|
if (!(await nodeModules.exists())) {
|
||||||
await launchUrl(result.uri);
|
await Process.run("${serverLocation.path}/install_packages.bat", [],
|
||||||
return null;
|
workingDirectory: serverLocation.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
var nodeModules = Directory("${serverLocation.path}/node_modules");
|
return await Process.start(serverRunner.path, [],
|
||||||
if (!(await nodeModules.exists())) {
|
|
||||||
await Process.run("${serverLocation.path}/install_packages.bat", [],
|
|
||||||
workingDirectory: serverLocation.path);
|
workingDirectory: serverLocation.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
return await Process.start(serverRunner.path, [],
|
var portableServer = await loadBinary("LawinServer.exe", true);
|
||||||
workingDirectory: serverLocation.path);
|
if(!(await portableServer.exists()) && !(await _showServerDownloadInfo(context, true))){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await Process.start(portableServer.path, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File?> _showNodeInfo(BuildContext context) async {
|
Future<bool> _showServerDownloadInfo(BuildContext context, bool portable) async {
|
||||||
var nodeFuture = downloadNode();
|
var nodeFuture = downloadServer(portable);
|
||||||
var result = await showDialog<bool>(
|
var result = await showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ContentDialog(
|
builder: (context) => ContentDialog(
|
||||||
@@ -184,7 +175,7 @@ Future<File?> _showNodeInfo(BuildContext context) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return const InfoLabel(
|
return const InfoLabel(
|
||||||
label: "Downloading node installer...",
|
label: "Downloading lawin server...",
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ProgressBar()
|
child: ProgressBar()
|
||||||
@@ -209,15 +200,15 @@ Future<File?> _showNodeInfo(BuildContext context) async {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return result == null || !result ? null : await nodeFuture;
|
return result != null && result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showNoRunnerError(BuildContext context, File serverRunner) {
|
void _showEmbeddedError(BuildContext context, String path) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => ContentDialog(
|
builder: (context) => ContentDialog(
|
||||||
content: Text(
|
content: Text(
|
||||||
"Cannot start server, missing start.bat at ${serverRunner.path}",
|
"Cannot start server, missing $path",
|
||||||
textAlign: TextAlign.center),
|
textAlign: TextAlign.center),
|
||||||
actions: [
|
actions: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -232,28 +223,6 @@ void _showNoRunnerError(BuildContext context, File serverRunner) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _showMissingNodeWarning(BuildContext context) async {
|
|
||||||
return await showDialog<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => ContentDialog(
|
|
||||||
content: const SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Text("Node is required to run the embedded server",
|
|
||||||
textAlign: TextAlign.center)),
|
|
||||||
actions: [
|
|
||||||
FilledButton(
|
|
||||||
onPressed: () => Navigator.of(context).pop(false),
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: ButtonState.all(Colors.red)),
|
|
||||||
child: const Text('Close'),
|
|
||||||
),
|
|
||||||
FilledButton(
|
|
||||||
child: const Text('Install'),
|
|
||||||
onPressed: () => Navigator.of(context).pop(true)),
|
|
||||||
],
|
|
||||||
)) ??
|
|
||||||
false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> _showAlreadyBindPortWarning(BuildContext context) async {
|
Future<bool> _showAlreadyBindPortWarning(BuildContext context) async {
|
||||||
return await showDialog<bool>(
|
return await showDialog<bool>(
|
||||||
|
|||||||
@@ -174,7 +174,6 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
List<String> _createProcessArguments() {
|
List<String> _createProcessArguments() {
|
||||||
return [
|
return [
|
||||||
"-log",
|
|
||||||
"-epicapp=Fortnite",
|
"-epicapp=Fortnite",
|
||||||
"-epicenv=Prod",
|
"-epicenv=Prod",
|
||||||
"-epiclocale=en-us",
|
"-epiclocale=en-us",
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -47,7 +47,7 @@ msix_config:
|
|||||||
display_name: Reboot Launcher
|
display_name: Reboot Launcher
|
||||||
publisher_display_name: Auties00
|
publisher_display_name: Auties00
|
||||||
identity_name: 31868Auties00.RebootLauncher
|
identity_name: 31868Auties00.RebootLauncher
|
||||||
msix_version: 2.3.0.0
|
msix_version: 3.0.0.0
|
||||||
publisher: CN=E6CD08C6-DECF-4034-A3EB-2D5FA2CA8029
|
publisher: CN=E6CD08C6-DECF-4034-A3EB-2D5FA2CA8029
|
||||||
logo_path: ./assets/icons/reboot.ico
|
logo_path: ./assets/icons/reboot.ico
|
||||||
architecture: x64
|
architecture: x64
|
||||||
|
|||||||
Reference in New Issue
Block a user