Finished Launcher

Everything is smooth and the UI is perfect
This commit is contained in:
Alessandro Autiero
2022-09-14 18:55:41 +02:00
parent df4bd5772f
commit 1c6137a4d9
13 changed files with 132 additions and 92 deletions

View File

@@ -1,4 +1,6 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:bitsdojo_window_windows/bitsdojo_window_windows.dart'
show WinDesktopWindow;
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
@@ -6,11 +8,13 @@ import 'package:reboot_launcher/src/controller/build_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/warning_controller.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:system_theme/system_theme.dart';
import 'package:reboot_launcher/src/page/home_page.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemTheme.accentColor.load();
await GetStorage.init("game");
await GetStorage.init("server");
await GetStorage.init("update");
@@ -21,6 +25,8 @@ void main() async {
SystemTheme.accentColor.load();
doWhenWindowReady(() {
const size = Size(600, 380);
var window = appWindow as WinDesktopWindow;
window.setWindowCutOnMaximize(appBarSize * 2);
appWindow.size = size;
appWindow.alignment = Alignment.center;
appWindow.title = "Reboot Launcher";

View File

@@ -1,9 +1,13 @@
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/page/info_page.dart';
import 'package:reboot_launcher/src/page/launcher_page.dart';
import 'package:reboot_launcher/src/page/server_page.dart';
import 'package:reboot_launcher/src/widget/window_buttons.dart';
import 'package:reboot_launcher/src/widget/window_border.dart';
import 'package:window_manager/window_manager.dart';
import '../util/os.dart';
import '../util/reboot.dart';
class HomePage extends StatefulWidget {
@@ -13,51 +17,92 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final List<Widget> _children = [LauncherPage(), ServerPage(), const InfoPage()];
class _HomePageState extends State<HomePage> with WindowListener {
late final Future _future;
bool _focused = true;
int _index = 0;
@override
void initState() {
windowManager.addListener(this);
_future = downloadRebootDll();
super.initState();
}
@override
void dispose() {
windowManager.removeListener(this);
super.dispose();
}
@override
void onWindowFocus() {
setState(() => _focused = true);
}
@override
void onWindowBlur() {
setState(() => _focused = false);
}
@override
Widget build(BuildContext context) {
return NavigationView(
pane: NavigationPane(
selected: _index,
onChanged: (index) => setState(() => _index = index),
displayMode: PaneDisplayMode.top,
indicator: const EndNavigationIndicator(),
items: [
_createPane("Launcher", FluentIcons.game),
_createPane("Server", FluentIcons.server_enviroment),
_createPane("Info", FluentIcons.info),
],
trailing: const WindowTitleBar()),
content: FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
"An error occurred while loading the launcher: ${snapshot.error}",
textAlign: TextAlign.center));
}
return Stack(
children: [
NavigationView(
pane: NavigationPane(
selected: _index,
onChanged: (index) => setState(() => _index = index),
displayMode: PaneDisplayMode.top,
indicator: const EndNavigationIndicator(),
items: [
_createPane("Launcher", FluentIcons.game),
_createPane("Server", FluentIcons.server_enviroment),
_createPane("Info", FluentIcons.info),
],
trailing: WindowTitleBar(focused: _focused)),
content: FutureBuilder(
future: _future,
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
"An error occurred while loading the launcher: ${snapshot.error}",
textAlign: TextAlign.center));
}
return NavigationBody(
index: _index,
children: _createPages(snapshot.hasData));
})
),
if (!snapshot.hasData) {
return const Center(child: ProgressRing());
}
if(_focused && isWin11)
const WindowBorder()
],
);
}
return NavigationBody(
index: _index,
children: _children
);
}
)
List<Widget> _createPages(bool data) {
return [
data ? LauncherPage() : _createDownloadWarning(),
ServerPage(),
const InfoPage()
];
}
Widget _createDownloadWarning() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
ProgressRing(),
SizedBox(height: 16.0),
Text("Updating Reboot DLL...")
],
),
],
);
}

View File

@@ -1,4 +1,5 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:url_launcher/url_launcher.dart';
const String _discordLink = "https://discord.gg/rTzBQH3N";
@@ -30,7 +31,7 @@ class InfoPage extends StatelessWidget {
),
const Expanded(
child: Align(
alignment: Alignment.bottomLeft, child: Text("Version 2.2")))
alignment: Alignment.bottomLeft, child: Text("Version 2.3${kDebugMode ? '-DEBUG' : ''}")))
],
);
}

View File

@@ -19,10 +19,12 @@ final _manifestSourceUrl = Uri.parse(
final _archiveCookieUrl = Uri.parse("http://allinstaller.xyz/rel");
final _archiveSourceUrl = Uri.parse("http://allinstaller.xyz/rel?i=1");
Future<List<FortniteBuild>> fetchBuilds() async => [
...await _fetchArchives(),
...await _fetchManifests()
]..sort((first, second) => first.version.compareTo(second.version));
Future<List<FortniteBuild>> fetchBuilds(ignored) async {
var futures = await Future.wait([_fetchArchives(), _fetchManifests()]);
return futures.expand((element) => element)
.toList()
..sort((first, second) => first.version.compareTo(second.version));
}
Future<List<FortniteBuild>> _fetchArchives() async {
var cookieResponse = await http.get(_archiveCookieUrl);
@@ -118,22 +120,6 @@ Future<void> downloadArchiveBuild(String archiveUrl, String destination,
await output.create(recursive: true);
var shell = Shell(workingDirectory: internalBinariesDirectory);
await shell.run("./winrar.exe x ${tempFile.path} *.* \"${output.path}\"");
var children = output.listSync();
if(children.isEmpty){
throw Exception("Missing extracted directory"); // No content
}
if(children.length != 1){
return; // Already in the correct schema
}
// Extract directories from wrapper directory
var wrapper = Directory(children[0].path);
for(var entry in wrapper.listSync()){
await entry.rename("${output.path}/${path.basename(entry.path)}");
}
await wrapper.delete();
} finally {
if (await tempFile.exists()) {
tempFile.delete();

View File

@@ -8,6 +8,10 @@ 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
// Update: it was a missing permission error, it could be refactored now
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 process = await shell.run("./injector.exe -p $pid --inject \"$dll\"");
var success = process.outText.contains("Successfully injected module");

View File

@@ -26,8 +26,8 @@ Future<File> downloadRebootDll() async {
}
var response = await http.get(Uri.parse(_rebootUrl));
var tempZip = File("${Platform.environment["Temp"]}/reboot.zip")
..writeAsBytesSync(response.bodyBytes);
var tempZip = File("${Platform.environment["Temp"]}/reboot.zip");
await tempZip.writeAsBytes(response.bodyBytes);
await extractFileToDisk(tempZip.path, safeBinariesDirectory);
var pdb = await loadBinary("Project Reboot.pdb", true);
pdb.delete();

View File

@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'package:async/async.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/controller/build_controller.dart';
import 'package:reboot_launcher/src/controller/game_controller.dart';
@@ -36,7 +37,10 @@ class _AddServerVersionState extends State<AddServerVersion> {
@override
void initState() {
_future = _fetchBuilds();
_future = _buildController.builds != null
? Future.value(true)
: compute(fetchBuilds, null)
.then((value) => _buildController.builds = value);
super.initState();
}
@@ -266,15 +270,6 @@ class _AddServerVersionState extends State<AddServerVersion> {
}
}
Future<bool> _fetchBuilds() async {
if (_buildController.builds != null) {
return false;
}
_buildController.builds = await fetchBuilds();
return true;
}
String? _checkDownloadDestination(text) {
if (text == null || text.isEmpty) {
return 'Invalid download path';

View File

@@ -1,9 +1,14 @@
import 'dart:io';
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:system_theme/system_theme.dart';
class WindowTitleBar extends StatelessWidget {
const WindowTitleBar({Key? key}) : super(key: key);
final bool focused;
const WindowTitleBar({Key? key, required this.focused}) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -13,28 +18,25 @@ class WindowTitleBar extends StatelessWidget {
children: [
MinimizeWindowButton(
colors: WindowButtonColors(
iconNormal: lightMode ? Colors.black : Colors.white,
iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter,
iconMouseDown: lightMode ? Colors.black : Colors.white,
iconMouseOver: lightMode ? Colors.black : Colors.white,
normal: Colors.transparent,
mouseOver: _getColor(context),
mouseDown: _getColor(context).withOpacity(0.7)),
mouseOver: _color,
mouseDown: _color.withOpacity(0.7)),
),
MaximizeWindowButton(
colors: WindowButtonColors(
iconNormal: lightMode ? Colors.black : Colors.white,
iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter,
iconMouseDown: lightMode ? Colors.black : Colors.white,
iconMouseOver: lightMode ? Colors.black : Colors.white,
normal: Colors.transparent,
mouseOver: _getColor(context),
mouseDown: _getColor(context).withOpacity(0.7)),
mouseOver: _color,
mouseDown: _color.withOpacity(0.7)),
),
CloseWindowButton(
onPressed: () {
appWindow.close();
},
colors: WindowButtonColors(
iconNormal: lightMode ? Colors.black : Colors.white,
iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter,
iconMouseDown: lightMode ? Colors.black : Colors.white,
iconMouseOver: lightMode ? Colors.black : Colors.white,
normal: Colors.transparent,
@@ -46,8 +48,6 @@ class WindowTitleBar extends StatelessWidget {
);
}
Color _getColor(BuildContext context) =>
FluentTheme.of(context).brightness.isDark
? SystemTheme.accentColor.light
: SystemTheme.accentColor.light;
Color get _color =>
SystemTheme.accentColor.accent;
}