XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
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<PluginConnection> 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<ProcessModule>().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);
}