From 2576a4b220dd457eec06684d1ccb3142640dd9bf Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 2 Feb 2023 17:55:01 +0100 Subject: [PATCH] feat: add nostale processes observation --- src/PacketLogger/Models/NostaleProcess.cs | 92 ++++++++++++++ src/PacketLogger/Models/NostaleProcesses.cs | 131 ++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 src/PacketLogger/Models/NostaleProcess.cs create mode 100644 src/PacketLogger/Models/NostaleProcesses.cs diff --git a/src/PacketLogger/Models/NostaleProcess.cs b/src/PacketLogger/Models/NostaleProcess.cs new file mode 100644 index 0000000..ab1f3db --- /dev/null +++ b/src/PacketLogger/Models/NostaleProcess.cs @@ -0,0 +1,92 @@ +// +// NostaleProcess.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.ComponentModel; +using System.Diagnostics; +using System.Reactive.Linq; +using CommunityToolkit.Mvvm.ComponentModel; +using NosSmooth.LocalBinding; +using ReactiveUI; +using Reloaded.Memory.Exceptions; + +namespace PacketLogger.Models; + +/// +/// A NosTale process. +/// +public class NostaleProcess : ObservableObject +{ + private bool _wasInGame; + private string? _lastCharacterName; + + /// + /// Initializes a new instance of the class. + /// + /// The process. + /// The browser manager. + public NostaleProcess(Process process, NosBrowserManager browserManager) + { + Process = process; + BrowserManager = browserManager; + + ObserveChanges(); + } + + /// + /// Gets the name of the character. + /// + public string CharacterString => _lastCharacterName ?? "- (Not in game)"; + + /// + /// Gets the process string. + /// + public string ProcessString => $"{Process.Id} | {Process.ProcessName}"; + + /// + /// Gets the process. + /// + public Process Process { get; init; } + + /// + /// Gets the browser manager. + /// + public NosBrowserManager BrowserManager { get; init; } + + /// + /// Look for changes in the process, fire property changed. + /// + internal void ObserveChanges() + { + try + { + if (BrowserManager.IsInGame != _wasInGame) + { + OnPropertyChanging(nameof(BrowserManager)); + OnPropertyChanged(nameof(BrowserManager)); + } + + var currentCharacterName = BrowserManager.IsInGame ? BrowserManager.PlayerManager.Player.Name : null; + var changed = _lastCharacterName != currentCharacterName; + + if (changed) + { + OnPropertyChanging(nameof(CharacterString)); + } + + _wasInGame = BrowserManager.IsInGame; + _lastCharacterName = currentCharacterName; + + if (changed) + { + OnPropertyChanged(nameof(CharacterString)); + } + } + catch (MemoryPermissionException) + { + // ignored, wait a bit, NosTale is probably just starting. + } + } +} \ No newline at end of file diff --git a/src/PacketLogger/Models/NostaleProcesses.cs b/src/PacketLogger/Models/NostaleProcesses.cs new file mode 100644 index 0000000..6e76987 --- /dev/null +++ b/src/PacketLogger/Models/NostaleProcesses.cs @@ -0,0 +1,131 @@ +// +// 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.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Threading.Tasks; +using NosSmooth.Comms.Local; +using NosSmooth.Core.Extensions; +using NosSmooth.LocalBinding; +using NosSmooth.LocalBinding.Options; +using ReactiveUI; + +namespace PacketLogger.Models; + +/// +/// Keeps and refreshes a collection of NosTale processes. +/// +public class NostaleProcesses : IDisposable +{ + private readonly IDisposable _cleanUp; + private readonly List _errorfulProcesses; + + /// + /// Initializes a new instance of the class. + /// + public NostaleProcesses() + { + _errorfulProcesses = new List(); + Processes = new ObservableCollection(); + _cleanUp = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1)) + .ObserveOn(RxApp.TaskpoolScheduler) + .Subscribe + ( + _ => + { + try + { + Refresh().GetAwaiter().GetResult(); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + ); + } + + /// + /// Gets NosTale processes. + /// + public ObservableCollection Processes { get; } + + /// + public void Dispose() + { + _cleanUp.Dispose(); + } + + private async Task Refresh() + { + var nosTaleProcesses = CommsInjector.FindNosTaleProcesses().ToArray(); + var toRemove = new List(); + + foreach (var currentProcess in Processes) + { + if (nosTaleProcesses.All(x => x.Id != currentProcess.Process.Id)) + { + toRemove.Add(currentProcess); + } + else + { + RxApp.MainThreadScheduler.Schedule(() => currentProcess.ObserveChanges()); + } + } + + foreach (var remove in toRemove) + { + RxApp.MainThreadScheduler.Schedule(() => Processes.Remove(remove)); + } + + foreach (var openProcess in nosTaleProcesses) + { + if (Processes.All(x => x.Process.Id != openProcess.Id) && !_errorfulProcesses.Contains(openProcess.Id)) + { + NosBrowserManager nosBrowserManager = new NosBrowserManager + ( + openProcess, + new PlayerManagerOptions(), + new SceneManagerOptions(), + new PetManagerOptions(), + new NetworkManagerOptions(), + new UnitManagerOptions() + ); + var result = nosBrowserManager.Initialize(); + if (result.IsSuccess) + { + RxApp.MainThreadScheduler.Schedule + (() => Processes.Add(new NostaleProcess(openProcess, nosBrowserManager))); + } + else + { + _errorfulProcesses.Add(openProcess.Id); + Console.WriteLine(result.ToFullString()); + } + } + } + + var errorfulToRemove = new List(); + + foreach (var errorfulProcess in _errorfulProcesses) + { + if (nosTaleProcesses.All(x => x.Id != errorfulProcess)) + { + errorfulToRemove.Add(errorfulProcess); + } + } + + foreach (var errorfulRemove in errorfulToRemove) + { + _errorfulProcesses.Remove(errorfulRemove); + } + } +} \ No newline at end of file -- 2.49.0