~ruther/NosSmooth.Comms

12e8d20913473e4c044e11e9460fd0c7b7bb3d0c — Rutherther 2 years ago ecfc8cd
feat: add a console sample logging all received and sent packets from nostale process
A src/Samples/ConsolePacketLogger/ClientService.cs => src/Samples/ConsolePacketLogger/ClientService.cs +97 -0
@@ 0,0 1,97 @@
//
//  ClientService.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.Diagnostics;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NosSmooth.Comms.Core;
using NosSmooth.Comms.Data.Messages;
using NosSmooth.Comms.Local;
using NosSmooth.Core.Contracts;
using NosSmooth.Core.Extensions;
using NosSmooth.PacketSerializer.Extensions;
using NosSmooth.PacketSerializer.Packets;

namespace ConsolePacketLogger;

/// <inheritdoc />
public class ClientService : BackgroundService
{
    private readonly IPacketTypesRepository _packetTypesRepository;
    private readonly CommsInjector _injector;
    private readonly IHostApplicationLifetime _lifetime;
    private readonly ILogger<ClientService> _logger;
    private readonly PacketLoggerOptions _options;

    /// <summary>
    /// Initializes a new instance of the <see cref="ClientService"/> class.
    /// </summary>
    /// <param name="packetTypesRepository">The packet types repository.</param>
    /// <param name="injector">The injector.</param>
    /// <param name="lifetime">The lifetime.</param>
    /// <param name="logger">The logger.</param>
    /// <param name="options">The options.</param>
    public ClientService
    (
        IPacketTypesRepository packetTypesRepository,
        CommsInjector injector,
        IHostApplicationLifetime lifetime,
        ILogger<ClientService> logger,
        IOptions<PacketLoggerOptions> options
    )
    {
        _packetTypesRepository = packetTypesRepository;
        _injector = injector;
        _lifetime = lifetime;
        _logger = logger;
        _options = options.Value;
    }

    /// <inheritdoc />
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var packetsResult = _packetTypesRepository.AddDefaultPackets();
        if (!packetsResult.IsSuccess)
        {
            _logger.LogResultError(packetsResult);
        }

        var process = Process.GetProcessById(_options.ProcessId);
        var connectionResult = await _injector.EstablishNamedPipesConnectionAsync
            (process, stoppingToken, stoppingToken);
        if (!connectionResult.IsDefined(out var connection))
        {
            _logger.LogResultError(connectionResult);
            _lifetime.StopApplication();
            return;
        }

        var handshakeResponseResult = await connection.Connection
            .ContractHanshake(new HandshakeRequest("ConsolePacketLogger example", true, false))
            .WaitForAsync(DefaultStates.ResponseObtained, ct: stoppingToken);

        if (!handshakeResponseResult.IsDefined(out var handshakeResponse))
        {
            _logger.LogResultError(handshakeResponseResult);
            _lifetime.StopApplication();
            return;
        }

        _logger.LogInformation
        (
            $"Connected to {handshakeResponse.CharacterName ?? "Not in game"} ({handshakeResponse.CharacterId?.ToString() ?? "Not in game"})"
        );

        var runResult = await connection.Connection.RunHandlerAsync(stoppingToken);
        if (!runResult.IsSuccess)
        {
            _logger.LogResultError(runResult);
        }

        _lifetime.StopApplication();
    }
}
\ No newline at end of file

A src/Samples/ConsolePacketLogger/ConsolePacketLogger.csproj => src/Samples/ConsolePacketLogger/ConsolePacketLogger.csproj +29 -0
@@ 0,0 1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net7.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
        <ApplicationManifest>app.manifest</ApplicationManifest>
    </PropertyGroup>

    <ItemGroup>
      <PackageReference Include="MessagePack" Version="2.4.59" />
      <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
      <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
      <PackageReference Include="Spectre.Console" Version="0.46.0" />
    </ItemGroup>

    <ItemGroup>
      <ProjectReference Include="..\..\Core\NosSmooth.Comms.Abstractions\NosSmooth.Comms.Abstractions.csproj" />
      <ProjectReference Include="..\..\Core\NosSmooth.Comms.Core\NosSmooth.Comms.Core.csproj" />
      <ProjectReference Include="..\..\Core\NosSmooth.Comms.NamedPipes\NosSmooth.Comms.NamedPipes.csproj" />
      <ProjectReference Include="..\..\Core\NosSmooth.Comms.Tcp\NosSmooth.Comms.Tcp.csproj" />
      <ProjectReference Include="..\..\Local\NosSmooth.Comms.Inject\NosSmooth.Comms.Inject.csproj" />
      <ProjectReference Include="..\..\Local\NosSmooth.Comms.LocalData\NosSmooth.Comms.LocalData.csproj" />
      <ProjectReference Include="..\..\Local\NosSmooth.Comms.Local\NosSmooth.Comms.Local.csproj" />
    </ItemGroup>

</Project>

A src/Samples/ConsolePacketLogger/EveryPacketResponder.cs => src/Samples/ConsolePacketLogger/EveryPacketResponder.cs +24 -0
@@ 0,0 1,24 @@
//
//  EveryPacketResponder.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 NosSmooth.Core.Packets;
using NosSmooth.Packets;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
using Remora.Results;

namespace ConsolePacketLogger;

/// <inheritdoc />
public class EveryPacketResponder : IEveryPacketResponder
{
    /// <inheritdoc />
    public Task<Result> Respond<TPacket>(PacketEventArgs<TPacket> packetArgs, CancellationToken ct = default)
        where TPacket : IPacket
    {
        Console.WriteLine((packetArgs.Source == PacketSource.Server ? "[Recv]\t" : "[Sent]\t") + packetArgs.PacketString);
        return Task.FromResult(Result.FromSuccess());
    }
}
\ No newline at end of file

A src/Samples/ConsolePacketLogger/PacketLoggerOptions.cs => src/Samples/ConsolePacketLogger/PacketLoggerOptions.cs +18 -0
@@ 0,0 1,18 @@
//
//  PacketLoggerOptions.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.

namespace ConsolePacketLogger;

/// <summary>
/// Options telling the process to inject into.
/// </summary>
public class PacketLoggerOptions
{
    /// <summary>
    /// Gets or sets the id of the process to connect to.
    /// </summary>
    public int ProcessId { get; set; }
}
\ No newline at end of file

A src/Samples/ConsolePacketLogger/Program.cs => src/Samples/ConsolePacketLogger/Program.cs +75 -0
@@ 0,0 1,75 @@
//
//  Program.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 ConsolePacketLogger;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NosSmooth.Comms.Core.Extensions;
using NosSmooth.Comms.Local;
using NosSmooth.Comms.Local.Extensions;
using NosSmooth.Core.Extensions;
using NosSmooth.Data.Abstractions;
using NosSmooth.LocalBinding;
using Spectre.Console;

/// <summary>
/// A class with main.
/// </summary>
public static class Program
{
    /// <summary>
    /// A main.
    /// </summary>
    /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
    public static async Task Main()
    {
        var processInitResults = CommsInjector.CreateNostaleProcesssesBrowsers().ToArray();

        if (processInitResults.Length == 0)
        {
            Console.WriteLine("Could not find any NosTale processes.");
            return;
        }

        foreach (var initResult in processInitResults.Where(x => !x.IsSuccess))
        {
            Console.WriteLine($"There was an error initializing browser manager for a process.");
            Console.WriteLine(initResult.ToFullString());
        }

        var nostaleProcesses = processInitResults.Where(x => x.IsSuccess).Select(x => x.Entity);

        var selectedProcess = AnsiConsole.Prompt
        (
            new SelectionPrompt<NosBrowserManager>()
                .Title("Choose NosTale process to log packets from.")
                .UseConverter
                (
                    x => x.IsInGame
                        ? $"{x.PlayerManager.Player.Name} ({x.Process.ProcessName} - {x.Process.Id})"
                        : $"Not in game ({x.Process.ProcessName} - {x.Process.Id})"
                )
                .AddChoices(nostaleProcesses)
        );

        var host = new HostBuilder()
            .ConfigureLogging(b => b.ClearProviders().AddConsole())
            .ConfigureServices
            (
                s => s
                    .Configure<PacketLoggerOptions>(o => o.ProcessId = selectedProcess.Process.Id)
                    .AddNostaleCore()
                    .AddSingleClientHandling()
                    .AddLocalComms()
                    .AddHostedService<ClientService>()
                    .AddPacketResponder<EveryPacketResponder>()
            )
            .Build();

        await host.RunAsync();
    }
}
\ No newline at end of file

A src/Samples/ConsolePacketLogger/app.manifest => src/Samples/ConsolePacketLogger/app.manifest +11 -0
@@ 0,0 1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
    <assemblyIdentity version="1.0.0.0" name="ConsolePacketLogger.app"/>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
        <security>
            <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
                <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
            </requestedPrivileges>
        </security>
    </trustInfo>
</assembly>
\ No newline at end of file

Do not follow this link