This commit is contained in:
Alessandro Autiero
2023-08-24 19:28:31 +02:00
parent cd7db4cf71
commit 69f2dcd527
70 changed files with 677 additions and 1087 deletions

View File

@@ -68,12 +68,11 @@ void main(List<String> args) async {
}
stdout.writeln("Launching game...");
if(version.executable == null){
var executable = await version.executable;
if(executable == null){
throw Exception("Missing game executable at: ${version.location.path}");
}
await patchHeadless(version.executable!);
var serverType = getServerType(result);
var serverHost = result["server-host"] ?? serverJson["${serverType.id}_host"];
var serverPort = result["server-port"] ?? serverJson["${serverType.id}_port"];

View File

@@ -1,8 +1,5 @@
import 'dart:async';
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';
@@ -17,6 +14,8 @@ import 'package:reboot_launcher/src/ui/page/home_page.dart';
import 'package:reboot_launcher/supabase.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:system_theme/system_theme.dart';
import 'package:flutter_acrylic/flutter_acrylic.dart';
import 'package:window_manager/window_manager.dart';
const double kDefaultWindowWidth = 1024;
const double kDefaultWindowHeight = 1024;
@@ -42,21 +41,21 @@ void main() async {
Get.put(BuildController());
Get.put(SettingsController());
Get.put(HostingController());
doWhenWindowReady(() {
var controller = Get.find<SettingsController>();
var size = Size(controller.width, controller.height);
var window = appWindow as WinDesktopWindow;
window.setWindowCutOnMaximize(appBarSize * 2);
appWindow.size = size;
if(controller.offsetX != null && controller.offsetY != null){
appWindow.position = Offset(controller.offsetX!, controller.offsetY!);
}else {
appWindow.alignment = Alignment.center;
}
appWindow.title = "Reboot Launcher";
appWindow.show();
});
await windowManager.ensureInitialized();
var controller = Get.find<SettingsController>();
var size = Size(controller.width, controller.height);
await windowManager.setSize(size);
if(controller.offsetX != null && controller.offsetY != null){
await windowManager.setPosition(Offset(controller.offsetX!, controller.offsetY!));
}else {
await windowManager.setAlignment(Alignment.center);
};
await Window.initialize();
await Window.setEffect(
effect: WindowEffect.acrylic,
color: Colors.transparent,
dark: SystemTheme.isDarkMode
);
var supabase = Supabase.instance.client;
await supabase.from('hosts')
.delete()
@@ -91,6 +90,7 @@ class _RebootApplicationState extends State<RebootApplication> {
FluentThemeData _createTheme(Brightness brightness) => FluentThemeData(
brightness: brightness,
accentColor: SystemTheme.accentColor.accent.toAccentColor(),
visualDensity: VisualDensity.standard
visualDensity: VisualDensity.standard,
scaffoldBackgroundColor: Colors.transparent
);
}

View File

@@ -1,8 +1,7 @@
import 'dart:collection';
import 'dart:convert';
import 'dart:io';
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

View File

@@ -1,9 +1,8 @@
import 'dart:convert';
import 'package:args/args.dart';
import '../model/fortnite_version.dart';
import '../model/server_type.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
Iterable<String> getServerTypes() => ServerType.values.map((entry) => entry.id);

View File

@@ -2,12 +2,11 @@ import 'dart:io';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/cli.dart';
import '../model/fortnite_version.dart';
import '../util/injector.dart';
import '../util/os.dart';
import '../util/process.dart';
import '../util/server.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/util/injector.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/process.dart';
import 'package:reboot_launcher/src/util/server.dart';
final List<String> _errorStrings = [
"port 3551 failed: Connection refused",
@@ -25,10 +24,9 @@ Future<void> startGame() async {
await _startLauncherProcess(version);
await _startEacProcess(version);
var gamePath = version.executable?.path;
if (gamePath == null) {
throw Exception("${version.location
.path} no longer contains a Fortnite executable, did you delete or move it?");
var executable = await version.executable;
if (executable == null) {
throw Exception("${version.location.path} no longer contains a Fortnite executable, did you delete or move it?");
}
if (username == null) {
@@ -36,7 +34,7 @@ Future<void> startGame() async {
stdout.writeln("No username was specified, using $username by default. Use --username to specify one");
}
_gameProcess = await Process.start(gamePath, createRebootArgs(username!, "", host, ""))
_gameProcess = await Process.start(executable.path, createRebootArgs(username!, "", host, ""))
..exitCode.then((_) => _onClose())
..outLines.forEach((line) => _onGameOutput(line, dll, host, verbose));
}

View File

@@ -1,10 +1,9 @@
import 'dart:io';
import 'package:archive/archive_io.dart';
import 'package:reboot_launcher/src/util/server.dart';
import '../util/os.dart';
import 'package:http/http.dart' as http;
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/server.dart';
const String _baseDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968021373169674/cobalt.dll";
const String _consoleDownload = "https://cdn.discordapp.com/attachments/1095351875961901057/1110968095033524234/console.dll";

View File

@@ -1,9 +1,8 @@
import 'dart:io';
import 'package:process_run/shell.dart';
import '../model/server_type.dart';
import '../util/server.dart' as server;
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/server.dart' as server;
Future<bool> startServer(String? host, String? port, ServerType type) async {
stdout.writeln("Starting backend server...");

View File

@@ -1,6 +1,8 @@
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as path;
import 'package:reboot_launcher/src/util/patcher.dart';
class FortniteVersion {
String name;
@@ -22,8 +24,22 @@ class FortniteVersion {
}
}
File? get executable {
return findExecutable(location, "FortniteClient-Win64-Shipping.exe");
Future<File?> get executable async {
var result = findExecutable(location, "FortniteClient-Win64-Shipping-Reboot.exe");
if(result != null) {
return result;
}
var original = findExecutable(location, "FortniteClient-Win64-Shipping.exe");
if(original == null) {
return null;
}
await Future.wait([
compute(patchMatchmaking, original),
compute(patchHeadless, original)
]);
return original;
}
File? get launcher {

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert';
import 'package:fluent_ui/fluent_ui.dart';
@@ -9,7 +8,6 @@ import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/model/game_instance.dart';
import 'package:uuid/uuid.dart';
import '../../model/update_status.dart';
const String kDefaultPlayerName = "Player";

View File

@@ -4,9 +4,9 @@ import 'package:get_storage/get_storage.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/controller/update_controller.dart';
import '../../model/game_instance.dart';
import '../../model/update_status.dart';
import '../../util/reboot.dart';
import 'package:reboot_launcher/src/model/game_instance.dart';
import 'package:reboot_launcher/src/model/update_status.dart';
import 'package:reboot_launcher/src/util/reboot.dart';
const String kDefaultServerName = "Reboot Game Server";

View File

@@ -4,8 +4,8 @@ import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../model/server_type.dart';
import '../../util/server.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/server.dart';
class ServerController extends GetxController {
static const String _kDefaultServerHost = "127.0.0.1";

View File

@@ -1,3 +1,4 @@
import 'dart:ui';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
@@ -5,9 +6,8 @@ import 'package:get_storage/get_storage.dart';
import 'package:reboot_launcher/main.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'dart:ui';
import '../../util/reboot.dart';
import 'package:reboot_launcher/src/util/reboot.dart';
class SettingsController extends GetxController {
static const String _kDefaultIp = "127.0.0.1";
@@ -49,9 +49,9 @@ class SettingsController extends GetxController {
autoUpdate = RxBool(_storage.read("auto_update") ?? _kDefaultAutoUpdate);
autoUpdate.listen((value) => _storage.write("auto_update", value));
scrollingDistance = 0.0;
firstRun = RxBool(_storage.read("fr") ?? true);
firstRun.listen((value) => _storage.write("fr", value));
index = RxInt(firstRun() ? 0 : 1);
firstRun = RxBool(_storage.read("first_run") ?? true);
firstRun.listen((value) => _storage.write("first_run", value));
index = RxInt(firstRun() ? 3 : 0);
}
TextEditingController _createController(String key, String name) {

View File

@@ -6,9 +6,8 @@ import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_name_input.dart';
import '../../util/checks.dart';
import '../widget/shared/file_selector.dart';
import '../widget/shared/smart_check_box.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
import 'dialog.dart';
import 'dialog_button.dart';

View File

@@ -6,17 +6,16 @@ import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/util/error.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/build.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/util/build.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:universal_disk_space/universal_disk_space.dart';
import '../../util/checks.dart';
import '../controller/build_controller.dart';
import '../widget/home/build_selector.dart';
import '../widget/home/version_name_input.dart';
import '../widget/shared/file_selector.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
import 'package:reboot_launcher/src/ui/widget/home/build_selector.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_name_input.dart';
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
import 'dialog.dart';
import 'dialog_button.dart';

View File

@@ -1,7 +1,5 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:clipboard/clipboard.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/material.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'dialog_button.dart';
@@ -24,10 +22,6 @@ class GenericDialog extends AbstractDialog {
Widget build(BuildContext context) {
return Stack(
children: [
MoveWindow(
child: const SizedBox.expand(),
),
ContentDialog(
style: ContentDialogThemeData(
padding: padding ?? const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 5.0)

View File

@@ -1,7 +1,7 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import '../../../main.dart';
import 'package:reboot_launcher/main.dart';
import 'dialog.dart';
const String _unsupportedServerError = "The build you are currently using is not supported by Reboot. "

View File

@@ -1,14 +1,12 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:sync/semaphore.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../../main.dart';
import '../../util/server.dart';
import '../controller/server_controller.dart';
import 'package:reboot_launcher/main.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
import 'dialog.dart';
import 'dialog_button.dart';

View File

@@ -1,6 +1,6 @@
import 'package:fluent_ui/fluent_ui.dart';
import '../../../main.dart';
import 'package:reboot_launcher/main.dart';
void showMessage(String text){
showSnackbar(

View File

@@ -1,8 +1,4 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

View File

@@ -1,18 +1,16 @@
import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder;
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:reboot_launcher/main.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/page/launcher_page.dart';
import 'package:reboot_launcher/src/ui/page/server_page.dart';
import 'package:reboot_launcher/src/ui/page/settings_page.dart';
import 'package:window_manager/window_manager.dart';
import 'package:reboot_launcher/src/ui/widget/shared/profile_widget.dart';
import '../controller/game_controller.dart';
import '../controller/settings_controller.dart';
import '../widget/os/window_border.dart';
import '../widget/os/window_buttons.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/widget/os/window_border.dart';
import 'package:reboot_launcher/src/ui/widget/os/window_title_bar.dart';
import 'package:window_manager/window_manager.dart';
import 'hosting_page.dart';
import 'info_page.dart';
@@ -25,8 +23,8 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepAliveClientMixin {
static const double _kDefaultPadding = 12.0;
static const int _kPagesLength = 5;
static const int _kPagesLength = 6;
final SettingsController _settingsController = Get.find<SettingsController>();
final GlobalKey _searchKey = GlobalKey();
final FocusNode _searchFocusNode = FocusNode();
@@ -41,6 +39,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
@override
void initState() {
windowManager.show();
windowManager.addListener(this);
_searchController.addListener(_onSearch);
super.initState();
@@ -52,7 +51,8 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
return;
}
_searchItems.value = _allItems.whereType<PaneItem>()
_searchItems.value = _allItems
.whereType<PaneItem>()
.where((item) => (item.title as Text).data!.toLowerCase().contains(searchValue.toLowerCase()))
.toList()
.cast<NavigationPaneItem>();
@@ -84,73 +84,88 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
@override
void onWindowMoved() {
_settingsController.saveWindowOffset(appWindow.position);
windowManager.getPosition()
.then((value) => _settingsController.saveWindowOffset(value));
super.onWindowMoved();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Stack(
children: [
LayoutBuilder(
builder: (context, specs) => Obx(() => NavigationView(
paneBodyBuilder: (pane, body) => Padding(
padding: const EdgeInsets.all(_kDefaultPadding),
child: body
return Stack(children: [
LayoutBuilder(
builder: (context, specs) => Obx(() => NavigationPaneTheme(
data: NavigationPaneThemeData(
backgroundColor: FluentTheme.of(context).micaBackgroundColor.withOpacity(0.9),
),
appBar: NavigationAppBar(
title: _draggableArea,
actions: WindowTitleBar(focused: _focused()),
automaticallyImplyLeading: false,
leading: _backButton
),
pane: NavigationPane(
key: appKey,
selected: _selectedIndex,
onChanged: _onIndexChanged,
displayMode: specs.biggest.width <= 1536 ? PaneDisplayMode.compact : PaneDisplayMode.open,
items: _items,
footerItems: _footerItems,
autoSuggestBox: _autoSuggestBox,
autoSuggestBoxReplacement: const Icon(FluentIcons.search),
),
onOpenSearch: () => _searchFocusNode.requestFocus(),
transitionBuilder: (child, animation) => child
))
),
if(isWin11)
Obx(() => _focused.value ? const WindowBorder() : const SizedBox())
]
);
child: NavigationView(
paneBodyBuilder: (pane, body) => Padding(
padding: const EdgeInsets.all(_kDefaultPadding),
child: body
),
appBar: NavigationAppBar(
height: 32,
title: _draggableArea,
actions: WindowTitleBar(focused: _focused()),
automaticallyImplyLeading: false,
leading: _backButton
),
pane: NavigationPane(
key: appKey,
selected: _selectedIndex,
onChanged: _onIndexChanged,
menuButton: const SizedBox(),
displayMode: PaneDisplayMode.open,
items: _items,
header: ProfileWidget(),
footerItems: _footerItems,
autoSuggestBox: _autoSuggestBox,
autoSuggestBoxReplacement: const Icon(FluentIcons.search),
),
contentShape: const RoundedRectangleBorder(),
onOpenSearch: () => _searchFocusNode.requestFocus(),
transitionBuilder: (child, animation) => child),
)
)
),
if (isWin11)
Obx(() => _focused.value ? const WindowBorder() : const SizedBox())
]);
}
GestureDetector get _draggableArea => GestureDetector(
onDoubleTap: () async => await windowManager.isMaximized() ? await windowManager.restore() : await windowManager.maximize(),
onHorizontalDragStart: (event) => windowManager.startDragging(),
onVerticalDragStart: (event) => windowManager.startDragging()
);
Widget get _backButton => Obx(() {
for(var entry in _navigationStatus){
for (var entry in _navigationStatus) {
entry.value;
}
var onBack = _onBack();
return PaneItem(
enabled: onBack != null,
icon: const Icon(FluentIcons.back, size: 14.0),
body: const SizedBox.shrink(),
).build(
context,
false,
onBack,
displayMode: PaneDisplayMode.compact
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Button(
onPressed: onBack,
style: ButtonStyle(
backgroundColor: ButtonState.all(Colors.transparent),
border: ButtonState.all(BorderSide(color: Colors.transparent))
),
child: const Icon(FluentIcons.back, size: 13.0)
)
);
});
Function()? _onBack() {
var navigator = _navigators[_settingsController.index.value].currentState;
if(navigator == null || !navigator.mounted || !navigator.canPop()){
if (navigator == null || !navigator.mounted || !navigator.canPop()) {
return null;
}
var status = _navigationStatus[_settingsController.index.value];
if(status.value <= 0){
if (status.value <= 0) {
return null;
}
@@ -165,17 +180,15 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
_settingsController.index.value = index;
}
TextBox get _autoSuggestBox => TextBox(
key: _searchKey,
controller: _searchController,
placeholder: 'Search',
focusNode: _searchFocusNode
);
GestureDetector get _draggableArea => GestureDetector(
onDoubleTap: () => appWindow.maximizeOrRestore(),
onHorizontalDragStart: (event) => appWindow.startDragging(),
onVerticalDragStart: (event) => appWindow.startDragging()
Widget get _autoSuggestBox => Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: TextBox(
key: _searchKey,
controller: _searchController,
placeholder: 'Find a setting',
focusNode: _searchFocusNode,
autofocus: true
),
);
int? get _selectedIndex {
@@ -184,11 +197,12 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
return _settingsController.index();
}
if(_settingsController.index() >= _allItems.length){
if (_settingsController.index() >= _allItems.length) {
return null;
}
var indexOnScreen = searchItems.indexOf(_allItems[_settingsController.index()]);
var indexOnScreen =
searchItems.indexOf(_allItems[_settingsController.index()]);
if (indexOnScreen.isNegative) {
return null;
}
@@ -199,36 +213,75 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
List<NavigationPaneItem> get _allItems => [..._items, ..._footerItems];
List<NavigationPaneItem> get _footerItems => searchValue.isNotEmpty ? [] : [
PaneItem(
title: const Text("Settings"),
icon: const Icon(FluentIcons.settings),
body: SettingsPage()
)
PaneItem(
title: const Text("Downloads"),
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/download.png")
)
),
body: const SettingsPage()
),
PaneItem(
title: const Text("Settings"),
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/settings.png")
)
),
body: const SettingsPage()
)
];
List<NavigationPaneItem> get _items => _searchItems() ?? [
PaneItem(
title: const Text("Tutorial"),
icon: const Icon(FluentIcons.info),
body: InfoPage(_navigators[0], _navigationStatus[0])
),
PaneItem(
title: const Text("Play"),
icon: const Icon(FluentIcons.game),
body: LauncherPage(_navigators[1], _navigationStatus[1])
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/play.png")
)
),
body: LauncherPage(_navigators[0], _navigationStatus[0])
),
PaneItem(
title: const Text("Host"),
icon: const Icon(FluentIcons.server_processes),
body: HostingPage(_navigators[2], _navigationStatus[2])
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/host.png")
)
),
body: HostingPage(_navigators[1], _navigationStatus[1])
),
PaneItem(
title: const Text("Backend"),
icon: const Icon(FluentIcons.user_window),
body: ServerPage(_navigators[3], _navigationStatus[3])
title: const Text("Authenticator"),
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/cloud.png")
)
),
body: ServerPage(_navigators[2], _navigationStatus[2])
),
PaneItem(
title: const Text("Tutorial"),
icon: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox.square(
dimension: 24,
child: Image.asset("assets/images/info.png")
)
),
body: InfoPage(_navigators[3], _navigationStatus[3])
)
];
String get searchValue => _searchController.text;

View File

@@ -3,10 +3,10 @@ import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
import '../../model/update_status.dart';
import 'package:reboot_launcher/src/model/update_status.dart';
import 'browse_page.dart';
class HostingPage extends StatefulWidget {
@@ -38,7 +38,7 @@ class _HostingPageState extends State<HostingPage> with AutomaticKeepAliveClient
mainAxisAlignment: MainAxisAlignment.center,
children: [
ProgressRing(),
SizedBox(height: 16.0),
SizedBox(height: 8.0),
Text("Updating Reboot DLL...")
],
),
@@ -97,7 +97,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
child: _hostingController.updateStatus.value == UpdateStatus.error ? _updateError : _rebootGuiInfo,
)),
const SizedBox(
height: 16.0
height: 8.0
),
SettingTile(
title: "Game Server",
@@ -135,7 +135,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
],
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Version",
@@ -163,7 +163,7 @@ class _HostPageState extends State<_HostPage> with AutomaticKeepAliveClientMixin
]
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Browse available servers",

View File

@@ -1,20 +1,16 @@
import 'dart:async';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/util/server.dart';
import '../../util/os.dart';
import '../controller/game_controller.dart';
import '../controller/settings_controller.dart';
import '../dialog/dialog.dart';
import '../dialog/dialog_button.dart';
import '../widget/home/version_selector.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
class InfoPage extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
@@ -219,7 +215,7 @@ class _PlayPageState extends State<_PlayPage> {
],
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: '2. Download Fortnite',
@@ -255,7 +251,7 @@ class _PlayPageState extends State<_PlayPage> {
],
),
const SizedBox(
height: 16.0,
height: 8.0,
),
StreamBuilder(
stream: _remoteGameServerStream.stream,

View File

@@ -1,20 +1,8 @@
import 'dart:async';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/material.dart' show Icons;
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:reboot_launcher/src/ui/page/browse_page.dart';
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/ui/page/play_page.dart';
import '../../util/checks.dart';
import '../../util/os.dart';
class LauncherPage extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
@@ -48,185 +36,11 @@ class _LauncherPageState extends State<LauncherPage> with AutomaticKeepAliveClie
Widget _createScreen(String? name) {
switch(name){
case "home":
return _GamePage(widget.navigatorKey, widget.nestedNavigation);
return PlayPage(widget.navigatorKey, widget.nestedNavigation);
case "browse":
return const BrowsePage();
default:
throw Exception("Unknown page: $name");
}
}
}
class _GamePage extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
final RxInt nestedNavigation;
const _GamePage(this.navigatorKey, this.nestedNavigation, {Key? key}) : super(key: key);
@override
State<_GamePage> createState() => _GamePageState();
}
class _GamePageState extends State<_GamePage> {
final GameController _gameController = Get.find<GameController>();
final SettingsController _settingsController = Get.find<SettingsController>();
late final RxBool _showPasswordTrailing = RxBool(_gameController.password.text.isNotEmpty);
final StreamController _matchmakingStream = StreamController();
@override
void initState() {
_gameController.password.addListener(() => _matchmakingStream.add(null));
_settingsController.matchmakingIp.addListener(() => _matchmakingStream.add(null));
super.initState();
}
@override
Widget build(BuildContext context) => Column(
children: [
Expanded(
child: ListView(
children: [
SettingTile(
title: "Credentials",
subtitle: "Your in-game login credentials",
expandedContentSpacing: 0,
expandedContent: [
SettingTile(
title: "Username",
subtitle: "The username that other players will see when you are in game",
isChild: true,
content: TextFormBox(
placeholder: "Username",
controller: _gameController.username,
autovalidateMode: AutovalidateMode.always
),
),
SettingTile(
title: "Password",
subtitle: "The password of your account, only used if the backend requires it",
isChild: true,
content: Obx(() => TextFormBox(
placeholder: "Password",
controller: _gameController.password,
autovalidateMode: AutovalidateMode.always,
obscureText: !_gameController.showPassword.value,
enableSuggestions: false,
autocorrect: false,
onChanged: (text) => _showPasswordTrailing.value = text.isNotEmpty,
suffix: Button(
onPressed: () => _gameController.showPassword.value = !_gameController.showPassword.value,
style: ButtonStyle(
shape: ButtonState.all(const CircleBorder()),
backgroundColor: ButtonState.all(Colors.transparent)
),
child: Icon(
_gameController.showPassword.value ? Icons.visibility_off : Icons.visibility,
color: _showPasswordTrailing.value ? null : Colors.transparent
),
)
))
)
],
),
const SizedBox(
height: 16.0,
),
StreamBuilder(
stream: _matchmakingStream.stream,
builder: (context, value) =>
SettingTile(
title: "Matchmaking host",
subtitle: "Enter the IP address of the game server hosting the match",
content: TextFormBox(
placeholder: "IP:PORT",
controller: _settingsController.matchmakingIp,
validator: checkMatchmaking,
autovalidateMode: AutovalidateMode.always
),
expandedContent: [
SettingTile(
title: "Automatically start game server",
subtitle: "This option is available when the matchmaker is set to localhost",
contentWidth: null,
content: !isLocalHost(_settingsController.matchmakingIp.text) || _gameController.password.text.isNotEmpty ? _disabledAutoGameServerSwitch : _autoGameServerSwitch,
isChild: true
),
SettingTile(
title: "Browse available servers",
subtitle: "Discover new game servers that fit your play-style",
content: Button(
onPressed: () {
widget.navigatorKey.currentState?.pushNamed('browse');
widget.nestedNavigation.value += 1;
},
child: const Text("Browse")
),
isChild: true
)
]
)
),
const SizedBox(
height: 16.0,
),
SettingTile(
title: "Version",
subtitle: "Select the version of Fortnite you want to play",
content: const VersionSelector(),
expandedContent: [
SettingTile(
title: "Add a version from this PC's local storage",
subtitle: "Versions coming from your local disk are not guaranteed to work",
content: Button(
onPressed: () => VersionSelector.openAddDialog(context),
child: const Text("Add build"),
),
isChild: true
),
SettingTile(
title: "Download any version from the cloud",
subtitle: "A curated list of supported versions by Project Reboot",
content: Button(
onPressed: () => VersionSelector.openDownloadDialog(context),
child: const Text("Download"),
),
isChild: true
)
]
)
],
),
),
const SizedBox(
height: 8.0,
),
const LaunchButton(
host: false
)
],
);
Widget get _disabledAutoGameServerSwitch => Container(
foregroundDecoration: const BoxDecoration(
color: Colors.grey,
backgroundBlendMode: BlendMode.saturation,
),
child: _autoGameServerSwitch,
);
Widget get _autoGameServerSwitch => Obx(() => ToggleSwitch(
checked: _gameController.autoStartGameServer() && isLocalHost(_settingsController.matchmakingIp.text) && _gameController.password.text.isEmpty,
onChanged: (value) {
if(!isLocalHost(_settingsController.matchmakingIp.text)){
showMessage("This option isn't available when the matchmaker isn't set to 127.0.0.1");
return;
}
if(_gameController.password.text.isNotEmpty){
showMessage("This option isn't available when the password isn't empty(LawinV2)");
return;
}
_gameController.autoStartGameServer.value = value;
}
));
}

View File

@@ -0,0 +1,154 @@
import 'dart:async';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/src/rx_types/rx_types.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:reboot_launcher/src/ui/widget/home/launch_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/ui/widget/home/version_selector.dart';
class PlayPage extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
final RxInt nestedNavigation;
const PlayPage(this.navigatorKey, this.nestedNavigation, {Key? key}) : super(key: key);
@override
State<PlayPage> createState() => _PlayPageState();
}
class _PlayPageState extends State<PlayPage> {
final GameController _gameController = Get.find<GameController>();
final SettingsController _settingsController = Get.find<SettingsController>();
final StreamController _matchmakingStream = StreamController();
@override
void initState() {
_gameController.password.addListener(() => _matchmakingStream.add(null));
_settingsController.matchmakingIp.addListener(() =>
_matchmakingStream.add(null));
super.initState();
}
@override
Widget build(BuildContext context) => Column(
children: [
Expanded(
child: ListView(
children: [
SettingTile(
title: "Version",
subtitle: "Select the version of Fortnite you want to play",
content: const VersionSelector(),
expandedContent: [
SettingTile(
title: "Add a version from this PC's local storage",
subtitle: "Versions coming from your local disk are not guaranteed to work",
content: Button(
onPressed: () =>
VersionSelector.openAddDialog(context),
child: const Text("Add build"),
),
isChild: true
),
SettingTile(
title: "Download any version from the cloud",
subtitle: "A curated list of supported versions by Project Reboot",
content: Button(
onPressed: () =>
VersionSelector.openDownloadDialog(context),
child: const Text("Download"),
),
isChild: true
)
]
),
const SizedBox(
height: 8.0,
),
StreamBuilder(
stream: _matchmakingStream.stream,
builder: (context, value) =>
SettingTile(
title: "Matchmaking host",
subtitle: "Enter the IP address of the game server hosting the match",
content: TextFormBox(
placeholder: "IP:PORT",
controller: _settingsController.matchmakingIp,
validator: checkMatchmaking,
autovalidateMode: AutovalidateMode.always
),
expandedContent: [
SettingTile(
title: "Automatically start game server",
subtitle: "This option is available when the matchmaker is set to localhost",
contentWidth: null,
content: !isLocalHost(
_settingsController.matchmakingIp.text) ||
_gameController.password.text.isNotEmpty
? _disabledAutoGameServerSwitch
: _autoGameServerSwitch,
isChild: true
),
SettingTile(
title: "Browse available servers",
subtitle: "Discover new game servers that fit your play-style",
content: Button(
onPressed: () {
widget.navigatorKey.currentState
?.pushNamed('browse');
widget.nestedNavigation.value += 1;
},
child: const Text("Browse")
),
isChild: true
)
]
)
),
],
),
),
const SizedBox(
height: 8.0,
),
const LaunchButton(
host: false
)
],
);
Widget get _disabledAutoGameServerSwitch => Container(
foregroundDecoration: const BoxDecoration(
color: Colors.grey,
backgroundBlendMode: BlendMode.saturation,
),
child: _autoGameServerSwitch,
);
Widget get _autoGameServerSwitch => Obx(() => ToggleSwitch(
checked: _gameController.autoStartGameServer() &&
isLocalHost(_settingsController.matchmakingIp.text) &&
_gameController.password.text.isEmpty,
onChanged: (value) {
if (!isLocalHost(_settingsController.matchmakingIp.text)) {
showMessage(
"This option isn't available when the matchmaker isn't set to 127.0.0.1");
return;
}
if (_gameController.password.text.isNotEmpty) {
showMessage(
"This option isn't available when the password isn't empty(LawinV2)");
return;
}
_gameController.autoStartGameServer.value = value;
}
));
}

View File

@@ -1,15 +1,15 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
import 'package:reboot_launcher/src/ui/widget/server/server_type_selector.dart';
import 'package:reboot_launcher/src/ui/widget/server/server_button.dart';
import 'package:reboot_launcher/src/ui/widget/server/server_type_selector.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'package:url_launcher/url_launcher.dart';
import '../dialog/dialog.dart';
import '../dialog/dialog_button.dart';
import '../widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
class ServerPage extends StatefulWidget {
final GlobalKey<NavigatorState> navigatorKey;
@@ -43,7 +43,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
),
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Host",
@@ -55,7 +55,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Port",
@@ -67,7 +67,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Type",
@@ -75,7 +75,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
content: ServerTypeSelector()
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Detached",
@@ -87,7 +87,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
))
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Server files",
@@ -98,7 +98,7 @@ class _ServerPageState extends State<ServerPage> with AutomaticKeepAliveClientMi
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Reset Backend",

View File

@@ -1,5 +1,4 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
@@ -7,12 +6,10 @@ import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../util/checks.dart';
import '../../util/os.dart';
import '../../util/selector.dart';
import '../dialog/dialog.dart';
import '../widget/home/setting_tile.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
import 'package:reboot_launcher/src/ui/widget/home/setting_tile.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key}) : super(key: key);
@@ -56,7 +53,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
],
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Automatic updates",
@@ -82,7 +79,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
]
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Custom launch arguments",
@@ -93,7 +90,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Create a bug report",
@@ -104,7 +101,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Reset settings",
@@ -134,7 +131,7 @@ class _SettingsPageState extends State<SettingsPage> with AutomaticKeepAliveClie
)
),
const SizedBox(
height: 16.0,
height: 8.0,
),
SettingTile(
title: "Version status",

View File

@@ -1,7 +1,7 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
import 'package:reboot_launcher/src/model/fortnite_build.dart';
import 'package:reboot_launcher/src/ui/controller/build_controller.dart';
class BuildSelector extends StatefulWidget {
final Function() onSelected;

View File

@@ -1,32 +1,29 @@
import 'dart:async';
import 'dart:collection';
import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as path;
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/../main.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/model/game_instance.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/controller/hosting_controller.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
import 'package:reboot_launcher/src/ui/dialog/game_dialogs.dart';
import 'package:reboot_launcher/src/ui/dialog/server_dialogs.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/injector.dart';
import 'package:reboot_launcher/src/util/patcher.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'package:path/path.dart' as path;
import 'package:reboot_launcher/src/../main.dart';
import 'package:reboot_launcher/src/ui/controller/settings_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:reboot_launcher/src/model/game_instance.dart';
import 'package:reboot_launcher/src/util/injector.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:reboot_launcher/src/util/server.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import '../../../util/process.dart';
import 'package:reboot_launcher/src/util/process.dart';
class LaunchButton extends StatefulWidget {
final bool host;
@@ -124,7 +121,8 @@ class _LaunchButtonState extends State<LaunchButton> {
try {
var version = _gameController.selectedVersion!;
if(version.executable?.path == null){
var executable = await version.executable;
if(executable == null){
showMissingBuildError(version);
_onStop(widget.host);
return;
@@ -136,9 +134,6 @@ class _LaunchButtonState extends State<LaunchButton> {
return;
}
await compute(patchHeadless, version.executable!);
// Is this needed? await compute(patchMatchmaking, version.executable!);
var automaticallyStartedServer = await _startMatchMakingServer();
await _startGameProcesses(version, widget.host, automaticallyStartedServer);
@@ -156,7 +151,14 @@ class _LaunchButtonState extends State<LaunchButton> {
_setStarted(host, true);
var launcherProcess = await _createLauncherProcess(version);
var eacProcess = await _createEacProcess(version);
var gameProcess = await _createGameProcess(version.executable!.path, host);
var executable = await version.executable;
if(executable == null){
showMissingBuildError(version);
_onStop(widget.host);
return;
}
var gameProcess = await _createGameProcess(executable.path, host);
var watchDogProcess = _createWatchdogProcess(gameProcess, launcherProcess, eacProcess);
var instance = GameInstance(gameProcess, launcherProcess, eacProcess, watchDogProcess, hasChildServer);
if(host){

View File

@@ -1,5 +1,4 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/ui/widget/shared/fluent_card.dart';
class SettingTile extends StatefulWidget {
static const double kDefaultContentWidth = 200.0;
@@ -42,19 +41,17 @@ class _SettingTileState extends State<SettingTile> {
return _contentCard;
}
return Mica(
elevation: 1,
child: Expander(
initiallyExpanded: true,
contentBackgroundColor: FluentTheme.of(context).menuColor,
headerShape: (open) => const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(4.0)),
),
header: _header,
headerHeight: widget.expandedContentHeaderHeight,
trailing: _trailing,
content: _content
),
return Expander(
initiallyExpanded: true,
headerShape: (open) => const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(4.0)),
),
header: SizedBox(
height: widget.expandedContentHeaderHeight,
child: _header
),
trailing: _trailing,
content: _content
);
}
@@ -90,8 +87,9 @@ class _SettingTileState extends State<SettingTile> {
);
}
return FluentCard(
child: _contentCardBody,
return Card(
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
child: _contentCardBody
);
}

View File

@@ -4,18 +4,18 @@ import 'dart:io';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/gestures.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/add_local_version.dart';
import 'package:reboot_launcher/src/ui/dialog/add_server_version.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog_button.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
import 'package:reboot_launcher/src/ui/dialog/add_local_version.dart';
import 'package:reboot_launcher/src/ui/widget/shared/smart_check_box.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:reboot_launcher/src/ui/dialog/add_server_version.dart';
import 'package:reboot_launcher/src/util/checks.dart';
import '../shared/file_selector.dart';
import 'package:reboot_launcher/src/ui/widget/shared/file_selector.dart';
class VersionSelector extends StatefulWidget {
const VersionSelector({Key? key}) : super(key: key);

View File

@@ -0,0 +1,126 @@
import 'dart:math';
import 'package:flutter/widgets.dart';
// Switched to CustomPaint icons by https://github.com/esDotDev
/// Close
class CloseIcon extends StatelessWidget {
final Color color;
const CloseIcon({Key? key, required this.color}) : super(key: key);
@override
Widget build(BuildContext context) => Align(
alignment: Alignment.topLeft,
child: Stack(children: [
// Use rotated containers instead of a painter because it renders slightly crisper than a painter for some reason.
Transform.rotate(
angle: pi * .25,
child:
Center(child: Container(width: 14, height: 1, color: color))),
Transform.rotate(
angle: pi * -.25,
child:
Center(child: Container(width: 14, height: 1, color: color))),
]),
);
}
/// Maximize
class MaximizeIcon extends StatelessWidget {
final Color color;
const MaximizeIcon({Key? key, required this.color}) : super(key: key);
@override
Widget build(BuildContext context) => _AlignedPaint(_MaximizePainter(color));
}
class _MaximizePainter extends _IconPainter {
_MaximizePainter(Color color) : super(color);
@override
void paint(Canvas canvas, Size size) {
Paint p = getPaint(color);
canvas.drawRect(Rect.fromLTRB(0, 0, size.width - 1, size.height - 1), p);
}
}
/// Restore
class RestoreIcon extends StatelessWidget {
final Color color;
const RestoreIcon({
Key? key,
required this.color,
}) : super(key: key);
@override
Widget build(BuildContext context) => _AlignedPaint(_RestorePainter(color));
}
class _RestorePainter extends _IconPainter {
_RestorePainter(Color color) : super(color);
@override
void paint(Canvas canvas, Size size) {
Paint p = getPaint(color);
canvas.drawRect(Rect.fromLTRB(0, 2, size.width - 2, size.height), p);
canvas.drawLine(const Offset(2, 2), const Offset(2, 0), p);
canvas.drawLine(const Offset(2, 0), Offset(size.width, 0), p);
canvas.drawLine(
Offset(size.width, 0), Offset(size.width, size.height - 2), p);
canvas.drawLine(Offset(size.width, size.height - 2),
Offset(size.width - 2, size.height - 2), p);
}
}
/// Minimize
class MinimizeIcon extends StatelessWidget {
final Color color;
const MinimizeIcon({Key? key, required this.color}) : super(key: key);
@override
Widget build(BuildContext context) => _AlignedPaint(_MinimizePainter(color));
}
class _MinimizePainter extends _IconPainter {
_MinimizePainter(Color color) : super(color);
@override
void paint(Canvas canvas, Size size) {
Paint p = getPaint(color);
canvas.drawLine(
Offset(0, size.height / 2), Offset(size.width, size.height / 2), p);
}
}
/// Helpers
abstract class _IconPainter extends CustomPainter {
_IconPainter(this.color);
final Color color;
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
class _AlignedPaint extends StatelessWidget {
const _AlignedPaint(this.painter, {Key? key}) : super(key: key);
final CustomPainter painter;
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: CustomPaint(size: const Size(10, 10), painter: painter));
}
}
Paint getPaint(Color color, [bool isAntiAlias = false]) => Paint()
..color = color
..style = PaintingStyle.stroke
..isAntiAlias = isAntiAlias
..strokeWidth = 1;

View File

@@ -0,0 +1,74 @@
import 'package:flutter/widgets.dart';
typedef MouseStateBuilderCB = Widget Function(
BuildContext context, MouseState mouseState);
class MouseState {
bool isMouseOver = false;
bool isMouseDown = false;
MouseState();
@override
String toString() {
return "isMouseDown: $isMouseDown - isMouseOver: $isMouseOver";
}
}
class MouseStateBuilder extends StatefulWidget {
final MouseStateBuilderCB builder;
final VoidCallback? onPressed;
const MouseStateBuilder({Key? key, required this.builder, this.onPressed})
: super(key: key);
@override
State<MouseStateBuilder> createState() => _MouseStateBuilderState();
}
class _MouseStateBuilderState extends State<MouseStateBuilder> {
late MouseState _mouseState;
_MouseStateBuilderState() {
_mouseState = MouseState();
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (event) {
setState(() {
_mouseState.isMouseOver = true;
});
},
onExit: (event) {
setState(() {
_mouseState.isMouseOver = false;
});
},
child: GestureDetector(
onTapDown: (_) {
setState(() {
_mouseState.isMouseDown = true;
});
},
onTapCancel: () {
setState(() {
_mouseState.isMouseDown = false;
});
},
onTap: () {
setState(() {
_mouseState.isMouseDown = false;
_mouseState.isMouseOver = false;
});
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.onPressed != null) {
widget.onPressed!();
}
});
},
onTapUp: (_) {},
child: widget.builder(context, _mouseState)));
}
}

View File

@@ -1,4 +1,3 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/material.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:system_theme/system_theme.dart';
@@ -10,18 +9,19 @@ class WindowBorder extends StatelessWidget {
Widget build(BuildContext context) {
return IgnorePointer(
child: Padding(
padding: EdgeInsets.only(
top: 1 / appWindow.scaleFactor
padding: const EdgeInsets.only(
top: 1
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: SystemTheme.accentColor.accent,
width: appBarSize.toDouble()
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: SystemTheme.accentColor.accent,
width: appBarSize.toDouble()
)
)
)
),
));
)
);
}
}

View File

@@ -0,0 +1,193 @@
import 'package:flutter/material.dart';
import 'package:window_manager/window_manager.dart';
import 'icons.dart';
import 'mouse_state_builder.dart';
typedef WindowButtonIconBuilder = Widget Function(
WindowButtonContext buttonContext);
typedef WindowButtonBuilder = Widget Function(
WindowButtonContext buttonContext, Widget icon);
class WindowButtonContext {
BuildContext context;
MouseState mouseState;
Color? backgroundColor;
Color iconColor;
WindowButtonContext(
{required this.context,
required this.mouseState,
this.backgroundColor,
required this.iconColor});
}
class WindowButtonColors {
late Color normal;
late Color mouseOver;
late Color mouseDown;
late Color iconNormal;
late Color iconMouseOver;
late Color iconMouseDown;
WindowButtonColors(
{Color? normal,
Color? mouseOver,
Color? mouseDown,
Color? iconNormal,
Color? iconMouseOver,
Color? iconMouseDown}) {
this.normal = normal ?? _defaultButtonColors.normal;
this.mouseOver = mouseOver ?? _defaultButtonColors.mouseOver;
this.mouseDown = mouseDown ?? _defaultButtonColors.mouseDown;
this.iconNormal = iconNormal ?? _defaultButtonColors.iconNormal;
this.iconMouseOver = iconMouseOver ?? _defaultButtonColors.iconMouseOver;
this.iconMouseDown = iconMouseDown ?? _defaultButtonColors.iconMouseDown;
}
}
final _defaultButtonColors = WindowButtonColors(
normal: Colors.transparent,
iconNormal: const Color(0xFF805306),
mouseOver: const Color(0xFF404040),
mouseDown: const Color(0xFF202020),
iconMouseOver: const Color(0xFFFFFFFF),
iconMouseDown: const Color(0xFFF0F0F0));
class WindowButton extends StatelessWidget {
final WindowButtonBuilder? builder;
final WindowButtonIconBuilder? iconBuilder;
late final WindowButtonColors colors;
final bool animate;
final EdgeInsets? padding;
final VoidCallback? onPressed;
WindowButton(
{Key? key,
WindowButtonColors? colors,
this.builder,
@required this.iconBuilder,
this.padding,
this.onPressed,
this.animate = false})
: super(key: key) {
this.colors = colors ?? _defaultButtonColors;
}
Color getBackgroundColor(MouseState mouseState) {
if (mouseState.isMouseDown) return colors.mouseDown;
if (mouseState.isMouseOver) return colors.mouseOver;
return colors.normal;
}
Color getIconColor(MouseState mouseState) {
if (mouseState.isMouseDown) return colors.iconMouseDown;
if (mouseState.isMouseOver) return colors.iconMouseOver;
return colors.iconNormal;
}
@override
Widget build(BuildContext context) {
return MouseStateBuilder(
builder: (context, mouseState) {
WindowButtonContext buttonContext = WindowButtonContext(
mouseState: mouseState,
context: context,
backgroundColor: getBackgroundColor(mouseState),
iconColor: getIconColor(mouseState));
var icon =
(iconBuilder != null) ? iconBuilder!(buttonContext) : Container();
var fadeOutColor =
getBackgroundColor(MouseState()..isMouseOver = true)
.withOpacity(0);
var padding = this.padding ?? EdgeInsets.zero;
var animationMs = mouseState.isMouseOver
? (animate ? 100 : 0)
: (animate ? 200 : 0);
Widget iconWithPadding = Padding(padding: padding, child: icon);
iconWithPadding = AnimatedContainer(
curve: Curves.easeOut,
duration: Duration(milliseconds: animationMs),
color: buttonContext.backgroundColor ?? fadeOutColor,
child: iconWithPadding);
var button = (builder != null)
? builder!(buttonContext, icon)
: iconWithPadding;
return SizedBox.square(dimension: 45, child: button);
},
onPressed: onPressed);
}
}
class MinimizeWindowButton extends WindowButton {
MinimizeWindowButton(
{Key? key,
WindowButtonColors? colors,
VoidCallback? onPressed,
bool? animate})
: super(
key: key,
colors: colors,
animate: animate ?? false,
iconBuilder: (buttonContext) =>
MinimizeIcon(color: buttonContext.iconColor),
onPressed: onPressed ?? () => windowManager.minimize());
}
class MaximizeWindowButton extends WindowButton {
MaximizeWindowButton(
{Key? key,
WindowButtonColors? colors,
VoidCallback? onPressed,
bool? animate})
: super(
key: key,
colors: colors,
animate: animate ?? false,
iconBuilder: (buttonContext) =>
MaximizeIcon(color: buttonContext.iconColor),
onPressed: onPressed ??
() async => await windowManager.isMaximized()
? await windowManager.restore()
: await windowManager.maximize());
}
class RestoreWindowButton extends WindowButton {
RestoreWindowButton(
{Key? key,
WindowButtonColors? colors,
VoidCallback? onPressed,
bool? animate})
: super(
key: key,
colors: colors,
animate: animate ?? false,
iconBuilder: (buttonContext) =>
RestoreIcon(color: buttonContext.iconColor),
onPressed: onPressed ??
() async => await windowManager.isMaximized()
? await windowManager.restore()
: await windowManager.maximize());
}
final _defaultCloseButtonColors = WindowButtonColors(
mouseOver: const Color(0xFFD32F2F),
mouseDown: const Color(0xFFB71C1C),
iconNormal: const Color(0xFF805306),
iconMouseOver: const Color(0xFFFFFFFF));
class CloseWindowButton extends WindowButton {
CloseWindowButton(
{Key? key,
WindowButtonColors? colors,
VoidCallback? onPressed,
bool? animate})
: super(
key: key,
colors: colors ?? _defaultCloseButtonColors,
animate: animate ?? false,
iconBuilder: (buttonContext) =>
CloseIcon(color: buttonContext.iconColor),
onPressed: onPressed ?? () => windowManager.close());
}

View File

@@ -1,5 +1,5 @@
import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:reboot_launcher/src/ui/widget/os/window_button.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:system_theme/system_theme.dart';

View File

@@ -1,8 +1,8 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
import 'package:reboot_launcher/src/ui/dialog/server_dialogs.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
class ServerButton extends StatefulWidget {
const ServerButton({Key? key}) : super(key: key);

View File

@@ -1,7 +1,7 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/ui/controller/server_controller.dart';
class ServerTypeSelector extends StatelessWidget {
final ServerController _serverController = Get.find<ServerController>();

View File

@@ -1,11 +1,6 @@
import 'dart:async';
import 'package:file_picker/file_picker.dart';
import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:reboot_launcher/src/ui/dialog/snackbar.dart';
import 'package:reboot_launcher/src/util/selector.dart';
class FileSelector extends StatefulWidget {

View File

@@ -1,16 +0,0 @@
import 'package:fluent_ui/fluent_ui.dart';
class FluentCard extends StatelessWidget {
final Widget child;
const FluentCard({Key? key, required this.child}) : super(key: key);
@override
Widget build(BuildContext context) => Mica(
elevation: 1,
child: Card(
backgroundColor: FluentTheme.of(context).menuColor,
borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)),
child: child
)
);
}

View File

@@ -0,0 +1,56 @@
import 'package:fluent_ui/fluent_ui.dart';
import 'package:get/get.dart';
import '../../controller/game_controller.dart';
class ProfileWidget extends StatelessWidget {
final GameController _gameController = Get.find<GameController>();
ProfileWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
vertical: 12.0
),
child: GestureDetector(
child: Row(
children: [
Container(
width: 64,
height: 64,
decoration: const BoxDecoration(
shape: BoxShape.circle
),
child: Image.asset("assets/images/user.png")
),
const SizedBox(
width: 12.0,
),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Auties00",
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w600
),
),
Text(
"alautiero@gmail.com",
textAlign: TextAlign.start,
style: TextStyle(
fontWeight: FontWeight.w100
),
)
],
)
],
),
),
);
}
}

View File

@@ -2,13 +2,12 @@ import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:archive/archive_io.dart';
import 'package:html/parser.dart' show parse;
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as path;
import 'package:reboot_launcher/src/model/fortnite_build.dart';
import 'package:reboot_launcher/src/util/time.dart';
import 'package:reboot_launcher/src/util/version.dart' as parser;
import 'package:path/path.dart' as path;
import 'os.dart';

View File

@@ -1,9 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'package:reboot_launcher/src/util/server.dart';
import '../model/fortnite_version.dart';
import 'package:reboot_launcher/src/model/fortnite_version.dart';
String? checkVersion(String? text, List<FortniteVersion> versions) {
if (text == null || text.isEmpty) {

View File

@@ -1,7 +1,7 @@
import 'package:fluent_ui/fluent_ui.dart';
import '../../../main.dart';
import '../ui/dialog/dialog.dart';
import 'package:reboot_launcher/main.dart';
import 'package:reboot_launcher/src/ui/dialog/dialog.dart';
String? lastError;

View File

@@ -2,8 +2,8 @@
import 'dart:ffi';
import 'package:win32/win32.dart';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';
final _kernel32 = DynamicLibrary.open('kernel32.dll');
final _CreateRemoteThread = _kernel32.lookupFunction<

View File

@@ -1,8 +1,8 @@
import 'dart:ffi';
import 'dart:io';
import 'package:win32/win32.dart';
import 'package:ffi/ffi.dart';
import 'dart:ffi';
import 'package:win32/win32.dart';
const int appBarSize = 2;

View File

@@ -1,6 +1,5 @@
import 'dart:ffi';
import 'package:win32/src/kernel32.dart';
import 'package:win32/win32.dart';
final _ntdll = DynamicLibrary.open('ntdll.dll');

View File

@@ -2,15 +2,14 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:ini/ini.dart';
import 'package:process_run/shell.dart';
import 'package:reboot_launcher/src/model/server_type.dart';
import 'package:reboot_launcher/src/ui/controller/game_controller.dart';
import 'package:reboot_launcher/src/util/os.dart';
import 'package:shelf_proxy/shelf_proxy.dart';
import 'package:shelf/shelf_io.dart';
import 'package:http/http.dart' as http;
import 'package:shelf_proxy/shelf_proxy.dart';
final serverLogFile = File("${logsDirectory.path}\\server.log");
final serverDirectory = Directory("${assetsDirectory.path}\\lawin");