~ruther/NosSmooth

9a36c429b33712d3281f154059c0f6908594662d — František Boháček 3 years ago f6304c8
feat(sample): use chat commands in WalkCommands sample
D Samples/WalkCommands/ChatPacketInterceptor.cs => Samples/WalkCommands/ChatPacketInterceptor.cs +0 -112
@@ 1,112 0,0 @@
//
//  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 NosSmooth.Core.Client;
using NosSmooth.Core.Extensions;
using NosSmooth.LocalClient;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Chat;
using NosSmooth.Packets.Server.Chat;
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(EntityType.Map, 1, SayColor.Green, $"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
                (
                    ushort.Parse(splitted[1]),
                    ushort.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(EntityType.Map, 1, SayColor.Green, $"The command {splitted[0]} was not found.")
                );
                return;
        }

        if (!result.IsSuccess)
        {
            _logger.LogError("Could not execute a command");
            _logger.LogResultError(result);
        }
    }
}

A Samples/WalkCommands/Commands/CombatCommands.cs => Samples/WalkCommands/Commands/CombatCommands.cs +45 -0
@@ 0,0 1,45 @@
//
//  CombatCommands.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.ChatCommands;
using NosSmooth.Core.Client;
using NosSmooth.LocalBinding.Objects;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Results;

namespace WalkCommands.Commands;

/// <summary>
/// Represents command group for combat commands.
/// </summary>
public class CombatCommands : CommandGroup
{
    private readonly SceneManagerBinding _sceneManagerBinding;
    private readonly FeedbackService _feedbackService;

    /// <summary>
    /// Initializes a new instance of the <see cref="CombatCommands"/> class.
    /// </summary>
    /// <param name="sceneManagerBinding">The scene manager binding.</param>
    /// <param name="feedbackService">The feedback service.</param>
    public CombatCommands(SceneManagerBinding sceneManagerBinding, FeedbackService feedbackService)
    {
        _sceneManagerBinding = sceneManagerBinding;
        _feedbackService = feedbackService;
    }

    /// <summary>
    /// Focus the given entity.
    /// </summary>
    /// <param name="entityId">The entity id to focus.</param>
    /// <returns>A task that may or may not have succeeded.</returns>
    [Command("focus")]
    public Task<Result> HandleFocusAsync(int entityId)
    {
        return Task.FromResult(_sceneManagerBinding.FocusEntity(entityId));
    }
}
\ No newline at end of file

M Samples/WalkCommands/Commands/DetachCommand.cs => Samples/WalkCommands/Commands/DetachCommand.cs +8 -7
@@ 4,10 4,12 @@
//  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.ChatCommands;
using NosSmooth.Core.Client;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Chat;
using NosSmooth.Packets.Server.Chat;
using Remora.Commands.Groups;
using Remora.Results;

namespace WalkCommands.Commands;


@@ 15,20 17,20 @@ namespace WalkCommands.Commands;
/// <summary>
/// Group for detaching command that detaches the dll.
/// </summary>
public class DetachCommand
public class DetachCommand : CommandGroup
{
    private readonly CancellationTokenSource _dllStop;
    private readonly INostaleClient _client;
    private readonly FeedbackService _feedbackService;

    /// <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)
    /// <param name="feedbackService">The feedback service.</param>
    public DetachCommand(CancellationTokenSource dllStop, FeedbackService feedbackService)
    {
        _dllStop = dllStop;
        _client = client;
        _feedbackService = feedbackService;
    }

    /// <summary>


@@ 37,8 39,7 @@ public class DetachCommand
    /// <returns>A result that may or may not have succeeded.</returns>
    public async Task<Result> HandleDetach()
    {
        var receiveResult = await _client.ReceivePacketAsync
            (new SayPacket(EntityType.Map, 1, SayColor.Green, "Going to detach!"));
        var receiveResult = await _feedbackService.SendInfoMessageAsync("Going to detach!", CancellationToken);

        if (!receiveResult.IsSuccess)
        {

M Samples/WalkCommands/Commands/WalkCommands.cs => Samples/WalkCommands/Commands/WalkCommands.cs +16 -10
@@ 4,11 4,14 @@
//  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.ChatCommands;
using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
using NosSmooth.Packets.Enums;
using NosSmooth.Packets.Enums.Chat;
using NosSmooth.Packets.Server.Chat;
using Remora.Commands.Attributes;
using Remora.Commands.Groups;
using Remora.Results;

namespace WalkCommands.Commands;


@@ 16,17 19,20 @@ namespace WalkCommands.Commands;
/// <summary>
/// Represents command group for walking.
/// </summary>
public class WalkCommands
public class WalkCommands : CommandGroup
{
    private readonly INostaleClient _client;
    private readonly FeedbackService _feedbackService;

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

    /// <summary>


@@ 35,20 41,19 @@ public class WalkCommands
    /// <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>
    [Command("walk")]
    public async Task<Result> HandleWalkToAsync
    (
        ushort x,
        ushort y,
        bool isCancellable = true,
        CancellationToken ct = default
        bool isCancellable = true
    )
    {
        var receiveResult = await _client.ReceivePacketAsync
        (
            new SayPacket(EntityType.Map, 1, SayColor.Red, $"Going to walk to {x} {y}."),
            ct
            CancellationToken
        );

        if (!receiveResult.IsSuccess)


@@ 57,13 62,14 @@ public class WalkCommands
        }

        var command = new WalkCommand(x, y, isCancellable);
        var walkResult = await _client.SendCommandAsync(command, ct);
        var walkResult = await _client.SendCommandAsync(command, CancellationToken);
        if (!walkResult.IsSuccess)
        {
            await _feedbackService.SendErrorMessageAsync($"Could not finish walking. {walkResult.Error.Message}", CancellationToken);
            await _client.ReceivePacketAsync
            (
                new SayPacket(EntityType.Map, 1, SayColor.Red, "Could not finish walking."),
                ct
                CancellationToken
            );
            return walkResult;
        }


@@ 71,7 77,7 @@ public class WalkCommands
        return await _client.ReceivePacketAsync
        (
            new SayPacket(EntityType.Map, 1, SayColor.Red, "Walk has finished successfully."),
            ct
            CancellationToken
        );
    }
}

M Samples/WalkCommands/Startup.cs => Samples/WalkCommands/Startup.cs +19 -9
@@ 6,10 6,12 @@

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.ChatCommands;
using NosSmooth.Core.Client;
using NosSmooth.LocalBinding;
using NosSmooth.LocalClient;
using NosSmooth.LocalClient.Extensions;
using Remora.Commands.Extensions;
using WalkCommands.Commands;

namespace WalkCommands;


@@ 21,20 23,28 @@ public class Startup
{
    private IServiceProvider BuildServices()
    {
        return new ServiceCollection()
        var collection = 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();
            .AddNostaleChatCommands()
            .AddLogging
            (
                b =>
                {
                    b.ClearProviders();
                    b.AddConsole();
                    b.SetMinimumLevel(LogLevel.Debug);
                }
            );

        collection.AddCommandTree()
            .WithCommandGroup<DetachCommand>()
            .WithCommandGroup<CombatCommands>()
            .WithCommandGroup<Commands.WalkCommands>();
        return collection.BuildServiceProvider();
    }

    /// <summary>

M Samples/WalkCommands/WalkCommands.csproj => Samples/WalkCommands/WalkCommands.csproj +4 -1
@@ 7,7 7,9 @@
    <RootNamespace>WalkCommands</RootNamespace>
    <LangVersion>10</LangVersion>
    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
    <IncludeCopyLocalFilesOutputGroup>true</IncludeCopyLocalFilesOutputGroup>
    <EnableDynamicLoading>true</EnableDynamicLoading>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Costura.Fody">


@@ 34,6 36,7 @@
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\Local\NosSmooth.ChatCommands\NosSmooth.ChatCommands.csproj" />
    <ProjectReference Include="..\..\Local\NosSmooth.LocalClient\NosSmooth.LocalClient.csproj" />
  </ItemGroup>
</Project>
\ No newline at end of file

Do not follow this link