From 6387c54d508d29708e8c81ec1c2fa03bfe7c7d7c Mon Sep 17 00:00:00 2001 From: Rutherther Date: Thu, 27 Jan 2022 21:49:53 +0100 Subject: [PATCH] feat(localclient): add pet walk handler --- .../Structs/ControlManager.cs | 5 + .../Structs/PetManager.cs | 3 + .../Structs/PlayerManager.cs | 3 + .../Walk/ControlCommandWalkHandler.cs | 145 ++++++++++++++++++ .../Walk/PetWalkCommandHandler.cs | 75 +++++++++ .../Walk/PlayerWalkCommandHandler.cs | 95 ++---------- .../Extensions/ServiceCollectionExtensions.cs | 3 +- 7 files changed, 244 insertions(+), 85 deletions(-) create mode 100644 Local/NosSmooth.LocalClient/CommandHandlers/Walk/ControlCommandWalkHandler.cs create mode 100644 Local/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs diff --git a/Local/NosSmooth.LocalBinding/Structs/ControlManager.cs b/Local/NosSmooth.LocalBinding/Structs/ControlManager.cs index 9870d7e0828c86e409ca928ee6d56a5156ca508f..12ff79be61a8797cd93ac74276ada115df045332 100644 --- a/Local/NosSmooth.LocalBinding/Structs/ControlManager.cs +++ b/Local/NosSmooth.LocalBinding/Structs/ControlManager.cs @@ -23,6 +23,11 @@ public abstract class ControlManager : NostaleObject { } + /// + /// Gets the entity this control manager is for. + /// + public abstract MapBaseObj Entity { get; } + /// /// Gets the current player position x coordinate. /// diff --git a/Local/NosSmooth.LocalBinding/Structs/PetManager.cs b/Local/NosSmooth.LocalBinding/Structs/PetManager.cs index 84cca5154cf4da22804290bce3ce71559ca4eac4..cd14b0142be44bfd8e6857b6642448f131372788 100644 --- a/Local/NosSmooth.LocalBinding/Structs/PetManager.cs +++ b/Local/NosSmooth.LocalBinding/Structs/PetManager.cs @@ -45,4 +45,7 @@ public class PetManager : ControlManager return new MapNpcObj(Memory, (IntPtr)playerAddress); } } + + /// + public override MapBaseObj Entity => Pet; } \ No newline at end of file diff --git a/Local/NosSmooth.LocalBinding/Structs/PlayerManager.cs b/Local/NosSmooth.LocalBinding/Structs/PlayerManager.cs index 8594628d53ae93f934cf31f476425e7569439c25..900bf548c48c6530f21f8a8b131c7b581bd4c0b0 100644 --- a/Local/NosSmooth.LocalBinding/Structs/PlayerManager.cs +++ b/Local/NosSmooth.LocalBinding/Structs/PlayerManager.cs @@ -88,4 +88,7 @@ public class PlayerManager : ControlManager return playerId; } } + + /// + public override MapBaseObj Entity => Player; } \ No newline at end of file diff --git a/Local/NosSmooth.LocalClient/CommandHandlers/Walk/ControlCommandWalkHandler.cs b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/ControlCommandWalkHandler.cs new file mode 100644 index 0000000000000000000000000000000000000000..4c88abd60b84788cb67992c0b24ae64e2386356c --- /dev/null +++ b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/ControlCommandWalkHandler.cs @@ -0,0 +1,145 @@ +// +// ControlCommandWalkHandler.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.Client; +using NosSmooth.Core.Commands.Control; +using NosSmooth.Core.Commands.Walking; +using NosSmooth.Core.Extensions; +using NosSmooth.LocalBinding.Structs; +using NosSmooth.LocalClient.CommandHandlers.Walk.Errors; +using Remora.Results; + +namespace NosSmooth.LocalClient.CommandHandlers.Walk; + +/// +/// Handler for control manager walk command. +/// +internal class ControlCommandWalkHandler +{ + private readonly INostaleClient _nostaleClient; + private readonly Func> _walkFunction; + private readonly ControlManager _controlManager; + private readonly WalkCommandHandlerOptions _options; + + private ushort _x; + private ushort _y; + + /// + /// Initializes a new instance of the class. + /// + /// The nostale client. + /// The walk function. + /// The control manager. + /// The options. + public ControlCommandWalkHandler + ( + INostaleClient nostaleClient, + Func> walkFunction, + ControlManager controlManager, + WalkCommandHandlerOptions options + ) + { + _nostaleClient = nostaleClient; + _walkFunction = walkFunction; + _controlManager = controlManager; + _options = options; + } + + /// + /// Handle walk take control command. + /// + /// The x coordinate. + /// The y coordinate. + /// The take control command. + /// The name of the take control group. + /// The cancellation token for cancelling the operation. + /// A representing the result of the asynchronous operation. + public async Task HandleCommand(ushort x, ushort y, ITakeControlCommand command, string groupName, CancellationToken ct = default) + { + _x = x; + _y = y; + + using CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(ct); + WalkUnfinishedReason? reason = null; + var takeControlCommand = command.CreateTakeControl + ( + groupName, + WalkGrantedCallback, + (r) => + { + reason = r switch + { + ControlCancelReason.AnotherTask => WalkUnfinishedReason.AnotherTask, + ControlCancelReason.UserAction => WalkUnfinishedReason.UserAction, + _ => WalkUnfinishedReason.Unknown + }; + return Task.FromResult(Result.FromSuccess()); + } + ); + + var commandResult = await _nostaleClient.SendCommandAsync(takeControlCommand, ct); + if (!commandResult.IsSuccess) + { + return commandResult; + } + + if (reason is null && !IsAt(x, y)) + { + reason = WalkUnfinishedReason.PathNotFound; + } + + if (reason is null) + { + return Result.FromSuccess(); + } + + return new WalkNotFinishedError + ( + _controlManager.X, + _controlManager.Y, + (WalkUnfinishedReason)reason + ); + } + + private bool IsAtTarget() + { + return _controlManager.TargetX == _controlManager.Entity.X + && _controlManager.TargetY == _controlManager.Entity.Y; + } + + private bool IsAt(ushort x, ushort y) + { + return _controlManager.Entity.X == x && _controlManager.Entity.Y == y; + } + + private async Task WalkGrantedCallback(CancellationToken ct) + { + var result = _walkFunction(_x, _y); + if (!result.IsSuccess) + { + return Result.FromError(result); + } + + while (!ct.IsCancellationRequested) + { + try + { + await Task.Delay(_options.CheckDelay, ct); + } + catch + { + // ignored + } + + if (IsAtTarget() || IsAt(_x, _y)) + { + return Result.FromSuccess(); + } + } + + return Result.FromSuccess(); // cancellation is handled in cancellation callback. + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs new file mode 100644 index 0000000000000000000000000000000000000000..7fe1083c79b5f7dd8587fe0b6d55c76f7372fb3e --- /dev/null +++ b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PetWalkCommandHandler.cs @@ -0,0 +1,75 @@ +// +// PetWalkCommandHandler.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.Options; +using NosSmooth.Core.Client; +using NosSmooth.Core.Commands; +using NosSmooth.Core.Commands.Control; +using NosSmooth.Core.Commands.Walking; +using NosSmooth.LocalBinding.Objects; +using Remora.Results; + +namespace NosSmooth.LocalClient.CommandHandlers.Walk; + +/// +/// Handles . +/// +public class PetWalkCommandHandler : ICommandHandler +{ + /// + /// Group that is used for . + /// + public const string PetWalkControlGroup = "PetWalk"; + + private readonly PetManagerBinding _petManagerBinding; + private readonly INostaleClient _nostaleClient; + private readonly WalkCommandHandlerOptions _options; + + /// + /// Initializes a new instance of the class. + /// + /// The character object binding. + /// The nostale client. + /// The options. + public PetWalkCommandHandler + ( + PetManagerBinding petManagerBinding, + INostaleClient nostaleClient, + IOptions options + ) + { + _options = options.Value; + _petManagerBinding = petManagerBinding; + _nostaleClient = nostaleClient; + } + + /// + public async Task HandleCommand(PetWalkCommand command, CancellationToken ct = default) + { + if (_petManagerBinding.PetManagerList.Length < command.PetSelector + 1) + { + return new NotFoundError("Could not find the pet using the given selector."); + } + var petManager = _petManagerBinding.PetManagerList[command.PetSelector]; + + var handler = new ControlCommandWalkHandler + ( + _nostaleClient, + (x, y) => _petManagerBinding.PetWalk(command.PetSelector, x, y), + petManager, + _options + ); + + return await handler.HandleCommand + ( + command.TargetX, + command.TargetY, + command, + PetWalkControlGroup + "_" + command.PetSelector, + ct + ); + } +} \ No newline at end of file diff --git a/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs index 04c46435a5b1a584aae14e098e1d753df63b9202..2c0b2dc0ce7cf4f43e0647cb1c0a03d249f81336 100644 --- a/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs +++ b/Local/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs @@ -30,9 +30,6 @@ public class PlayerWalkCommandHandler : ICommandHandler private readonly INostaleClient _nostaleClient; private readonly WalkCommandHandlerOptions _options; - private ushort _x; - private ushort _y; - /// /// Initializes a new instance of the class. /// @@ -52,93 +49,23 @@ public class PlayerWalkCommandHandler : ICommandHandler } /// - /// 1) If client called walk, cancel. - /// 2) If another walk command requested, cancel. - /// 3) If at the correct spot, cancel. - /// 4) If not walking for over x ms, cancel. public async Task HandleCommand(PlayerWalkCommand command, CancellationToken ct = default) { - _x = command.TargetX; - _y = command.TargetY; - - using CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(ct); - WalkUnfinishedReason? reason = null; - var takeControlCommand = command.CreateTakeControl + var handler = new ControlCommandWalkHandler ( - PlayerWalkControlGroup, - WalkGrantedCallback, - (r) => - { - reason = r switch - { - ControlCancelReason.AnotherTask => WalkUnfinishedReason.AnotherTask, - ControlCancelReason.UserAction => WalkUnfinishedReason.UserAction, - _ => WalkUnfinishedReason.Unknown - }; - return Task.FromResult(Result.FromSuccess()); - } + _nostaleClient, + (x, y) => _playerManagerBinding.Walk(x, y), + _playerManagerBinding.PlayerManager, + _options ); - var commandResult = await _nostaleClient.SendCommandAsync(takeControlCommand, ct); - if (!commandResult.IsSuccess) - { - return commandResult; - } - - if (reason is null && !IsAt(command.TargetX, command.TargetY)) - { - reason = WalkUnfinishedReason.PathNotFound; - } - - if (reason is null) - { - return Result.FromSuccess(); - } - - return new WalkNotFinishedError + return await handler.HandleCommand ( - _playerManagerBinding.PlayerManager.X, - _playerManagerBinding.PlayerManager.Y, - (WalkUnfinishedReason)reason + command.TargetX, + command.TargetY, + command, + PlayerWalkControlGroup, + ct ); } - - private bool IsAtTarget() - { - return _playerManagerBinding.PlayerManager.TargetX == _playerManagerBinding.PlayerManager.Player.X - && _playerManagerBinding.PlayerManager.TargetY == _playerManagerBinding.PlayerManager.Player.Y; - } - - private bool IsAt(ushort x, ushort y) - { - return _playerManagerBinding.PlayerManager.Player.X == x && _playerManagerBinding.PlayerManager.Player.Y == y; - } - - private async Task WalkGrantedCallback(CancellationToken ct) - { - while (!ct.IsCancellationRequested) - { - var result = _playerManagerBinding.Walk(_x, _y); - if (!result.IsSuccess) - { - return Result.FromError(result); - } - - try - { - await Task.Delay(_options.CheckDelay, ct); - } - catch - { - // ignored - } - - if (IsAtTarget() || IsAt(_x, _y)) - { - return Result.FromSuccess(); - } - } - - return Result.FromSuccess(); // cancellation is handled in cancellation callback. - } } \ No newline at end of file diff --git a/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs b/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs index 03d8fe5683d65998dea8d4a99d89aa56a29ff3c4..a2b94f743c6373cc44424fddb2c29b17441fa023 100644 --- a/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +++ b/Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs @@ -29,7 +29,8 @@ public static class ServiceCollectionExtensions serviceCollection.AddNostaleBindings(); serviceCollection .AddTakeControlCommand() - .AddCommandHandler(); + .AddCommandHandler() + .AddCommandHandler(); serviceCollection.TryAddSingleton(); serviceCollection.TryAddSingleton(p => p.GetRequiredService());