diff --git a/assets/binaries/DefaultEngine.ini b/assets/binaries/DefaultEngine.ini new file mode 100644 index 0000000..a250c5e --- /dev/null +++ b/assets/binaries/DefaultEngine.ini @@ -0,0 +1,15 @@ +[OnlineSubsystemMcp.Xmpp] +bUseSSL=false +ServerAddr="ws://lawinserverxmpp.herokuapp.com" +ServerPort=80 + +[OnlineSubsystemMcp.Xmpp Prod] +bUseSSL=false +ServerAddr="ws://lawinserverxmpp.herokuapp.com" +ServerPort=80 + +[OnlineSubsystemMcp] +bUsePartySystemV2=false + +[OnlineSubsystemMcp.OnlinePartySystemMcpAdapter] +bUsePartySystemV2=false \ No newline at end of file diff --git a/assets/binaries/reboot.dll b/assets/binaries/reboot.dll deleted file mode 100644 index fcaff05..0000000 Binary files a/assets/binaries/reboot.dll and /dev/null differ diff --git a/lib/src/page/home_page.dart b/lib/src/page/home_page.dart index b19c73b..35c7c2a 100644 --- a/lib/src/page/home_page.dart +++ b/lib/src/page/home_page.dart @@ -11,6 +11,7 @@ import 'package:reboot_launcher/src/widget/window_buttons.dart'; import '../model/fortnite_version.dart'; import '../util/generic_controller.dart'; +import '../util/reboot.dart'; import '../util/version_controller.dart'; class HomePage extends StatefulWidget { @@ -31,16 +32,23 @@ class _HomePageState extends State { late final GenericController _serverController; late final GenericController _startedServerController; late final GenericController _startedGameController; - + late Future _future; bool _loaded = false; int _index = 0; + @override + void initState(){ + _future = _load(); + super.initState(); + } + Future _load() async { if (_loaded) { return false; } var preferences = await SharedPreferences.getInstance(); + await downloadRebootDll(preferences); Iterable json = jsonDecode(preferences.getString("versions") ?? "[]"); var versions = @@ -103,7 +111,7 @@ class _HomePageState extends State { ], trailing: const WindowTitleBar()), content: FutureBuilder( - future: _load(), + future: _future, builder: (context, snapshot) { if (snapshot.hasError) { return Center( diff --git a/lib/src/page/launcher_page.dart b/lib/src/page/launcher_page.dart index f34bf76..22010ae 100644 --- a/lib/src/page/launcher_page.dart +++ b/lib/src/page/launcher_page.dart @@ -52,7 +52,7 @@ class LauncherPage extends StatelessWidget { DeploymentSelector( controller: rebootController, onSelected: () => _streamController.add(null), - enabled: false + enabled: true ), LaunchButton( usernameController: usernameController, diff --git a/lib/src/util/download_build.dart b/lib/src/util/download_build.dart index 6a00279..a76e560 100644 --- a/lib/src/util/download_build.dart +++ b/lib/src/util/download_build.dart @@ -8,7 +8,7 @@ import 'package:unrar_file/unrar_file.dart'; Future downloadManifestBuild(String manifestUrl, String destination, Function(double) onProgress) async { - var process = await Process.start(await locateBinary("build.exe"), [manifestUrl, destination]); + var process = await Process.start(await locateAndCopyBinary("build.exe"), [manifestUrl, destination]); process.errLines .where((message) => message.contains("%")) diff --git a/lib/src/util/locate_binary.dart b/lib/src/util/locate_binary.dart index b89137f..8589e59 100644 --- a/lib/src/util/locate_binary.dart +++ b/lib/src/util/locate_binary.dart @@ -1,7 +1,7 @@ import 'dart:io'; -Future locateBinary(String binary) async{ - var originalFile = File("$binariesDirectory\\$binary"); +Future locateAndCopyBinary(String binary) async{ + var originalFile = locateBinary(binary); var tempFile = File("${Platform.environment["Temp"]}\\$binary"); if(!(await tempFile.exists())){ await originalFile.copy("${Platform.environment["Temp"]}\\$binary"); @@ -10,5 +10,9 @@ Future locateBinary(String binary) async{ return tempFile.path; } +File locateBinary(String binary){ + return File("$binariesDirectory\\$binary"); +} + String get binariesDirectory => "${File(Platform.resolvedExecutable).parent.path}\\data\\flutter_assets\\assets\\binaries"; diff --git a/lib/src/util/reboot.dart b/lib/src/util/reboot.dart new file mode 100644 index 0000000..671f0a4 --- /dev/null +++ b/lib/src/util/reboot.dart @@ -0,0 +1,44 @@ +import 'dart:io'; + +import 'package:archive/archive_io.dart'; +import 'package:reboot_launcher/src/util/locate_binary.dart'; +import 'package:http/http.dart' as http; +import 'package:crypto/crypto.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +const _rebootUrl = + "https://nightly.link/UWUFN/Universal-Walking-Simulator/workflows/msbuild/master/Release.zip"; + +Future _getLastUpdate(SharedPreferences preferences) async { + var timeInMillis = preferences.getInt("last_update"); + return timeInMillis != null ? DateTime.fromMillisecondsSinceEpoch(timeInMillis) : null; +} + +Future downloadRebootDll(SharedPreferences preferences) async { + var now = DateTime.now(); + var oldRebootDll = locateBinary("reboot.dll"); + var lastUpdate = await _getLastUpdate(preferences); + var exists = await oldRebootDll.exists(); + if(lastUpdate != null && now.difference(lastUpdate).inHours <= 24 && exists){ + return oldRebootDll; + } + + var response = await http.get(Uri.parse(_rebootUrl)); + var tempZip = File("${Platform.environment["Temp"]}/reboot.zip") + ..writeAsBytesSync(response.bodyBytes); + await extractFileToDisk(tempZip.path, binariesDirectory); + locateBinary("Project Reboot.pdb").delete(); + var rebootDll = locateBinary("Project Reboot.dll"); + if (!(await rebootDll.exists())) { + throw Exception("Missing reboot dll"); + } + + preferences.setInt("last_update", now.millisecondsSinceEpoch); + if (exists && sha1.convert(await oldRebootDll.readAsBytes()) == sha1.convert(await rebootDll.readAsBytes())) { + rebootDll.delete(); + return oldRebootDll; + } + + await rebootDll.rename(oldRebootDll.path); + return oldRebootDll; +} diff --git a/lib/src/util/server.dart b/lib/src/util/server.dart index 4dbb167..a4c95fa 100644 --- a/lib/src/util/server.dart +++ b/lib/src/util/server.dart @@ -7,20 +7,30 @@ import 'package:http/http.dart' as http; import 'package:path/path.dart' as path; import 'package:process_run/shell.dart'; import 'package:reboot_launcher/src/util/locate_binary.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; +final serverLocation = Directory("${Platform.environment["UserProfile"]}/.lawin"); const String _serverUrl = "https://github.com/Lawin0129/LawinServer/archive/refs/heads/main.zip"; const String _nodeUrl = "https://nodejs.org/dist/v16.16.0/node-v16.16.0-x64.msi"; -Future downloadServer(Directory output) async { +Future downloadServer() async { var response = await http.get(Uri.parse(_serverUrl)); var tempZip = File("${Platform.environment["Temp"]}/lawin.zip") ..writeAsBytesSync(response.bodyBytes); - await extractFileToDisk(tempZip.path, output.parent.path); - var result = Directory("${output.parent.path}/LawinServer-main"); - result.renameSync("${output.parent.path}/${path.basename(output.path)}"); + await extractFileToDisk(tempZip.path, serverLocation.parent.path); + var result = Directory("${serverLocation.parent.path}/LawinServer-main"); + await result.rename("${serverLocation.parent.path}/${path.basename(serverLocation.path)}"); + await updateEngineConfig(); +} + +Future updateEngineConfig() async { + var engine = File("${serverLocation.path}/CloudStorage/DefaultEngine.ini"); + await engine.writeAsString(await locateBinary("DefaultEngine.ini").readAsString()); + var preferences = await SharedPreferences.getInstance(); + preferences.setBool("config_update", true); } Future downloadNode() async { @@ -34,7 +44,7 @@ Future downloadNode() async { } Future isPortFree() async { - var process = await Process.run(await locateBinary("port.bat"), []); + var process = await Process.run(await locateAndCopyBinary("port.bat"), []); return !process.outText.contains(" LISTENING "); // Goofy way, best we got } @@ -87,27 +97,24 @@ Future _pingAddress(String host, String port) async { && process.outText.contains("TcpTestSucceeded : True"); } -Future startEmbedded(BuildContext context, bool running, bool askFreePort) async { +Future startEmbedded(BuildContext context, bool running, bool needsFreePort) async { if (running) { - await Process.run(await locateBinary("release.bat"), []); + await Process.run(await locateAndCopyBinary("release.bat"), []); return null; } var free = await isPortFree(); - if (!free) { - if(askFreePort) { - var shouldKill = await _showAlreadyBindPortWarning(context); - if (!shouldKill) { - return null; - } + if (!free && needsFreePort) { + var shouldKill = await _showAlreadyBindPortWarning(context); + if (!shouldKill) { + return null; } - await Process.run(await locateBinary("release.bat"), []); + await Process.run(await locateAndCopyBinary("release.bat"), []); } - var serverLocation = Directory("${Platform.environment["UserProfile"]}/.lawin"); if (!(await serverLocation.exists())) { - await downloadServer(serverLocation); + await downloadServer(); } var serverRunner = File("${serverLocation.path}/start.bat"); @@ -141,7 +148,7 @@ Future startEmbedded(BuildContext context, bool running, bool askFreeP showSnackbar( context, const Snackbar( - content: Text("Start the server when node is installed"))); // Using a infobr could be nicer + content: Text("Start the server when node is installed"))); // Using a infobar could be nicer return null; } @@ -151,6 +158,11 @@ Future startEmbedded(BuildContext context, bool running, bool askFreeP workingDirectory: serverLocation.path); } + var preferences = await SharedPreferences.getInstance(); + if(!(preferences.getBool("config_update") ?? false)){ + await updateEngineConfig(); + } + return await Process.start(serverRunner.path, [], workingDirectory: serverLocation.path); } diff --git a/lib/src/widget/add_server_version.dart b/lib/src/widget/add_server_version.dart index 41c2c01..4ac6076 100644 --- a/lib/src/widget/add_server_version.dart +++ b/lib/src/widget/add_server_version.dart @@ -31,6 +31,7 @@ class _AddServerVersionState extends State { late TextEditingController _nameController; late TextEditingController _pathController; late DownloadStatus _status; + late Future _future; double _downloadProgress = 0; String? _error; Process? _process; @@ -38,6 +39,7 @@ class _AddServerVersionState extends State { @override void initState() { + _future = _fetchBuilds(); _buildController = GenericController(initialValue: null); _nameController = TextEditingController(); _pathController = TextEditingController(); @@ -51,7 +53,7 @@ class _AddServerVersionState extends State { _pathController.dispose(); _nameController.dispose(); if (_process != null && _status == DownloadStatus.downloading) { - locateBinary("stop.bat") + locateAndCopyBinary("stop.bat") .then((value) => Process.runSync(value, [])); // kill doesn't work :/ widget.onCancel(); } @@ -79,21 +81,20 @@ class _AddServerVersionState extends State { style: ButtonStyle(backgroundColor: ButtonState.all(Colors.red)), child: const Text('Close')), FilledButton( - onPressed: () => _startDownload(context), - child: const Text('Download'), + onPressed: () => _startDownload(context), + child: const Text('Download'), ) ]; case DownloadStatus.error: return [ SizedBox( - width: double.infinity, - child: FilledButton( - onPressed: () => _onClose(), - style: ButtonStyle(backgroundColor: ButtonState.all(Colors.red)), - child: const Text('Close') - ) - ) + width: double.infinity, + child: FilledButton( + onPressed: () => _onClose(), + style: + ButtonStyle(backgroundColor: ButtonState.all(Colors.red)), + child: const Text('Close'))) ]; default: return [ @@ -122,12 +123,13 @@ class _AddServerVersionState extends State { try { setState(() => _status = DownloadStatus.downloading); var build = _buildController.value!; - if(build.hasManifest) { - _process = await downloadManifestBuild(build.link, _pathController.text, - _onDownloadProgress); + if (build.hasManifest) { + _process = await downloadManifestBuild( + build.link, _pathController.text, _onDownloadProgress); _process!.exitCode.then((value) => _onDownloadComplete()); - }else{ - downloadArchiveBuild(build.link, _pathController.text, _onDownloadProgress, _onUnrar) + } else { + downloadArchiveBuild( + build.link, _pathController.text, _onDownloadProgress, _onUnrar) .then((value) => _onDownloadComplete()) .catchError(_handleError); } @@ -137,7 +139,7 @@ class _AddServerVersionState extends State { } void _handleError(Object exception) { - var message = exception.toString(); + var message = exception.toString(); _onDownloadError(message.contains(":") ? " ${message.substring(message.indexOf(":") + 1)}" : message); @@ -184,7 +186,7 @@ class _AddServerVersionState extends State { Widget _createDownloadVersionBody() { return FutureBuilder( - future: _fetchBuilds(), + future: _future, builder: (context, snapshot) { if (snapshot.hasError) { return Text("Cannot fetch builds: ${snapshot.error}", @@ -199,63 +201,62 @@ class _AddServerVersionState extends State { ); } - switch (_status) { - case DownloadStatus.none: - return Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - BuildSelector(builds: _builds!, controller: _buildController), - VersionNameInput( - controller: _nameController, - versions: widget.controller.versions, - ), - SelectFile( - label: "Destination", - placeholder: "Type the download destination", - windowTitle: "Select download destination", - allowNavigator: false, - controller: _pathController, - validator: _checkDownloadDestination), - ], - ); - case DownloadStatus.downloading: - return InfoLabel( - label: "Downloading", - child: InfoLabel( - label: "${_downloadProgress.round()}%", - child: SizedBox( - width: double.infinity, - child: - ProgressBar(value: _downloadProgress.toDouble()))), - ); - case DownloadStatus.extracting: - return const InfoLabel( - label: "Extracting", - child: InfoLabel( - label: "This might take a while...", - child: SizedBox( - width: double.infinity, - child: - ProgressBar() - ), - ), - ); - case DownloadStatus.done: - return const SizedBox( - width: double.infinity, - child: Text("The download was completed successfully!", - textAlign: TextAlign.center)); - case DownloadStatus.error: - return SizedBox( - width: double.infinity, - child: Text( - "An exception was thrown during the download process:$_error", - textAlign: TextAlign.center)); - } + return _buildBody(); }); } + Widget _buildBody() { + switch (_status) { + case DownloadStatus.none: + return Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + BuildSelector(builds: _builds!, controller: _buildController), + VersionNameInput( + controller: _nameController, + versions: widget.controller.versions, + ), + SelectFile( + label: "Destination", + placeholder: "Type the download destination", + windowTitle: "Select download destination", + allowNavigator: false, + controller: _pathController, + validator: _checkDownloadDestination), + ], + ); + case DownloadStatus.downloading: + return InfoLabel( + label: "Downloading", + child: InfoLabel( + label: "${_downloadProgress.round()}%", + child: SizedBox( + width: double.infinity, + child: ProgressBar(value: _downloadProgress.toDouble()))), + ); + case DownloadStatus.extracting: + return const InfoLabel( + label: "Extracting", + child: InfoLabel( + label: "This might take a while...", + child: SizedBox(width: double.infinity, child: ProgressBar()), + ), + ); + case DownloadStatus.done: + return const SizedBox( + width: double.infinity, + child: Text("The download was completed successfully!", + textAlign: TextAlign.center)); + case DownloadStatus.error: + return SizedBox( + width: double.infinity, + child: Text( + "An exception was thrown during the download process:$_error", + textAlign: TextAlign.center)); + } + } + Future _fetchBuilds() async { if (_builds != null) { return false; diff --git a/lib/src/widget/launch_button.dart b/lib/src/widget/launch_button.dart index bcd07a6..171a066 100644 --- a/lib/src/widget/launch_button.dart +++ b/lib/src/widget/launch_button.dart @@ -164,7 +164,7 @@ class _LaunchButtonState extends State { } try{ - var success = await injectDll(gameProcess.pid, await locateBinary(binary)); + var success = await injectDll(gameProcess.pid, await locateAndCopyBinary(binary)); if(success){ return; } diff --git a/lib/src/widget/smart_input.dart b/lib/src/widget/smart_input.dart index 5ebced7..010a452 100644 --- a/lib/src/widget/smart_input.dart +++ b/lib/src/widget/smart_input.dart @@ -48,12 +48,7 @@ class _SmartInputState extends State { return; } - var decoded = preferences.getString(widget.keyName); - if(decoded == null) { - return; - } - - widget.controller.text = decoded; + widget.controller.text = preferences.getString(widget.keyName) ?? ""; } TextBox _buildTextBox() { diff --git a/pubspec.yaml b/pubspec.yaml index 5a23db6..d5547de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -25,6 +25,7 @@ dependencies: win32_suspend_process: ^1.0.0 version: ^3.0.2 unrar_file: ^1.1.0 + crypto: ^3.0.2 dev_dependencies: flutter_test: