~ruther/NosTale-PacketLogger

2576a4b220dd457eec06684d1ccb3142640dd9bf — Rutherther 2 years ago fd54708
feat: add nostale processes observation
2 files changed, 223 insertions(+), 0 deletions(-)

A src/PacketLogger/Models/NostaleProcess.cs
A src/PacketLogger/Models/NostaleProcesses.cs
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

Do not follow this link