From 0e0daae17288897ef778a75485048589d8e1e872 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 19 Feb 2022 13:50:38 +0100 Subject: [PATCH] fix(combat): make skill cancellation tokens actions async --- .../CombatManager.cs | 75 +++++++++++++------ .../Operations/UseSkillOperation.cs | 16 +++- .../Responders/CancelResponder.cs | 2 +- .../Responders/SuResponder.cs | 2 +- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs index d59cd8a7a6789aa10fb0c9cf95a80e172105577f..173e92be86bc3e90124e46a42cf7a5c1376743ae 100644 --- a/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs +++ b/Extensions/NosSmooth.Extensions.Combat/CombatManager.cs @@ -20,7 +20,7 @@ namespace NosSmooth.Extensions.Combat; public class CombatManager : IStatefulEntity { private readonly List _tokenSource; - private readonly Semaphore _semaphore; + private readonly SemaphoreSlim _semaphore; private readonly INostaleClient _client; private readonly Game.Game _game; @@ -31,7 +31,7 @@ public class CombatManager : IStatefulEntity /// The game. public CombatManager(INostaleClient client, Game.Game game) { - _semaphore = new Semaphore(1, 1); + _semaphore = new SemaphoreSlim(1, 1); _tokenSource = new List(); _client = client; _game = game; @@ -136,43 +136,74 @@ public class CombatManager : IStatefulEntity /// Register the given cancellation token source to be cancelled on skill use/cancel. /// /// The token source to register. - public void RegisterSkillCancellationToken(CancellationTokenSource tokenSource) + /// The cancellation token for cancelling the operation. + /// A task. + public async Task RegisterSkillCancellationTokenAsync(CancellationTokenSource tokenSource, CancellationToken ct) { - _semaphore.WaitOne(); - _tokenSource.Add(tokenSource); - _semaphore.Release(); + await _semaphore.WaitAsync(ct); + try + { + _tokenSource.Add(tokenSource); + } + finally + { + _semaphore.Release(); + } } /// /// Unregister the given cancellation token registered using . /// /// The token source to unregister. - public void UnregisterSkillCancellationToken(CancellationTokenSource tokenSource) + /// The cancellation token for cancelling the operation. + /// A task. + public async Task UnregisterSkillCancellationTokenAsync(CancellationTokenSource tokenSource, CancellationToken ct) { - _semaphore.WaitOne(); - _tokenSource.Remove(tokenSource); - _semaphore.Release(); + if (_cancelling) + { + return; + } + + await _semaphore.WaitAsync(ct); + try + { + _tokenSource.Remove(tokenSource); + } + finally + { + _semaphore.Release(); + } } /// /// Cancel all of the skill tokens. /// - internal void CancelSkillTokens() + /// The cancellation token for cancelling the operation. + /// A task. + internal async Task CancelSkillTokensAsync(CancellationToken ct) { - _semaphore.WaitOne(); - foreach (var tokenSource in _tokenSource) + await _semaphore.WaitAsync(ct); + _cancelling = true; + try { - try - { - tokenSource.Cancel(); - } - catch + foreach (var tokenSource in _tokenSource) { - // ignored + try + { + tokenSource.Cancel(); + } + catch + { + // ignored + } } - } - _tokenSource.Clear(); - _semaphore.Release(); + _tokenSource.Clear(); + } + finally + { + _cancelling = false; + _semaphore.Release(); + } } } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs index cec4553caf58eaa4e8bfbcac027351cf38374d8e..ff571a6639738ebec6e802f380ae6ae205ac021f 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs @@ -49,6 +49,8 @@ public record UseSkillOperation(Skill Skill, ILivingEntity Target) : ICombatOper } // TODO: support for area skills, support skills that use x, y coordinates (like dashes or teleports) + var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(ct); + await combatState.CombatManager.RegisterSkillCancellationTokenAsync(linkedSource, ct); var sendResponse = await combatState.Client.SendPacketAsync ( new UseSkillPacket @@ -64,13 +66,19 @@ public record UseSkillOperation(Skill Skill, ILivingEntity Target) : ICombatOper if (!sendResponse.IsSuccess) { + await combatState.CombatManager.UnregisterSkillCancellationTokenAsync(linkedSource, ct); return sendResponse; } - var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(ct); - combatState.CombatManager.RegisterSkillCancellationToken(linkedSource); - await Task.Delay(Skill.Info.CastTime * 200 * 5, linkedSource.Token); - combatState.CombatManager.UnregisterSkillCancellationToken(linkedSource); + try + { + await Task.Delay(Skill.Info.CastTime * 200 * 5, linkedSource.Token); + } + catch (TaskCanceledException) + { + // ignored + } + await combatState.CombatManager.UnregisterSkillCancellationTokenAsync(linkedSource, ct); return Result.FromSuccess(); } diff --git a/Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs b/Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs index 33a85bd3572aa476f5d02ed9e02f2117b9a62f23..582a58d4f22bf973a7949482ecc6531d0ef97823 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs @@ -29,7 +29,7 @@ public class CancelResponder : IPacketResponder /// public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { - _combatManager.CancelSkillTokens(); + _combatManager.CancelSkillTokensAsync(ct); return Task.FromResult(Result.FromSuccess()); } } \ No newline at end of file diff --git a/Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs b/Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs index 05190aa9c25c5e5bc7c436145073354534b35521..933d0b9f6955a282e8594b329f6caefe2c44b1d1 100644 --- a/Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs +++ b/Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs @@ -29,7 +29,7 @@ public class SuResponder : IPacketResponder /// public Task Respond(PacketEventArgs packetArgs, CancellationToken ct = default) { - _combatManager.CancelSkillTokens(); + _combatManager.CancelSkillTokensAsync(ct); return Task.FromResult(Result.FromSuccess()); } } \ No newline at end of file