M Extensions/NosSmooth.Extensions.Combat/CombatManager.cs => Extensions/NosSmooth.Extensions.Combat/CombatManager.cs +53 -22
@@ 20,7 20,7 @@ namespace NosSmooth.Extensions.Combat;
public class CombatManager : IStatefulEntity
{
private readonly List<CancellationTokenSource> _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
/// <param name="game">The game.</param>
public CombatManager(INostaleClient client, Game.Game game)
{
- _semaphore = new Semaphore(1, 1);
+ _semaphore = new SemaphoreSlim(1, 1);
_tokenSource = new List<CancellationTokenSource>();
_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.
/// </summary>
/// <param name="tokenSource">The token source to register.</param>
- public void RegisterSkillCancellationToken(CancellationTokenSource tokenSource)
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A task.</returns>
+ 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();
+ }
}
/// <summary>
/// Unregister the given cancellation token registered using <see cref="RegisterSkillCancellationToken"/>.
/// </summary>
/// <param name="tokenSource">The token source to unregister.</param>
- public void UnregisterSkillCancellationToken(CancellationTokenSource tokenSource)
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A task.</returns>
+ 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();
+ }
}
/// <summary>
/// Cancel all of the skill tokens.
/// </summary>
- internal void CancelSkillTokens()
+ /// <param name="ct">The cancellation token for cancelling the operation.</param>
+ /// <returns>A task.</returns>
+ 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
M Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs => Extensions/NosSmooth.Extensions.Combat/Operations/UseSkillOperation.cs +12 -4
@@ 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();
}
M Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs => Extensions/NosSmooth.Extensions.Combat/Responders/CancelResponder.cs +1 -1
@@ 29,7 29,7 @@ public class CancelResponder : IPacketResponder<CancelPacket>
/// <inheritdoc />
public Task<Result> Respond(PacketEventArgs<CancelPacket> packetArgs, CancellationToken ct = default)
{
- _combatManager.CancelSkillTokens();
+ _combatManager.CancelSkillTokensAsync(ct);
return Task.FromResult(Result.FromSuccess());
}
}=
\ No newline at end of file
M Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs => Extensions/NosSmooth.Extensions.Combat/Responders/SuResponder.cs +1 -1
@@ 29,7 29,7 @@ public class SuResponder : IPacketResponder<SuPacket>
/// <inheritdoc />
public Task<Result> Respond(PacketEventArgs<SuPacket> packetArgs, CancellationToken ct = default)
{
- _combatManager.CancelSkillTokens();
+ _combatManager.CancelSkillTokensAsync(ct);
return Task.FromResult(Result.FromSuccess());
}
}=
\ No newline at end of file