This commit is contained in:
Alessandro Autiero
2025-03-08 17:06:01 +01:00
parent b319479def
commit 90448eeaa1
22 changed files with 589 additions and 670 deletions

View File

@@ -1,9 +1,12 @@
import 'dart:io';
class ServerResult {
final ServerResultType type;
final ServerImplementation? implementation;
final Object? error;
final StackTrace? stackTrace;
ServerResult(this.type, {this.error, this.stackTrace});
ServerResult(this.type, {this.implementation, this.error, this.stackTrace});
@override
String toString() {
@@ -11,22 +14,32 @@ class ServerResult {
}
}
class ServerImplementation {
final Process? process;
final HttpServer? server;
ServerImplementation({this.process, this.server});
}
enum ServerResultType {
starting,
startMissingHostError,
startMissingPortError,
startIllegalPortError,
startFreeingPort,
startFreePortSuccess,
startFreePortError,
startPingingRemote,
startPingingLocal,
startPingError,
startedImplementation,
startSuccess,
startError,
stopping,
stopSuccess,
stopError,
missingHostError,
missingPortError,
illegalPortError,
freeingPort,
freePortSuccess,
freePortError,
pingingRemote,
pingingLocal,
pingError;
stopError;
bool get isStart => name.contains("start");
bool get isError => name.contains("Error");

View File

@@ -15,6 +15,122 @@ final Semaphore _semaphore = Semaphore();
String? _lastIp;
String? _lastPort;
Stream<ServerResult> startBackend({required ServerType type, required String host, required String port, required bool detached, required void Function(String) onError}) async* {
Process? process;
HttpServer? server;
try {
host = host.trim();
port = port.trim();
if(type != ServerType.local || port != kDefaultBackendPort.toString()) {
yield ServerResult(ServerResultType.starting);
}
if (host.isEmpty) {
yield ServerResult(ServerResultType.startMissingHostError);
return;
}
if (port.isEmpty) {
yield ServerResult(ServerResultType.startMissingPortError);
return;
}
final portNumber = int.tryParse(port);
if (portNumber == null) {
yield ServerResult(ServerResultType.startIllegalPortError);
return;
}
if ((type != ServerType.local || port != kDefaultBackendPort.toString()) && !(await isBackendPortFree())) {
yield ServerResult(ServerResultType.startFreeingPort);
final result = await freeBackendPort();
if(!result) {
yield ServerResult(ServerResultType.startFreePortError);
return;
}
yield ServerResult(ServerResultType.startFreePortSuccess);
}
switch(type){
case ServerType.embedded:
process = await startEmbeddedBackend(detached, onError: onError);
yield ServerResult(ServerResultType.startedImplementation, implementation: ServerImplementation(process: process));
break;
case ServerType.remote:
yield ServerResult(ServerResultType.startPingingRemote);
final uriResult = await pingBackend(host, portNumber);
if(uriResult == null) {
yield ServerResult(ServerResultType.startPingError);
return;
}
server = await startRemoteBackendProxy(uriResult);
yield ServerResult(ServerResultType.startedImplementation, implementation: ServerImplementation(server: server));
break;
case ServerType.local:
if(portNumber != kDefaultBackendPort) {
yield ServerResult(ServerResultType.startPingingLocal);
final uriResult = await pingBackend(kDefaultBackendHost, portNumber);
if(uriResult == null) {
yield ServerResult(ServerResultType.startPingError);
return;
}
server = await startRemoteBackendProxy(Uri.parse("http://$kDefaultBackendHost:$port"));
yield ServerResult(ServerResultType.startedImplementation, implementation: ServerImplementation(server: server));
}
break;
}
yield ServerResult(ServerResultType.startPingingLocal);
final uriResult = await pingBackend(kDefaultBackendHost, kDefaultBackendPort);
if(uriResult == null) {
yield ServerResult(ServerResultType.startPingError);
process?.kill(ProcessSignal.sigterm);
server?.close(force: true);
return;
}
yield ServerResult(ServerResultType.startSuccess);
}catch(error, stackTrace) {
yield ServerResult(
ServerResultType.startError,
error: error,
stackTrace: stackTrace
);
process?.kill(ProcessSignal.sigterm);
server?.close(force: true);
}
}
Stream<ServerResult> stopBackend({required ServerType type, required ServerImplementation? implementation}) async* {
yield ServerResult(ServerResultType.stopping);
try{
switch(type){
case ServerType.embedded:
final process = implementation?.process;
if(process != null) {
Process.killPid(process.pid, ProcessSignal.sigterm);
}
break;
case ServerType.remote:
await implementation?.server?.close(force: true);
break;
case ServerType.local:
await implementation?.server?.close(force: true);
break;
}
yield ServerResult(ServerResultType.stopSuccess);
}catch(error, stackTrace){
yield ServerResult(
ServerResultType.stopError,
error: error,
stackTrace: stackTrace
);
}
}
Future<Process> startEmbeddedBackend(bool detached, {void Function(String)? onError}) async {
final process = await startProcess(
executable: backendStartExecutable,
@@ -25,7 +141,9 @@ Future<Process> startEmbeddedBackend(bool detached, {void Function(String)? onEr
log("[BACKEND] Error: $error");
onError?.call(error);
});
process.exitCode.then((exitCode) => log("[BACKEND] Exit code: $exitCode"));
if(!detached) {
process.exitCode.then((exitCode) => log("[BACKEND] Exit code: $exitCode"));
}
return process;
}

View File

@@ -7,11 +7,17 @@ import 'package:reboot_common/common.dart';
final File rebootBeforeS20DllFile = File("${dllsDirectory.path}\\reboot.dll");
final File rebootAboveS20DllFile = File("${dllsDirectory.path}\\rebootS20.dll");
const String kRebootBelowS20DownloadUrl =
"https://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/master/Reboot.zip";
const String kRebootAboveS20DownloadUrl =
"https://nightly.link/Milxnor/Project-Reboot-3.0/workflows/msbuild/master/RebootS20.zip";
const String _kRebootBelowS20FallbackDownloadUrl =
"https://github.com/Auties00/reboot_launcher/raw/master/gui/dependencies/dlls/RebootFallback.zip";
const String _kRebootAboveS20FallbackDownloadUrl =
"https://github.com/Auties00/reboot_launcher/raw/master/gui/dependencies/dlls/RebootS20Fallback.zip";
Future<bool> hasRebootDllUpdate(int? lastUpdateMs, {int hours = 24, bool force = false}) async {
final lastUpdate = await _getLastUpdate(lastUpdateMs);
final exists = await rebootBeforeS20DllFile.exists() && await rebootAboveS20DllFile.exists();
@@ -45,12 +51,15 @@ Future<void> downloadDependency(InjectableDll dll, String outputPath) async {
await output.writeAsBytes(response.bodyBytes, flush: true);
}
Future<void> downloadRebootDll(File file, String url) async {
Future<void> downloadRebootDll(File file, String url, bool aboveS20) async {
Directory? outputDir;
try {
final response = await http.get(Uri.parse(url));
var response = await http.get(Uri.parse(url));
if(response.statusCode != 200) {
throw Exception("Cannot download reboot.zip: status code ${response.statusCode}");
response = await http.get(Uri.parse(aboveS20 ? _kRebootAboveS20FallbackDownloadUrl : _kRebootBelowS20FallbackDownloadUrl));
if(response.statusCode != 200) {
throw Exception("status code ${response.statusCode}");
}
}
outputDir = await installationDirectory.createTemp("reboot_out");

View File

@@ -168,20 +168,6 @@ bool resume(int pid) {
}
}
Future<void> watchProcess(int pid) => Isolate.run(() {
final processHandle = OpenProcess(FILE_ACCESS_RIGHTS.SYNCHRONIZE, FALSE, pid);
if (processHandle == 0) {
return;
}
try {
WaitForSingleObject(processHandle, INFINITE);
}finally {
CloseHandle(processHandle);
}
});
List<String> createRebootArgs(String username, String password, bool host, GameServerType hostType, bool logging, String additionalArgs) {
log("[PROCESS] Generating reboot args");
if(password.isEmpty) {
@@ -292,16 +278,8 @@ final class _ExtendedProcess implements Process {
_stdout = attached ? delegate.stdout.asBroadcastStream() : null,
_stderr = attached ? delegate.stderr.asBroadcastStream() : null;
@override
Future<int> get exitCode {
try {
return _delegate.exitCode;
}catch(_) {
return watchProcess(_delegate.pid)
.then((_) => -1);
}
}
Future<int> get exitCode => _delegate.exitCode;
@override
bool kill([ProcessSignal signal = ProcessSignal.sigterm]) => _delegate.kill(signal);