// // NostaleProcesses.cs // // Copyright (c) František Boháček. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Management; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Runtime.InteropServices; using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using NosSmooth.Comms.Local; using NosSmooth.Core.Extensions; using NosSmooth.LocalBinding; using NosSmooth.LocalBinding.Errors; using NosSmooth.LocalBinding.Options; using NosSmooth.LocalBinding.Structs; using ReactiveUI; namespace PacketLogger.Models; /// /// Keeps and refreshes a collection of NosTale processes. /// public class NostaleProcesses : IDisposable { private readonly IDisposable? _cleanUp; private readonly SemaphoreSlim _semaphore; private readonly ManagementEventWatcher? _processStartWatcher; private readonly ManagementEventWatcher? _processStopWatcher; /// /// Initializes a new instance of the class. /// public NostaleProcesses() { _semaphore = new SemaphoreSlim(1, 1); Processes = new ObservableCollection(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); if (principal.IsInRole(WindowsBuiltInRole.Administrator)) { _cleanUp = Observable.Timer(DateTimeOffset.Now, TimeSpan.FromSeconds(1)) .Subscribe(_ => UpdateNames()); Supported = true; _processStartWatcher = new ManagementEventWatcher (new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace")); _processStartWatcher.EventArrived += HandleProcessOpenedEvent; _processStartWatcher.Start(); _processStopWatcher = new ManagementEventWatcher (new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace")); _processStopWatcher.EventArrived += HandleProcessClosedEvent; _processStopWatcher.Start(); // initial nostale processes // rest is handled by events foreach (var process in CommsInjector.FindNosTaleProcesses()) { HandleProcessOpened(process.Id); } } } } private void HandleProcessOpenedEvent(object sender, EventArrivedEventArgs e) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { HandleProcessOpened(Convert.ToInt32(e.NewEvent.Properties["ProcessId"].Value)); } } private void HandleProcessClosedEvent(object sender, EventArrivedEventArgs e) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { HandleProcessClosed(Convert.ToInt32(e.NewEvent.Properties["ProcessId"].Value)); } } private void HandleProcessOpened(int processId) { Process process; try { process = Process.GetProcessById(processId); } catch (Exception) { return; } if (!NosBrowserManager.IsProcessNostaleProcess(process)) { return; } NosBrowserManager nosBrowserManager = new NosBrowserManager ( process, new PlayerManagerOptions(), new SceneManagerOptions(), new PetManagerOptions(), new NetworkManagerOptions(), new UnitManagerOptions() ); var result = nosBrowserManager.Initialize(); if (!result.IsSuccess) { Console.WriteLine ($"Got an error when trying to initialize nos browser manager for {process.ProcessName}"); Console.WriteLine(result.ToFullString()); } if (nosBrowserManager.IsModuleLoaded()) { RxApp.MainThreadScheduler.Schedule (() => { _semaphore.Wait(); Processes.Add(new NostaleProcess(process, nosBrowserManager)); _semaphore.Release(); } ); } else { Console.WriteLine ( $"Cannot add {process.ProcessName} to nostale processes as player manager was not found in memory." ); } } private void HandleProcessClosed(int processId) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { var process = Processes.FirstOrDefault(x => x.Process.Id == processId); if (process is not null) { RxApp.MainThreadScheduler.Schedule (() => { process.ObserveChanges(); _semaphore.Wait(); Processes.Remove(process); _semaphore.Release(); } ); } } } /// /// Gets whether tracking and attaching to processes is supported for this run. /// /// /// Supported only on Windows run as elevated. /// public bool Supported { get; } /// /// Gets NosTale processes. /// public ObservableCollection Processes { get; } /// public void Dispose() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { _processStartWatcher?.Stop(); _processStartWatcher?.Dispose(); _processStopWatcher?.Stop(); _processStopWatcher?.Dispose(); } } private void UpdateNames() { _semaphore.Wait(); foreach (var process in Processes) { process.ObserveChanges(); } _semaphore.Release(); } }