R Core/NosSmooth.Core/Commands/WalkCommand.cs => Core/NosSmooth.Core/Commands/Walking/PlayerWalkCommand.cs +25 -16
@@ 1,16 1,25 @@
-//
-// WalkCommand.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 NosSmooth.Core.Commands;
-
-/// <summary>
-/// Command that moves the player to the specified target position.
-/// May be used only in world.
-/// </summary>
-/// <param name="TargetX">The x coordinate of the target position to move to.</param>
-/// <param name="TargetY">The y coordinate of the target position to move to.</param>
-/// <param name="CancelOnUserMove">Whether to cancel the walk when the user clicks to move somewhere.</param>
-public record WalkCommand(ushort TargetX, ushort TargetY, bool CancelOnUserMove = true) : ICommand;>
\ No newline at end of file
+//
+// PlayerWalkCommand.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.Commands.Control;
+
+namespace NosSmooth.Core.Commands.Walking;
+
+/// <summary>
+/// Command that moves the player to the specified target position.
+/// May be used only in world.
+/// </summary>
+/// <param name="TargetX">The x coordinate of the target position to move to.</param>
+/// <param name="TargetY">The y coordinate of the target position to move to.</param>
+public record PlayerWalkCommand
+(
+ ushort TargetX,
+ ushort TargetY,
+ bool CanBeCancelledByAnother = true,
+ bool WaitForCancellation = true,
+ bool AllowUserCancel = true,
+ bool CancelOnMapChange = true
+) : ICommand, ITakeControlCommand;<
\ No newline at end of file
M Local/NosSmooth.LocalClient/CommandHandlers/Walk/Errors/WalkNotFinishedError.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/Errors/WalkNotFinishedError.cs +1 -1
@@ 14,5 14,5 @@ namespace NosSmooth.LocalClient.CommandHandlers.Walk.Errors;
/// <param name="X">The x coordinate where the player is. (if known)</param>
/// <param name="Y">The y coordinate where the player is. (if known)</param>
/// <param name="Reason"></param>
-public record WalkNotFinishedError(int? X, int? Y, WalkCancelReason Reason)
+public record WalkNotFinishedError(int? X, int? Y, WalkUnfinishedReason Reason)
: ResultError($"Could not finish the walk to {X} {Y}, because {Reason}");=
\ No newline at end of file
R Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkCommandHandler.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/PlayerWalkCommandHandler.cs +143 -96
@@ 1,97 1,144 @@
-//
-// WalkCommandHandler.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.Commands;
-using NosSmooth.LocalBinding.Objects;
-using NosSmooth.LocalClient.CommandHandlers.Walk.Errors;
-using Remora.Results;
-
-namespace NosSmooth.LocalClient.CommandHandlers.Walk;
-
-/// <summary>
-/// Handles <see cref="WalkCommand"/>.
-/// </summary>
-public class WalkCommandHandler : ICommandHandler<WalkCommand>
-{
- private readonly PlayerManagerBinding _playerManagerBinding;
- private readonly WalkStatus _walkStatus;
- private readonly WalkCommandHandlerOptions _options;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WalkCommandHandler"/> class.
- /// </summary>
- /// <param name="playerManagerBinding">The character object binding.</param>
- /// <param name="walkStatus">The walk status.</param>
- /// <param name="options">The options.</param>
- public WalkCommandHandler(PlayerManagerBinding playerManagerBinding, WalkStatus walkStatus, IOptions<WalkCommandHandlerOptions> options)
- {
- _options = options.Value;
- _playerManagerBinding = playerManagerBinding;
- _walkStatus = walkStatus;
- }
-
- /// <inheritdoc/>
- /// 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<Result> HandleCommand(WalkCommand command, CancellationToken ct = default)
- {
- CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(ct);
- ct = linked.Token;
- await _walkStatus.SetWalking(linked, command.TargetX, command.TargetY, command.CancelOnUserMove);
- while (!ct.IsCancellationRequested)
- {
- var walkResult = _playerManagerBinding.Walk(command.TargetX, command.TargetY);
- if (!walkResult.IsSuccess)
- {
- try
- {
- await _walkStatus.CancelWalkingAsync(ct: ct);
- }
- catch
- {
- // ignored, just for cancellation
- }
-
- return Result.FromError(walkResult);
- }
-
- if (walkResult.Entity == false)
- {
- await _walkStatus.CancelWalkingAsync(WalkCancelReason.NosTaleReturnedFalse);
- return new WalkNotFinishedError(_walkStatus.CurrentX, _walkStatus.CurrentY, WalkCancelReason.NosTaleReturnedFalse);
- }
- try
- {
- await Task.Delay(_options.CheckDelay, ct);
- }
- catch
- {
- // ignored
- }
-
- if (_walkStatus.IsFinished)
- {
- return Result.FromSuccess();
- }
-
- if (_walkStatus.Error is not null)
- {
- return new WalkNotFinishedError(_walkStatus.CurrentX, _walkStatus.CurrentY, (WalkCancelReason)_walkStatus.Error);
- }
-
- if ((DateTimeOffset.Now - _walkStatus.LastWalkTime).TotalMilliseconds > _options.NotWalkingTooLongTrigger)
- {
- await _walkStatus.CancelWalkingAsync(WalkCancelReason.NotWalkingTooLong);
- return new WalkNotFinishedError(_walkStatus.CurrentX, _walkStatus.CurrentY, WalkCancelReason.NotWalkingTooLong);
- }
- }
-
- return new WalkNotFinishedError(_walkStatus.CurrentX, _walkStatus.CurrentY, WalkCancelReason.Unknown);
- }
+//
+// PlayerWalkCommandHandler.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.Core.Extensions;
+using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalClient.CommandHandlers.Walk.Errors;
+using Remora.Results;
+
+namespace NosSmooth.LocalClient.CommandHandlers.Walk;
+
+/// <summary>
+/// Handles <see cref="PlayerWalkCommand"/>.
+/// </summary>
+public class PlayerWalkCommandHandler : ICommandHandler<PlayerWalkCommand>
+{
+ /// <summary>
+ /// Group that is used for <see cref="TakeControlCommand"/>.
+ /// </summary>
+ public const string PlayerWalkControlGroup = "PlayerWalk";
+
+ private readonly PlayerManagerBinding _playerManagerBinding;
+ private readonly INostaleClient _nostaleClient;
+ private readonly WalkCommandHandlerOptions _options;
+
+ private ushort _x;
+ private ushort _y;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PlayerWalkCommandHandler"/> class.
+ /// </summary>
+ /// <param name="playerManagerBinding">The character object binding.</param>
+ /// <param name="nostaleClient">The nostale client.</param>
+ /// <param name="options">The options.</param>
+ public PlayerWalkCommandHandler
+ (
+ PlayerManagerBinding playerManagerBinding,
+ INostaleClient nostaleClient,
+ IOptions<WalkCommandHandlerOptions> options
+ )
+ {
+ _options = options.Value;
+ _playerManagerBinding = playerManagerBinding;
+ _nostaleClient = nostaleClient;
+ }
+
+ /// <inheritdoc/>
+ /// 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<Result> 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
+ (
+ PlayerWalkControlGroup,
+ 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(command.TargetX, command.TargetY))
+ {
+ reason = WalkUnfinishedReason.PathNotFound;
+ }
+
+ if (reason is null)
+ {
+ return Result.FromSuccess();
+ }
+
+ return new WalkNotFinishedError
+ (
+ _playerManagerBinding.PlayerManager.X,
+ _playerManagerBinding.PlayerManager.Y,
+ (WalkUnfinishedReason)reason
+ );
+ }
+
+ 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<Result> 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
M Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkCommandHandlerOptions.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkCommandHandlerOptions.cs +3 -11
@@ 7,25 7,17 @@
namespace NosSmooth.LocalClient.CommandHandlers.Walk
{
/// <summary>
- /// Options for <see cref="WalkCommandHandler"/>.
+ /// Options for <see cref="PlayerWalkCommandHandler"/>.
/// </summary>
public class WalkCommandHandlerOptions
{
/// <summary>
- /// After what time to trigger not walking for too long error in milliseconds.
- /// </summary>
- /// <remarks>
- /// Use at least 2000 to avoid problems with false triggers.
- /// </remarks>
- public int NotWalkingTooLongTrigger { get; set; } = 2000;
-
- /// <summary>
- /// The command handler sleeps for this duration, then checks new info in milliseconds.
+ /// The command handler sleeps for this duration, then checks for new info. Unit is milliseconds.
/// </summary>
/// <remarks>
/// The operation is done with a cancellation token, if there is an outer event, then it can be faster.
/// Walk function is called again as well after this delay.
/// </remarks>
- public int CheckDelay { get; set; } = 100;
+ public int CheckDelay { get; set; } = 50;
}
}
D Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkPacketResponder.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkPacketResponder.cs +0 -58
@@ 1,58 0,0 @@
-//
-// WalkPacketResponder.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.Commands;
-using NosSmooth.Core.Packets;
-using NosSmooth.Packets.Client.Movement;
-using NosSmooth.Packets.Server.Maps;
-using Remora.Results;
-
-namespace NosSmooth.LocalClient.CommandHandlers.Walk;
-
-/// <summary>
-/// Responds to <see cref="WalkPacket"/> to manage <see cref="WalkCommand"/>.
-/// </summary>
-public class WalkPacketResponder : IPacketResponder<WalkPacket>, IPacketResponder<CMapPacket>
-{
- private readonly WalkStatus _walkStatus;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WalkPacketResponder"/> class.
- /// </summary>
- /// <param name="walkStatus">The walk status.</param>
- public WalkPacketResponder(WalkStatus walkStatus)
- {
- _walkStatus = walkStatus;
- }
-
- /// <inheritdoc />
- public async Task<Result> Respond(PacketEventArgs<WalkPacket> packet, CancellationToken ct = default)
- {
- if (_walkStatus.IsWalking)
- {
- _walkStatus.UpdateWalkTime(packet.Packet.PositionX, packet.Packet.PositionY);
- if (packet.Packet.PositionX == _walkStatus.TargetX && packet.Packet.PositionY == _walkStatus.TargetY)
- {
- await _walkStatus.FinishWalkingAsync(ct);
- }
- }
-
- return Result.FromSuccess();
- }
-
- /// <inheritdoc />
- public async Task<Result> Respond(PacketEventArgs<CMapPacket> packet, CancellationToken ct = default)
- {
- if (_walkStatus.IsWalking)
- {
- await _walkStatus.CancelWalkingAsync(WalkCancelReason.MapChanged, ct);
- }
-
- return Result.FromSuccess();
- }
-
- // TODO: handle teleport on map
-}
D Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkStatus.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkStatus.cs +0 -177
@@ 1,177 0,0 @@
-//
-// WalkStatus.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.LocalBinding.Objects;
-
-namespace NosSmooth.LocalClient.CommandHandlers.Walk;
-
-/// <summary>
-/// The status for <see cref="WalkCommandHandler"/>.
-/// </summary>
-public class WalkStatus
-{
- private readonly PlayerManagerBinding _playerManagerBinding;
- private readonly SemaphoreSlim _semaphore;
- private CancellationTokenSource? _walkingCancellation;
- private bool _userCanCancel;
- private bool _walkHooked;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WalkStatus"/> class.
- /// </summary>
- /// <param name="playerManagerBinding">The character binding.</param>
- public WalkStatus(PlayerManagerBinding playerManagerBinding)
- {
- _playerManagerBinding = playerManagerBinding;
- _semaphore = new SemaphoreSlim(1, 1);
- }
-
- /// <summary>
- /// Gets if the walk command is in progress.
- /// </summary>
- public bool IsWalking => _walkingCancellation is not null;
-
- /// <summary>
- /// Gets if the current walk command has been finished.
- /// </summary>
- public bool IsFinished { get; private set; }
-
- /// <summary>
- /// Gets the last time of walk.
- /// </summary>
- public DateTimeOffset LastWalkTime { get; private set; }
-
- /// <summary>
- /// Gets the walk target x coordinate.
- /// </summary>
- public int TargetX { get; private set; }
-
- /// <summary>
- /// Gets the walk target y coordinate.
- /// </summary>
- public int TargetY { get; private set; }
-
- /// <summary>
- /// Gets the characters current x coordinate.
- /// </summary>
- public int? CurrentX { get; private set; }
-
- /// <summary>
- /// Gets the characters current y coordinate.
- /// </summary>
- public int? CurrentY { get; private set; }
-
- /// <summary>
- /// Gets the error cause of cancellation.
- /// </summary>
- public WalkCancelReason? Error { get; private set; }
-
- /// <summary>
- /// Update the time of last walk, called on WalkPacket.
- /// </summary>
- /// <param name="currentX">The current characters x coordinate.</param>
- /// <param name="currentY">The current characters y coordinate.</param>
- internal void UpdateWalkTime(int currentX, int currentY)
- {
- CurrentX = currentX;
- CurrentY = currentY;
- LastWalkTime = DateTimeOffset.Now;
- }
-
- /// <summary>
- /// Sets that the walk command handler is handling walk command.
- /// </summary>
- /// <param name="cancellationTokenSource">The cancellation token source for cancelling the operation.</param>
- /// <param name="targetX">The walk target x coordinate.</param>
- /// <param name="targetY">The walk target y coordinate.</param>
- /// <param name="userCanCancel">Whether the user can cancel the operation by moving elsewhere.</param>
- /// <returns>A task that may or may not have succeeded.</returns>
- internal async Task SetWalking(CancellationTokenSource cancellationTokenSource, int targetX, int targetY, bool userCanCancel)
- {
- await _semaphore.WaitAsync(cancellationTokenSource.Token);
- if (IsWalking)
- {
- // Cannot call CancelWalkingAsync as that would result in a deadlock
- _walkingCancellation?.Cancel();
- _walkingCancellation = null;
- }
-
- IsFinished = false;
- Error = null;
- TargetX = targetX;
- TargetY = targetY;
- CurrentX = CurrentY = null;
- _walkingCancellation = cancellationTokenSource;
- LastWalkTime = DateTime.Now;
- _userCanCancel = userCanCancel;
-
- if (!_walkHooked)
- {
- _playerManagerBinding.WalkCall += OnCharacterWalked;
- _walkHooked = true;
- }
-
- _semaphore.Release();
- }
-
- /// <summary>
- /// Cancel the walking token.
- /// </summary>
- /// <param name="error">The cause.</param>
- /// <param name="ct">The cancellation token for cancelling the operation.</param>
- /// <returns>A task that may or may not have succeeded.</returns>
- internal async Task CancelWalkingAsync(WalkCancelReason? error = null, CancellationToken ct = default)
- {
- await _semaphore.WaitAsync(ct);
- if (!IsWalking)
- {
- _semaphore.Release();
- return;
- }
-
- Error = error;
- try
- {
- _walkingCancellation?.Cancel();
- }
- catch
- {
- // ignored
- }
-
- _walkingCancellation = null;
- _semaphore.Release();
- }
-
- /// <summary>
- /// Finish the walk successfully.
- /// </summary>
- /// <param name="ct">The cancellation token for cancelling the operation.</param>
- /// <returns>A task that may or may not have succeeded.</returns>
- internal async Task FinishWalkingAsync(CancellationToken ct = default)
- {
- await _semaphore.WaitAsync(ct);
- IsFinished = true;
- _semaphore.Release();
- await CancelWalkingAsync(ct: ct);
- }
-
- private bool OnCharacterWalked(ushort x, ushort y)
- {
- if (IsWalking)
- {
- if (!_userCanCancel)
- {
- return false;
- }
-
- CancelWalkingAsync(WalkCancelReason.UserWalked)
- .GetAwaiter()
- .GetResult();
- }
- return true;
- }
-}>
\ No newline at end of file
R Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkCancelReason.cs => Local/NosSmooth.LocalClient/CommandHandlers/Walk/WalkUnfinishedReason.cs +35 -42
@@ 1,43 1,36 @@
-//
-// WalkCancelReason.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.Commands;
-
-namespace NosSmooth.LocalClient.CommandHandlers.Walk;
-
-/// <summary>
-/// Reason of cancellation of <see cref="WalkCommand"/>.
-/// </summary>
-public enum WalkCancelReason
-{
- /// <summary>
- /// There was an unknown cancel reason.
- /// </summary>
- Unknown,
-
- /// <summary>
- /// The user walked and CancelOnUserMove flag was set.
- /// </summary>
- UserWalked,
-
- /// <summary>
- /// The map has changed during the walk was in progress.
- /// </summary>
- MapChanged,
-
- /// <summary>
- /// The client was not walking for too long.
- /// </summary>
- NotWalkingTooLong,
-
- /// <summary>
- /// The nostale walk function has returned false.
- /// </summary>
- /// <remarks>
- /// The player may be stunned or immobile.
- /// </remarks>
- NosTaleReturnedFalse
+//
+// WalkUnfinishedReason.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 NosSmooth.LocalClient.CommandHandlers.Walk;
+
+/// <summary>
+/// Reason for not finishing a walk.
+/// </summary>
+public enum WalkUnfinishedReason
+{
+ /// <summary>
+ /// There was an unknown unfinished reason.
+ /// </summary>
+ Unknown,
+
+ /// <summary>
+ /// The client could not find path to the given location.
+ /// </summary>
+ /// <remarks>
+ /// The user walked just some part of the path.
+ /// </remarks>
+ PathNotFound,
+
+ /// <summary>
+ /// The user has took an action that has cancelled the walk.
+ /// </summary>
+ UserAction,
+
+ /// <summary>
+ /// There was another walk action that cancelled this one.
+ /// </summary>
+ AnotherTask
}=
\ No newline at end of file
M Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs => Local/NosSmooth.LocalClient/Extensions/ServiceCollectionExtensions.cs +2 -3
@@ 28,9 28,8 @@ public static class ServiceCollectionExtensions
serviceCollection.AddNostaleCore();
serviceCollection.AddNostaleBindings();
serviceCollection
- .AddCommandHandler<WalkCommandHandler>()
- .AddPacketResponder<WalkPacketResponder>()
- .AddSingleton<WalkStatus>();
+ .AddTakeControlCommand()
+ .AddCommandHandler<PlayerWalkCommandHandler>();
serviceCollection.TryAddSingleton<NostaleLocalClient>();
serviceCollection.TryAddSingleton<INostaleClient>(p => p.GetRequiredService<NostaleLocalClient>());
M Local/NosSmooth.LocalClient/NosSmooth.LocalClient.csproj => Local/NosSmooth.LocalClient/NosSmooth.LocalClient.csproj +0 -4
@@ 18,8 18,4 @@
<PackageReference Include="Reloaded.Memory.Sigscan" Version="1.2.1" />
</ItemGroup>
- <ItemGroup>
- <Folder Include="Bindings" />
- </ItemGroup>
-
</Project>
M Local/NosSmooth.LocalClient/NostaleLocalClient.cs => Local/NosSmooth.LocalClient/NostaleLocalClient.cs +31 -1
@@ 9,9 9,11 @@ using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NosSmooth.Core.Client;
using NosSmooth.Core.Commands;
+using NosSmooth.Core.Commands.Control;
using NosSmooth.Core.Extensions;
using NosSmooth.Core.Packets;
using NosSmooth.LocalBinding.Objects;
+using NosSmooth.LocalBinding.Structs;
using NosSmooth.Packets;
using NosSmooth.Packets.Errors;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
@@ 29,6 31,8 @@ namespace NosSmooth.LocalClient;
public class NostaleLocalClient : BaseNostaleClient
{
private readonly NetworkBinding _networkBinding;
+ private readonly PlayerManagerBinding _playerManagerBinding;
+ private readonly ControlCommands _controlCommands;
private readonly IPacketSerializer _packetSerializer;
private readonly IPacketHandler _packetHandler;
private readonly ILogger _logger;
@@ 41,6 45,8 @@ public class NostaleLocalClient : BaseNostaleClient
/// Initializes a new instance of the <see cref="NostaleLocalClient"/> class.
/// </summary>
/// <param name="networkBinding">The network binding.</param>
+ /// <param name="playerManagerBinding">The player manager binding.</param>
+ /// <param name="controlCommands">The control commands.</param>
/// <param name="commandProcessor">The command processor.</param>
/// <param name="packetSerializer">The packet serializer.</param>
/// <param name="packetHandler">The packet handler.</param>
@@ 50,6 56,8 @@ public class NostaleLocalClient : BaseNostaleClient
public NostaleLocalClient
(
NetworkBinding networkBinding,
+ PlayerManagerBinding playerManagerBinding,
+ ControlCommands controlCommands,
CommandProcessor commandProcessor,
IPacketSerializer packetSerializer,
IPacketHandler packetHandler,
@@ 61,6 69,8 @@ public class NostaleLocalClient : BaseNostaleClient
{
_options = options.Value;
_networkBinding = networkBinding;
+ _playerManagerBinding = playerManagerBinding;
+ _controlCommands = controlCommands;
_packetSerializer = packetSerializer;
_packetHandler = packetHandler;
_logger = logger;
@@ 75,6 85,9 @@ public class NostaleLocalClient : BaseNostaleClient
_networkBinding.PacketSend += SendCallback;
_networkBinding.PacketReceive += ReceiveCallback;
+ _playerManagerBinding.FollowEntityCall += FollowEntity;
+ _playerManagerBinding.WalkCall += Walk;
+
try
{
await Task.Delay(-1, stopRequested);
@@ 86,6 99,8 @@ public class NostaleLocalClient : BaseNostaleClient
_networkBinding.PacketSend -= SendCallback;
_networkBinding.PacketReceive -= ReceiveCallback;
+ _playerManagerBinding.FollowEntityCall -= FollowEntity;
+ _playerManagerBinding.WalkCall -= Walk;
return Result.FromSuccess();
}
@@ 183,4 198,19 @@ public class NostaleLocalClient : BaseNostaleClient
_logger.LogResultError(result);
}
}
-}
+
+ private bool FollowEntity(MapBaseObj? obj)
+ {
+ Task.Run
+ (
+ async () => await _controlCommands.CancelAsync
+ (ControlCommandsFilter.UserCancellable, false, (CancellationToken)_stopRequested!)
+ );
+ return true;
+ }
+
+ private bool Walk(ushort x, ushort y)
+ {
+ return _controlCommands.AllowUserActions;
+ }
+}<
\ No newline at end of file