~ruther/NosSmooth

e4b2ea9faa61be1369e4acb98706d7c33bf0a9f2 — František Boháček 3 years ago a90b9c1
feat: add sample for walk command
A Samples/WalkCommands/ChatPacketInterceptor.cs => Samples/WalkCommands/ChatPacketInterceptor.cs +106 -0
@@ 0,0 1,106 @@
//
//  ChatPacketInterceptor.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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NosCore.Packets.Enumerations;
using NosCore.Packets.ServerPackets.Chats;
using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalClient;
using Remora.Results;
using WalkCommands.Commands;

namespace WalkCommands;

/// <summary>
/// Interceptor of chat commands.
/// </summary>
public class ChatPacketInterceptor : IPacketInterceptor
{
    private readonly IServiceProvider _provider;
    private readonly ILogger<ChatPacketInterceptor> _logger;
    private readonly INostaleClient _client;

    /// <summary>
    /// Initializes a new instance of the <see cref="ChatPacketInterceptor"/> class.
    /// </summary>
    /// <param name="provider">The service provider.</param>
    /// <param name="logger">The logger.</param>
    /// <param name="client">The nostale client.</param>
    public ChatPacketInterceptor(IServiceProvider provider, ILogger<ChatPacketInterceptor> logger, INostaleClient client)
    {
        _provider = provider;
        _logger = logger;
        _client = client;
    }

    /// <inheritdoc />
    public bool InterceptSend(ref string packet)
    {
        if (packet.StartsWith($"say #"))
        {
            var packetString = packet;
            Task.Run(async () =>
            {
                try
                {
                    await ExecuteCommand(packetString.Substring(5));
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Could not execute command.");
                }
            });
            return false;
        }

        return true;
    }

    /// <inheritdoc />
    public bool InterceptReceive(ref string packet)
    {
        return true;
    }

    private async Task ExecuteCommand(string command)
    {
        await _client.ReceivePacketAsync(new SayPacket
        {
            Type = SayColorType.Green, Message = $"Handling a command {command}."
        });

        var splitted = command.Split(new[] { ' ' });
        using var scope = _provider.CreateScope();
        Result result;
        switch (splitted[0])
        {
            case "walk":
                var walkCommand = scope.ServiceProvider.GetRequiredService<Commands.WalkCommands>();
                result = await walkCommand.HandleWalkToAsync(int.Parse(splitted[1]), int.Parse(splitted[2]), splitted.Length > 3 ? bool.Parse(splitted[3]) : true);
                break;
            case "detach":
                var detachCommand = scope.ServiceProvider.GetRequiredService<DetachCommand>();
                result = await detachCommand.HandleDetach();
                break;
            default:
                await _client.ReceivePacketAsync(new SayPacket
                {
                    Type = SayColorType.Red, Message = $"The command {splitted[0]} was not found."
                });
                return;
        }

        if (!result.IsSuccess)
        {
            _logger.LogError("Could not execute a command");
            _logger.LogResultError(result);
        }
    }
}
\ No newline at end of file

A Samples/WalkCommands/Commands/DetachCommand.cs => Samples/WalkCommands/Commands/DetachCommand.cs +53 -0
@@ 0,0 1,53 @@
//
//  DetachCommand.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 NosCore.Packets.Enumerations;
using NosCore.Packets.ServerPackets.Chats;
using NosSmooth.Core.Client;
using Remora.Results;

namespace WalkCommands.Commands;

/// <summary>
/// Group for detaching command that detaches the dll.
/// </summary>
public class DetachCommand
{
    private readonly CancellationTokenSource _dllStop;
    private readonly INostaleClient _client;

    /// <summary>
    /// Initializes a new instance of the <see cref="DetachCommand"/> class.
    /// </summary>
    /// <param name="dllStop">The cancellation token source to stop the client.</param>
    /// <param name="client">The nostale client.</param>
    public DetachCommand(CancellationTokenSource dllStop, INostaleClient client)
    {
        _dllStop = dllStop;
        _client = client;
    }

    /// <summary>
    /// Detach the dll.
    /// </summary>
    /// <returns>A result that may or may not have succeeded.</returns>
    public async Task<Result> HandleDetach()
    {
        var receiveResult = await _client.ReceivePacketAsync(new SayPacket
        {
            Message = "Going to detach!",
            Type = SayColorType.Green
        });

        if (!receiveResult.IsSuccess)
        {
            return receiveResult;
        }

        _dllStop.Cancel();
        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Samples/WalkCommands/Commands/WalkCommands.cs => Samples/WalkCommands/Commands/WalkCommands.cs +77 -0
@@ 0,0 1,77 @@
//
//  WalkCommands.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 NosCore.Packets.Enumerations;
using NosCore.Packets.ServerPackets.Chats;
using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
using Remora.Results;

namespace WalkCommands.Commands;

/// <summary>
/// Represents command group for walking.
/// </summary>
public class WalkCommands
{
    private readonly INostaleClient _client;

    /// <summary>
    /// Initializes a new instance of the <see cref="WalkCommands"/> class.
    /// </summary>
    /// <param name="client">The nostale client.</param>
    public WalkCommands(INostaleClient client)
    {
        _client = client ?? throw new ArgumentNullException(nameof(client));
    }

    /// <summary>
    /// Attempts to walk the character to the specified lcoation.
    /// </summary>
    /// <param name="x">The x coordinate.</param>
    /// <param name="y">The y coordinate.</param>
    /// <param name="isCancellable">Whether the user can cancel the operation.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>A result that may or may not have succeeded.</returns>
    public async Task<Result> HandleWalkToAsync
    (
        int x,
        int y,
        bool isCancellable = true,
        CancellationToken ct = default
    )
    {
        var receiveResult = await _client.ReceivePacketAsync
        (
            new SayPacket
            {
                Type = SayColorType.Red, Message = $"Going to walk to {x} {y}"
            },
            ct
        );

        if (!receiveResult.IsSuccess)
        {
            return receiveResult;
        }

        var command = new WalkCommand(x, y, isCancellable);
        var walkResult = await _client.SendCommandAsync(command, ct);
        if (!walkResult.IsSuccess)
        {
            return walkResult;
        }

        return await _client.ReceivePacketAsync
        (
            new SayPacket
            {
                Type = SayColorType.Red, Message = "Walk has finished successfully."
            },
            ct
        );
    }
}
\ No newline at end of file

A Samples/WalkCommands/DllMain.cs => Samples/WalkCommands/DllMain.cs +40 -0
@@ 0,0 1,40 @@
//
//  DllMain.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.Runtime.InteropServices;

namespace WalkCommands;

/// <summary>
/// Represents the dll entrypoint class.
/// </summary>
public class DllMain
{
    [DllImport("kernel32")]
#pragma warning disable SA1600
    public static extern bool AllocConsole();
#pragma warning restore SA1600

    /// <summary>
    /// Represents the dll entrypoint method.
    /// </summary>
    [DllExport]
    public static void Main()
    {
        AllocConsole();
        new Thread(() =>
        {
            try
            {
                new Startup().RunAsync().GetAwaiter().GetResult();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }).Start();
    }
}
\ No newline at end of file

A Samples/WalkCommands/FodyWeavers.xml => Samples/WalkCommands/FodyWeavers.xml +3 -0
@@ 0,0 1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Costura />
</Weavers>
\ No newline at end of file

A Samples/WalkCommands/Startup.cs => Samples/WalkCommands/Startup.cs +51 -0
@@ 0,0 1,51 @@
//
//  Startup.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 Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Core.Client;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
using WalkCommands.Commands;

namespace WalkCommands;

/// <summary>
/// Startup class of WalkCommands.
/// </summary>
public class Startup
{
    private IServiceProvider BuildServices()
    {
        return new ServiceCollection()
            .AddLocalClient()
            .AddScoped<Commands.WalkCommands>()
            .AddScoped<DetachCommand>()
            .AddSingleton<CancellationTokenSource>()
            .Configure<LocalClientOptions>(o => o.AllowIntercept = true)
            .AddSingleton<IPacketInterceptor, ChatPacketInterceptor>()
            .AddLogging(b =>
            {
                b.ClearProviders();
                b.AddConsole();
                b.SetMinimumLevel(LogLevel.Debug);
            })
            .BuildServiceProvider();
    }

    /// <summary>
    /// Run the MoveToMiniland.
    /// </summary>
    /// <returns>A task that may or may not have succeeded.</returns>
    public async Task RunAsync()
    {
        var provider = BuildServices();
        var mainCancellation = provider.GetRequiredService<CancellationTokenSource>();

        var client = provider.GetRequiredService<INostaleClient>();
        await client.RunAsync(mainCancellation.Token);
    }
}
\ No newline at end of file

A Samples/WalkCommands/WalkCommands.csproj => Samples/WalkCommands/WalkCommands.csproj +76 -0
@@ 0,0 1,76 @@
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <AssemblyName>WalkCommands</AssemblyName>
    <RootNamespace>WalkCommands</RootNamespace>
    <LangVersion>10</LangVersion>
  </PropertyGroup>
  <PropertyGroup>
    <DllExportIdent>9C088A1D-54DE-4A9B-9C1B-DBC5BD5F5299</DllExportIdent>
    <DllExportMetaLibName>DllExport.dll</DllExportMetaLibName>
    <DllExportNamespace>WalkCommands</DllExportNamespace>
    <DllExportDDNSCecil>true</DllExportDDNSCecil>
    <PlatformTarget>x86</PlatformTarget>
    <DllExportOrdinalsBase>7</DllExportOrdinalsBase>
    <DllExportGenExpLib>false</DllExportGenExpLib>
    <DllExportOurILAsm>false</DllExportOurILAsm>
    <DllExportSysObjRebase>false</DllExportSysObjRebase>
    <DllExportLeaveIntermediateFiles>false</DllExportLeaveIntermediateFiles>
    <DllExportTimeout>30000</DllExportTimeout>
    <DllExportPeCheck>2</DllExportPeCheck>
    <DllExportPatches>0</DllExportPatches>
    <DllExportPreProcType>0</DllExportPreProcType>
    <DllExportPostProcType>0</DllExportPostProcType>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Costura.Fody">
      <Version>5.7.0</Version>
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="DllExport">
      <Version>1.7.4</Version>
      <Visible>false</Visible>
      <Wz>1</Wz>
    </PackageReference>
    <PackageReference Include="Fody">
      <Version>6.6.0</Version>
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection">
      <Version>6.0.0</Version>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions">
      <Version>6.0.0</Version>
    </PackageReference>
    <PackageReference Include="Microsoft.Extensions.Logging.Console">
      <Version>6.0.0</Version>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
  </ItemGroup>
  <ImportGroup Label=".NET DllExport">
    <Import Project="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Condition="Exists($([MSBuild]::Escape('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')))" Label="8337224c9ad9e356" />
  </ImportGroup>
  <Target Name="DllExportRestorePkg" BeforeTargets="PrepareForBuild">
    <Error Condition="!Exists('$(SolutionDir)DllExport.bat')" Text="DllExport.bat is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport" />
    <Exec Condition="('$(DllExportModImported)' != 'true' Or !Exists('$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets')) And Exists('$(SolutionDir)DllExport.bat')" Command=".\DllExport.bat  -action Restore" WorkingDirectory="$(SolutionDir)" />
    <MSBuild Condition="'$(DllExportModImported)' != 'true'" Projects="$(SolutionDir)packages\DllExport.1.7.4\tools\net.r_eg.DllExport.targets" Targets="DllExportMetaXBaseTarget" Properties="TargetFramework=$(TargetFramework)">
      <Output TaskParameter="TargetOutputs" PropertyName="DllExportMetaXBase" />
    </MSBuild>
    <ItemGroup>
      <Reference Include="DllExport, PublicKeyToken=8337224c9ad9e356">
        <HintPath>$(SolutionDir)packages\DllExport.1.7.4\gcache\$(DllExportMetaXBase)\$(DllExportNamespace)\$(DllExportMetaLibName)</HintPath>
        <Private>False</Private>
        <SpecificVersion>False</SpecificVersion>
      </Reference>
    </ItemGroup>
  </Target>
  <Target Name="DllExportRPkgDynamicImport" BeforeTargets="PostBuildEvent" DependsOnTargets="GetFrameworkPaths" Condition="'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'">
    <MSBuild BuildInParallel="true" UseResultsCache="true" Projects="$(MSBuildProjectFullPath)" Properties="DllExportRPkgDyn=true" Targets="Build" />
  </Target>
</Project>
\ No newline at end of file

Do not follow this link