mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
10.0.7
This commit is contained in:
BIN
gui/dependencies/dlls/RebootFallback.zip
Normal file
BIN
gui/dependencies/dlls/RebootFallback.zip
Normal file
Binary file not shown.
BIN
gui/dependencies/dlls/RebootS20Fallback.zip
Normal file
BIN
gui/dependencies/dlls/RebootS20Fallback.zip
Normal file
Binary file not shown.
@@ -128,7 +128,7 @@
|
||||
"importVersionDescription": "Import a new version of Fortnite into the launcher",
|
||||
"addLocalBuildName": "Add a version from this PC's local storage",
|
||||
"addLocalBuildDescription": "Versions coming from your local disk are not guaranteed to work",
|
||||
"addVersion": "Add version",
|
||||
"addVersion": "New version",
|
||||
"downloadBuildName": "Download any version from the cloud",
|
||||
"downloadBuildDescription": "Download any Fortnite build easily from the cloud",
|
||||
"downloadBuildContent": "Download build",
|
||||
|
||||
@@ -35,10 +35,8 @@ class BackendController extends GetxController {
|
||||
late final RxBool started;
|
||||
late final RxBool detached;
|
||||
late final List<InfoBarEntry> _infoBars;
|
||||
StreamSubscription? worker;
|
||||
int? embeddedProcessPid;
|
||||
HttpServer? localServer;
|
||||
HttpServer? remoteServer;
|
||||
StreamSubscription? _worker;
|
||||
ServerImplementation? _implementation;
|
||||
|
||||
BackendController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
@@ -48,11 +46,6 @@ class BackendController extends GetxController {
|
||||
host.text = _readHost();
|
||||
port.text = _readPort();
|
||||
_storage?.write("type", value.index);
|
||||
if (!started.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
stop();
|
||||
});
|
||||
host = TextEditingController(text: _readHost());
|
||||
host.addListener(() =>
|
||||
@@ -148,18 +141,27 @@ class BackendController extends GetxController {
|
||||
detached.value = false;
|
||||
}
|
||||
|
||||
Future<bool> toggleInteractive() async {
|
||||
Future<bool> toggle() {
|
||||
if(started.value) {
|
||||
return stop(interactive: true);
|
||||
}else {
|
||||
return start(interactive: true);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> start({required bool interactive}) async {
|
||||
if(started.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_cancel();
|
||||
final stream = started.value ? stop() : start(
|
||||
onExit: () {
|
||||
_cancel();
|
||||
_showRebootInfoBar(
|
||||
translations.backendProcessError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
},
|
||||
final stream = startBackend(
|
||||
type: type.value,
|
||||
host: host.text,
|
||||
port: port.text,
|
||||
detached: detached.value,
|
||||
onError: (errorMessage) {
|
||||
_cancel();
|
||||
stop(interactive: false);
|
||||
_showRebootInfoBar(
|
||||
translations.backendErrorMessage,
|
||||
severity: InfoBarSeverity.error,
|
||||
@@ -173,265 +175,203 @@ class BackendController extends GetxController {
|
||||
);
|
||||
final completer = Completer<bool>();
|
||||
InfoBarEntry? entry;
|
||||
worker = stream.listen((event) {
|
||||
_worker = stream.listen((event) {
|
||||
entry?.close();
|
||||
entry = _handeEvent(event);
|
||||
entry = _handeEvent(event, interactive);
|
||||
if(event.type.isError) {
|
||||
completer.complete(false);
|
||||
}else if(event.type.isSuccess) {
|
||||
completer.complete(true);
|
||||
}
|
||||
});
|
||||
|
||||
return await completer.future;
|
||||
}
|
||||
|
||||
Stream<ServerResult> start({required void Function() onExit, required void Function(String) onError}) async* {
|
||||
try {
|
||||
if(started.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
final serverType = type.value;
|
||||
final hostData = this.host.text.trim();
|
||||
final portData = this.port.text.trim();
|
||||
started.value = true;
|
||||
if(serverType != ServerType.local || portData != kDefaultBackendPort.toString()) {
|
||||
yield ServerResult(ServerResultType.starting);
|
||||
}
|
||||
|
||||
if (hostData.isEmpty) {
|
||||
yield ServerResult(ServerResultType.missingHostError);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (portData.isEmpty) {
|
||||
yield ServerResult(ServerResultType.missingPortError);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
final portNumber = int.tryParse(portData);
|
||||
if (portNumber == null) {
|
||||
yield ServerResult(ServerResultType.illegalPortError);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((serverType != ServerType.local || portData != kDefaultBackendPort.toString()) && !(await isBackendPortFree())) {
|
||||
yield ServerResult(ServerResultType.freeingPort);
|
||||
final result = await freeBackendPort();
|
||||
yield ServerResult(result ? ServerResultType.freePortSuccess : ServerResultType.freePortError);
|
||||
if(!result) {
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(serverType){
|
||||
case ServerType.embedded:
|
||||
final process = await startEmbeddedBackend(detached.value, onError: (errorMessage) {
|
||||
if(started.value) {
|
||||
started.value = false;
|
||||
onError(errorMessage);
|
||||
}
|
||||
});
|
||||
watchProcess(process.pid).then((_) {
|
||||
if(started.value) {
|
||||
started.value = false;
|
||||
onExit();
|
||||
}
|
||||
});
|
||||
embeddedProcessPid = process.pid;
|
||||
break;
|
||||
case ServerType.remote:
|
||||
yield ServerResult(ServerResultType.pingingRemote);
|
||||
final uriResult = await pingBackend(hostData, portNumber);
|
||||
if(uriResult == null) {
|
||||
yield ServerResult(ServerResultType.pingError);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
remoteServer = await startRemoteBackendProxy(uriResult);
|
||||
break;
|
||||
case ServerType.local:
|
||||
if(portNumber != kDefaultBackendPort) {
|
||||
yield ServerResult(ServerResultType.pingingLocal);
|
||||
final uriResult = await pingBackend(kDefaultBackendHost, portNumber);
|
||||
if(uriResult == null) {
|
||||
yield ServerResult(ServerResultType.pingError);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
localServer = await startRemoteBackendProxy(Uri.parse("http://$kDefaultBackendHost:$portData"));
|
||||
}else {
|
||||
// If the local server is running on port 3551 there is no reverse proxy running
|
||||
// We only need to check if everything is working
|
||||
started.value = false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
yield ServerResult(ServerResultType.pingingLocal);
|
||||
final uriResult = await pingBackend(kDefaultBackendHost, kDefaultBackendPort);
|
||||
if(uriResult == null) {
|
||||
yield ServerResult(ServerResultType.pingError);
|
||||
remoteServer?.close(force: true);
|
||||
localServer?.close(force: true);
|
||||
started.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
yield ServerResult(ServerResultType.startSuccess);
|
||||
}catch(error, stackTrace) {
|
||||
yield ServerResult(
|
||||
ServerResultType.startError,
|
||||
error: error,
|
||||
stackTrace: stackTrace
|
||||
);
|
||||
remoteServer?.close(force: true);
|
||||
localServer?.close(force: true);
|
||||
started.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
Stream<ServerResult> stop() async* {
|
||||
Future<bool> stop({required bool interactive}) async {
|
||||
if(!started.value) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
yield ServerResult(ServerResultType.stopping);
|
||||
started.value = false;
|
||||
try{
|
||||
switch(type()){
|
||||
case ServerType.embedded:
|
||||
final embeddedProcessPid = this.embeddedProcessPid;
|
||||
if(embeddedProcessPid != null) {
|
||||
Process.killPid(embeddedProcessPid, ProcessSignal.sigterm);
|
||||
this.embeddedProcessPid = null;
|
||||
}
|
||||
break;
|
||||
case ServerType.remote:
|
||||
await remoteServer?.close(force: true);
|
||||
remoteServer = null;
|
||||
break;
|
||||
case ServerType.local:
|
||||
await localServer?.close(force: true);
|
||||
localServer = null;
|
||||
break;
|
||||
_cancel();
|
||||
final stream = stopBackend(
|
||||
type: type.value,
|
||||
implementation: _implementation
|
||||
);
|
||||
final completer = Completer<bool>();
|
||||
InfoBarEntry? entry;
|
||||
_worker = stream.listen((event) {
|
||||
entry?.close();
|
||||
entry = _handeEvent(event, interactive);
|
||||
if(event.type.isError) {
|
||||
completer.complete(false);
|
||||
}else if(event.type.isSuccess) {
|
||||
completer.complete(true);
|
||||
}
|
||||
yield ServerResult(ServerResultType.stopSuccess);
|
||||
}catch(error, stackTrace){
|
||||
yield ServerResult(
|
||||
ServerResultType.stopError,
|
||||
error: error,
|
||||
stackTrace: stackTrace
|
||||
);
|
||||
started.value = true;
|
||||
}
|
||||
});
|
||||
return await completer.future;
|
||||
}
|
||||
|
||||
void _cancel() {
|
||||
worker?.cancel(); // Do not await or it will hang
|
||||
_worker?.cancel(); // Do not await or it will hang
|
||||
_infoBars.forEach((infoBar) => infoBar.close());
|
||||
_infoBars.clear();
|
||||
}
|
||||
|
||||
InfoBarEntry _handeEvent(ServerResult event) {
|
||||
log("[BACKEND] Handling event: $event");
|
||||
InfoBarEntry? _handeEvent(ServerResult event, bool interactive) {
|
||||
log("[BACKEND] Handling event: $event (interactive: $interactive, start: ${event.type.isStart}, error: ${event.type.isError})");
|
||||
started.value = event.type.isStart && !event.type.isError;
|
||||
switch (event.type) {
|
||||
case ServerResultType.starting:
|
||||
return _showRebootInfoBar(
|
||||
translations.startingServer,
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.startingServer,
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startSuccess:
|
||||
return _showRebootInfoBar(
|
||||
type.value == ServerType.local ? translations.checkedServer : translations.startedServer,
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
type.value == ServerType.local ? translations.checkedServer : translations.startedServer,
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startError:
|
||||
return _showRebootInfoBar(
|
||||
type.value == ServerType.local ? translations.localServerError(event.error ?? translations.unknownError) : translations.startServerError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
type.value == ServerType.local ? translations.localServerError(event.error ?? translations.unknownError) : translations.startServerError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.stopping:
|
||||
return _showRebootInfoBar(
|
||||
translations.stoppingServer,
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.stoppingServer,
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.stopSuccess:
|
||||
return _showRebootInfoBar(
|
||||
translations.stoppedServer,
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.stoppedServer,
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.stopError:
|
||||
return _showRebootInfoBar(
|
||||
translations.stopServerError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
case ServerResultType.missingHostError:
|
||||
return _showRebootInfoBar(
|
||||
translations.missingHostNameError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
case ServerResultType.missingPortError:
|
||||
return _showRebootInfoBar(
|
||||
translations.missingPortError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
case ServerResultType.illegalPortError:
|
||||
return _showRebootInfoBar(
|
||||
translations.illegalPortError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
case ServerResultType.freeingPort:
|
||||
return _showRebootInfoBar(
|
||||
translations.freeingPort,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
case ServerResultType.freePortSuccess:
|
||||
return _showRebootInfoBar(
|
||||
translations.freedPort,
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
case ServerResultType.freePortError:
|
||||
return _showRebootInfoBar(
|
||||
translations.freePortError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
case ServerResultType.pingingRemote:
|
||||
return _showRebootInfoBar(
|
||||
translations.pingingServer(ServerType.remote.name),
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
case ServerResultType.pingingLocal:
|
||||
return _showRebootInfoBar(
|
||||
translations.pingingServer(type.value.name),
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
case ServerResultType.pingError:
|
||||
return _showRebootInfoBar(
|
||||
translations.pingError(type.value.name),
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.stopServerError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startMissingHostError:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.missingHostNameError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startMissingPortError:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.missingPortError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startIllegalPortError:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.illegalPortError,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startFreeingPort:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.freeingPort,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startFreePortSuccess:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.freedPort,
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: infoBarShortDuration
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startFreePortError:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.freePortError(event.error ?? translations.unknownError),
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: infoBarLongDuration
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startPingingRemote:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.pingingServer(ServerType.remote.name),
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startPingingLocal:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.pingingServer(type.value.name),
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startPingError:
|
||||
if(interactive) {
|
||||
return _showRebootInfoBar(
|
||||
translations.pingError(type.value.name),
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
case ServerResultType.startedImplementation:
|
||||
_implementation = event.implementation;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -597,4 +537,11 @@ class BackendController extends GetxController {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<void> restart() async {
|
||||
if(started.value) {
|
||||
await stop(interactive: false);
|
||||
await start(interactive: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ class DllController extends GetxController {
|
||||
late final RxBool customGameServer;
|
||||
late final RxnInt timestamp;
|
||||
late final Rx<UpdateStatus> status;
|
||||
InfoBarEntry? infoBarEntry;
|
||||
|
||||
DllController() {
|
||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||
@@ -75,6 +74,7 @@ class DllController extends GetxController {
|
||||
}
|
||||
|
||||
Future<bool> updateGameServerDll({bool force = false, bool silent = false}) async {
|
||||
InfoBarEntry? infoBarEntry;
|
||||
try {
|
||||
if(customGameServer.value) {
|
||||
status.value = UpdateStatus.success;
|
||||
@@ -100,8 +100,8 @@ class DllController extends GetxController {
|
||||
}
|
||||
await Future.wait(
|
||||
[
|
||||
downloadRebootDll(rebootBeforeS20DllFile, beforeS20Mirror.text),
|
||||
downloadRebootDll(rebootAboveS20DllFile, aboveS20Mirror.text),
|
||||
downloadRebootDll(rebootBeforeS20DllFile, beforeS20Mirror.text, false),
|
||||
downloadRebootDll(rebootAboveS20DllFile, aboveS20Mirror.text, true),
|
||||
Future.delayed(const Duration(seconds: 1))
|
||||
],
|
||||
eagerError: false
|
||||
|
||||
@@ -110,7 +110,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
|
||||
log("[${host ? 'HOST' : 'GAME'}] Checking backend(port: ${_backendController.type.value.name}, type: ${_backendController.type.value.name})...");
|
||||
final backendResult = _backendController.started() || await _backendController.toggleInteractive();
|
||||
final backendResult = _backendController.started() || await _backendController.toggle();
|
||||
if(!backendResult){
|
||||
log("[${host ? 'HOST' : 'GAME'}] Cannot start backend");
|
||||
_onStop(
|
||||
@@ -526,7 +526,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
}
|
||||
await _operation?.cancel();
|
||||
_operation = null;
|
||||
_backendController.stop();
|
||||
_backendController.stop(interactive: false);
|
||||
}
|
||||
|
||||
host = host ?? widget.host;
|
||||
@@ -629,7 +629,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
||||
);
|
||||
break;
|
||||
case _StopReason.tokenError:
|
||||
_backendController.stop();
|
||||
_backendController.stop(interactive: false);
|
||||
showRebootInfoBar(
|
||||
translations.tokenError(instance == null ? translations.none : instance.injectedDlls.map((element) => element.name).join(", ")),
|
||||
severity: InfoBarSeverity.error,
|
||||
|
||||
@@ -162,7 +162,12 @@ class _BackendPageState extends RebootPageState<BackendPage> {
|
||||
key: backendDetachedOverlayTargetKey,
|
||||
child: ToggleSwitch(
|
||||
checked: _backendController.detached(),
|
||||
onChanged: (value) => _backendController.detached.value = value
|
||||
onChanged: (value) async {
|
||||
_backendController.detached.value = value;
|
||||
if(_backendController.started.value) {
|
||||
await _backendController.restart();
|
||||
}
|
||||
}
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -75,6 +75,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
|
||||
lastPage = index;
|
||||
_pageController.jumpToPage(index);
|
||||
pagesController.add(null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,7 +153,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
|
||||
try {
|
||||
if(_backendController.started.value) {
|
||||
await _backendController.toggleInteractive();
|
||||
await _backendController.toggle();
|
||||
}
|
||||
}catch(error) {
|
||||
log("[BACKEND] Cannot stop backend on exit: $error");
|
||||
@@ -524,36 +525,6 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
);
|
||||
}
|
||||
|
||||
Widget get _backButton => StreamBuilder(
|
||||
stream: pagesController.stream,
|
||||
builder: (context, _) => Button(
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all(const EdgeInsets.symmetric(
|
||||
vertical: 12.0,
|
||||
horizontal: 16.0
|
||||
)),
|
||||
backgroundColor: WidgetStateProperty.all(Colors.transparent),
|
||||
shape: WidgetStateProperty.all(Border())
|
||||
),
|
||||
onPressed: appStack.isEmpty && !inDialog ? null : () {
|
||||
if(inDialog) {
|
||||
Navigator.of(appNavigatorKey.currentContext!).pop();
|
||||
}else {
|
||||
final lastPage = appStack.removeLast();
|
||||
pageStack.remove(lastPage);
|
||||
if (lastPage is int) {
|
||||
hitBack = true;
|
||||
pageIndex.value = lastPage;
|
||||
} else {
|
||||
Navigator.of(pageKey.currentContext!).pop();
|
||||
}
|
||||
}
|
||||
pagesController.add(null);
|
||||
},
|
||||
child: const Icon(FluentIcons.back, size: 12.0),
|
||||
)
|
||||
);
|
||||
|
||||
Widget get _autoSuggestBox => Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter_gen/gen_l10n/reboot_localizations.dart';
|
||||
@@ -36,6 +39,7 @@ class SettingsPage extends RebootPage {
|
||||
class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final DllController _dllController = Get.find<DllController>();
|
||||
int? _downloadFromMirrorId;
|
||||
|
||||
@override
|
||||
Widget? get button => null;
|
||||
@@ -115,7 +119,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
}
|
||||
|
||||
_dllController.customGameServer.value = entry.key;
|
||||
_dllController.infoBarEntry?.close();
|
||||
if(!entry.key) {
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
@@ -141,11 +144,7 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
child: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _dllController.beforeS20Mirror,
|
||||
onChanged: (value) {
|
||||
if(Uri.tryParse(value) != null) {
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
}
|
||||
},
|
||||
onChanged: _scheduleMirrorDownload
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
@@ -194,6 +193,24 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
}
|
||||
});
|
||||
|
||||
void _scheduleMirrorDownload(String value) async {
|
||||
if(_downloadFromMirrorId != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Uri.tryParse(value) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final id = Random.secure().nextInt(1000000);
|
||||
_downloadFromMirrorId = id;
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
if(_downloadFromMirrorId == id) {
|
||||
await _dllController.updateGameServerDll(force: true);
|
||||
}
|
||||
_downloadFromMirrorId = null;
|
||||
}
|
||||
|
||||
Widget get _internalFilesNewServerSource => Obx(() {
|
||||
if(!_dllController.customGameServer.value) {
|
||||
return SettingTile(
|
||||
@@ -209,11 +226,7 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
child: TextFormBox(
|
||||
placeholder: translations.settingsServerMirrorPlaceholder,
|
||||
controller: _dllController.aboveS20Mirror,
|
||||
onChanged: (value) {
|
||||
if(Uri.tryParse(value) != null) {
|
||||
_dllController.updateGameServerDll(force: true);
|
||||
}
|
||||
},
|
||||
onChanged: _scheduleMirrorDownload
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8.0),
|
||||
@@ -273,7 +286,6 @@ class _SettingsPageState extends RebootPageState<SettingsPage> {
|
||||
text: Text(entry.text),
|
||||
onPressed: () {
|
||||
_dllController.timer.value = entry;
|
||||
_dllController.infoBarEntry?.close();
|
||||
_dllController.updateGameServerDll(
|
||||
force: true
|
||||
);
|
||||
|
||||
@@ -45,7 +45,7 @@ class _ServerButtonState extends State<ServerButton> {
|
||||
builder: (context, snapshot) => Obx(() => Text(_buttonText))
|
||||
),
|
||||
),
|
||||
onPressed: () => _controller.toggleInteractive()
|
||||
onPressed: () => _controller.toggle()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -32,18 +32,16 @@ class _ServerTypeSelectorState extends State<ServerTypeSelector> {
|
||||
));
|
||||
}
|
||||
|
||||
MenuFlyoutItem _createItem(ServerType type) {
|
||||
return MenuFlyoutItem(
|
||||
text: Text(type.label),
|
||||
onPressed: () async {
|
||||
_controller.stop();
|
||||
_controller.type.value = type;
|
||||
}
|
||||
);
|
||||
}
|
||||
MenuFlyoutItem _createItem(ServerType type) => MenuFlyoutItem(
|
||||
text: Text(type.label),
|
||||
onPressed: () async {
|
||||
await _controller.stop(interactive: false);
|
||||
_controller.type.value = type;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
extension ServerTypeExtension on ServerType {
|
||||
extension _ServerTypeExtension on ServerType {
|
||||
String get label {
|
||||
return this == ServerType.embedded ? translations.embedded
|
||||
: this == ServerType.remote ? translations.remote
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: reboot_launcher
|
||||
description: Graphical User Interface for Project Reboot
|
||||
version: "10.0.6"
|
||||
version: "10.0.7"
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
@@ -43,6 +43,7 @@ dependencies:
|
||||
# Async helpers
|
||||
async: ^2.11.0
|
||||
sync: ^0.3.0
|
||||
synchronized: ^3.3.0+3
|
||||
|
||||
# State management
|
||||
get: ^4.6.6
|
||||
|
||||
Reference in New Issue
Block a user