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