using XFEEDToolBox.Core.Navigation; using XFEEDToolBox.Core.Notifications; using XFEEDToolBox.Core.Session; namespace XFEEDToolBox.ViewModels; public partial class ToolboxViewModel { private event Action? ProtectionReentry; internal IDisposable ObserveArrivals(Action arrived,Action captureStar, Action sessionReady) { var observer=new NavigationArrivalObserver(); var reentryObserver=new SessionReadyNotificationObserver(); int ProcessId()=>(runtime.FlightDriver as NativeFlightControlDriver)?.ProcessId??gameEnvironment?.ProcessId??0; void Interstellar(NavigationProgress progress) { if(OfflinePreview||DemoMode)return; if(observer.Observe(progress,runtime.Player,DateTimeOffset.UtcNow) is {} arrival)arrived(arrival,ProcessId()); } void Local(SystemNavigationProgress progress) { if(OfflinePreview||DemoMode)return; if(observer.Observe(progress,runtime.Player,DateTimeOffset.UtcNow) is {} arrival)arrived(arrival,ProcessId()); } void Jump(Core.Data.ObservedJournalEvent observation,Core.Models.PlayerState player) { if(OfflinePreview||DemoMode||runtime.Navigator.State!=NavigationRunState.Running ||!observation.Data.TryGetProperty("event",out var kind)||kind.GetString()!="FSDJump" ||!player.InGame||player.Navigation.CurrentSystemAddress is not >0)return; captureStar(new(Guid.NewGuid().ToString("N"),player.Navigation.CurrentSystemAddress.Value,player.SystemName,player.SystemName,false,false, player.CommanderId,player.LoadGameAt,DateTimeOffset.UtcNow),ProcessId()); } void SessionState(Core.Services.RuntimeChange change) { if(OfflinePreview||DemoMode||reentryObserver.ExpectedTarget is not {} expected)return; try { using var process=System.Diagnostics.Process.GetProcessById(expected.ProcessId); if(process.HasExited||!process.ProcessName.Equals("EliteDangerous64",StringComparison.OrdinalIgnoreCase))return; var running=expected with{StartedUtcTicks=process.StartTime.ToUniversalTime().Ticks,Executable=process.MainModule?.FileName??""}; if(reentryObserver.Observe(runtime.Player,running,DateTimeOffset.UtcNow) is {} notice)sessionReady(notice,running.ProcessId); } catch(Exception error)when(error is ArgumentException or InvalidOperationException or System.ComponentModel.Win32Exception){} } void Reentry(SessionReentryEvent entry) { if(OfflinePreview||DemoMode)return; reentryObserver.OnReentry(entry,runtime.Player);SessionState(Core.Services.RuntimeChange.Player); } runtime.Navigator.Progress+=Interstellar;systemNavigator.Progress+=Local; runtime.LiveJournalEvent+=Jump; sessions.Reentry+=Reentry;ProtectionReentry+=Reentry;runtime.DataChanged+=SessionState; return new ArrivalSubscription(()=> { runtime.Navigator.Progress-=Interstellar;systemNavigator.Progress-=Local;runtime.LiveJournalEvent-=Jump; sessions.Reentry-=Reentry;ProtectionReentry-=Reentry;runtime.DataChanged-=SessionState; }); } private sealed class ArrivalSubscription(Action release):IDisposable { private Action? cleanup=release; public void Dispose()=>Interlocked.Exchange(ref cleanup,null)?.Invoke(); } }