using XFEEDToolBox.NativeSupport; using System.Diagnostics; using XFEEDToolBox.Core.Execution; using XFEEDToolBox.Core.Navigation; using XFEEDToolBox.Core.Plugins; using XFEEDToolBox.Core.Session; namespace XFEEDToolBox.ViewModels; public partial class ToolboxViewModel { private int pluginQueryWorkerThread, pluginQueryApplyThread; public object PluginQueryDiagnostics => new { workerThread = Volatile.Read(ref pluginQueryWorkerThread), applyThread = pluginQueryApplyThread }; private sealed record PluginConnection(string Executable, PluginStatus? Status = null, PluginPackage? Package = null, PluginHandshake? Handshake = null, int ProcessId = 0, bool RelogReady = false, bool TradeReady = false, string Waiting = "", string RelogIssue = "", string TradeIssue = ""); // Process/module enumeration, disk validation, IPC and response decoding // stay on the worker even when an awaited I/O operation completes inline. private Task ReadPluginConnectionAsync(string executable, string fallback, bool inGame, CancellationToken token) => Task.Run(async () => { Volatile.Write(ref pluginQueryWorkerThread, Environment.CurrentManagedThreadId); if (Environment.GetCommandLineArgs().Contains("--check-background-data")) await Task.Delay(500, token).ConfigureAwait(false); if (executable.Length == 0) executable = fallback.Length != 0 ? fallback : FindSteamGame(); if (executable.Length == 0) return new PluginConnection(executable, Waiting: "未找到游戏目录,请选择 EliteDangerous64.exe。"); var state = await pluginManager.InspectAsync(executable, token).ConfigureAwait(false); if (!state.Installed) return new PluginConnection(executable, state, Waiting: state.Message); var package = pluginManager.Package(); var games = Process.GetProcessesByName("EliteDangerous64"); try { var matching = games.Where(p => string.Equals(p.MainModule?.FileName, executable, StringComparison.OrdinalIgnoreCase)).ToArray(); if (matching.Length != 1) return new PluginConnection(executable, state, package, Waiting: matching.Length == 0 ? "游戏尚未运行,请先启动游戏并进入飞船驾驶舱。" : "检测到多个游戏进程,请保留要控制的唯一游戏实例。"); var game = matching[0]; var loader = Path.Combine(Path.GetDirectoryName(executable)!, "dinput8.dll"); if (!game.Modules.Cast().Any(m => string.Equals(m.FileName, loader, StringComparison.OrdinalIgnoreCase))) return new PluginConnection(executable, state, package, Waiting: "插件尚未加载,请在游戏下次启动后重试。"); var hello = await GamePluginManager.ProbeAsync(game.Id, token: token).ConfigureAwait(false); // Observers and extension loading wait for an actual Journal session. if (inGame && !state.PendingRemoval && hello.Loaded && hello.Active && package.SupportsNavigation && !hello.ObservationStarted) hello = await GamePluginManager.StartNavigationObservationAsync(game.Id, token).ConfigureAwait(false); var relog = false; var trade = false; var relogIssue = "当前插件尚未提供可用的重进接口"; var tradeIssue = ""; if (inGame && !state.PendingRemoval && hello.Loaded && hello.Active) { if (hello.SupportsRelog && package.SupportsRelog) { try { await new NativeOriginalSessionMenuRecovery(game.Id, state.Installation!.GameSha256, Path.Combine(NativeSupportPackage.Directory,"DockingControl")).PrepareAsync(token).ConfigureAwait(false); relog = true; relogIssue = ""; } catch (OperationCanceledException) when (token.IsCancellationRequested) { throw; } catch (Exception error) { relogIssue = "重进连接未就绪:" + error.Message; } } if (hello.SupportsNavigation && package.SupportsNavigation && (runtime.FlightDriver is not NativeFlightControlDriver current || current.ProcessId != game.Id)) { var processId = game.Id; var hash = state.Installation!.GameSha256; try { await runtime.BindFlightDriverAsync(new NativeFlightControlDriver(processId, hash, () => runtime.Player, () => runtime.Settings.NavigationFuelReserve, new NativeGameInputChannel(processId, hash, NativeSupportPackage.Directory), cancellation => EnsureNativeFlightProtectionAsync(processId, hash, cancellation)), token).ConfigureAwait(false); } catch(Exception error)when(!token.IsCancellationRequested) {return new PluginConnection(executable,state,package,hello,game.Id,relog,false,Waiting:"插件已安装,飞行遥测尚未就绪:"+error.Message,RelogIssue:relogIssue);} } if (runtime.FlightDriver is NativeFlightControlDriver driver && driver.ProcessId == game.Id) { try { var capabilities = await new NativeCommerceChannel(game.Id).CallAsync(new { op = "describe" }, token).ConfigureAwait(false); trade = NativeTradeCapabilities.SupportsDockedTrade(capabilities); } catch (OperationCanceledException) when (token.IsCancellationRequested) { throw; } catch (Exception error) { tradeIssue = error.Message; } } } return new PluginConnection(executable, state, package, hello, game.Id, relog, trade, RelogIssue: relogIssue, TradeIssue: tradeIssue); } finally { foreach (var game in games) game.Dispose(); } }, token); }