Switched to embedded lawin server if node is not available

Requested by microsoft
This commit is contained in:
Alessandro Autiero
2022-09-15 18:30:27 +02:00
parent c9f99d98d2
commit 273214ceef
11 changed files with 67 additions and 142 deletions

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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()
]; ];
} }

View File

@@ -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' : ''}")))
], ],
); );
} }

View File

@@ -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() ],
], );
));
} }
} }

View File

@@ -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() ]);
]));
} }
} }

View File

@@ -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");

View File

@@ -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>(

View File

@@ -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",

View File

@@ -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
);
}
}

View File

@@ -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