A src/PacketLogger/Models/NostaleProcess.cs => src/PacketLogger/Models/NostaleProcess.cs +92 -0
@@ 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;
+
+/// <summary>
+/// A NosTale process.
+/// </summary>
+public class NostaleProcess : ObservableObject
+{
+ private bool _wasInGame;
+ private string? _lastCharacterName;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleProcess"/> class.
+ /// </summary>
+ /// <param name="process">The process.</param>
+ /// <param name="browserManager">The browser manager.</param>
+ public NostaleProcess(Process process, NosBrowserManager browserManager)
+ {
+ Process = process;
+ BrowserManager = browserManager;
+
+ ObserveChanges();
+ }
+
+ /// <summary>
+ /// Gets the name of the character.
+ /// </summary>
+ public string CharacterString => _lastCharacterName ?? "- (Not in game)";
+
+ /// <summary>
+ /// Gets the process string.
+ /// </summary>
+ public string ProcessString => $"{Process.Id} | {Process.ProcessName}";
+
+ /// <summary>
+ /// Gets the process.
+ /// </summary>
+ public Process Process { get; init; }
+
+ /// <summary>
+ /// Gets the browser manager.
+ /// </summary>
+ public NosBrowserManager BrowserManager { get; init; }
+
+ /// <summary>
+ /// Look for changes in the process, fire property changed.
+ /// </summary>
+ 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
A src/PacketLogger/Models/NostaleProcesses.cs => src/PacketLogger/Models/NostaleProcesses.cs +131 -0
@@ 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;
+
+/// <summary>
+/// Keeps and refreshes a collection of NosTale processes.
+/// </summary>
+public class NostaleProcesses : IDisposable
+{
+ private readonly IDisposable _cleanUp;
+ private readonly List<long> _errorfulProcesses;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NostaleProcesses"/> class.
+ /// </summary>
+ public NostaleProcesses()
+ {
+ _errorfulProcesses = new List<long>();
+ Processes = new ObservableCollection<NostaleProcess>();
+ _cleanUp = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(1))
+ .ObserveOn(RxApp.TaskpoolScheduler)
+ .Subscribe
+ (
+ _ =>
+ {
+ try
+ {
+ Refresh().GetAwaiter().GetResult();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+ );
+ }
+
+ /// <summary>
+ /// Gets NosTale processes.
+ /// </summary>
+ public ObservableCollection<NostaleProcess> Processes { get; }
+
+ /// <inheritdoc />
+ public void Dispose()
+ {
+ _cleanUp.Dispose();
+ }
+
+ private async Task Refresh()
+ {
+ var nosTaleProcesses = CommsInjector.FindNosTaleProcesses().ToArray();
+ var toRemove = new List<NostaleProcess>();
+
+ 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<long>();
+
+ 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