From dc2080b1de88b6aa27fd4065923f319801abdc49 Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Thu, 30 May 2024 08:05:02 +0200 Subject: [PATCH 1/3] Improve StartForAsync and add tests --- .../Integration/Elements/PlayerTests.cs | 47 +++++++++++++++++++ SlipeServer.Server/Resources/Resource.cs | 37 +++++++++------ 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs index d1aff570..ac7acc81 100644 --- a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs +++ b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs @@ -4,7 +4,11 @@ using SlipeServer.Server.Loggers; using SlipeServer.Server.PacketHandling.Handlers.Player; using SlipeServer.Server.PacketHandling.Handlers.QueueHandlers; +using SlipeServer.Server.Resources; using SlipeServer.Server.TestTools; +using System; +using System.Threading; +using System.Threading.Tasks; using Xunit; namespace SlipeServer.Server.Tests.Integration.Elements; @@ -55,4 +59,47 @@ public void KickingPlayerShouldDestroyAndDisconnectPlayer() player.IsDestroyed.Should().BeTrue(); player.Client.IsConnected.Should().BeFalse(); } + + [Fact] + public async Task StartForAsyncShouldBeCancellable() + { + var server = new TestingServer(); + var player = server.AddFakePlayer(); + + var resource = new Resource(server, server.RootElement, "test"); + + var cts = new CancellationTokenSource(); + cts.Cancel(); + + var act = async () => await resource.StartForAsync(player, cts.Token); + + await act.Should().ThrowAsync(); + } + + [Fact] + public async Task StartForAsyncShouldBeCancellable2() + { + var server = new TestingServer(); + var player = server.AddFakePlayer(); + + var resource = new Resource(server, server.RootElement, "test"); + + var cts = new CancellationTokenSource(); + + var act = async () => await resource.StartForAsync(player, cts.Token); + + var waitHandle = new ManualResetEvent(false); + var cancelled = false; + var _ = Task.Run(async () => + { + await act.Should().ThrowAsync(); + waitHandle.Set(); + cancelled = true; + }); + + await Task.Delay(200); // Let StartForAsync execute for a while to block on "await source.Task" + await cts.CancelAsync(); + waitHandle.WaitOne(TimeSpan.FromSeconds(5)); + cancelled.Should().BeTrue(); + } } diff --git a/SlipeServer.Server/Resources/Resource.cs b/SlipeServer.Server/Resources/Resource.cs index cb6a84f9..654357d9 100644 --- a/SlipeServer.Server/Resources/Resource.cs +++ b/SlipeServer.Server/Resources/Resource.cs @@ -3,6 +3,7 @@ using SlipeServer.Server.Elements; using SlipeServer.Server.Elements.Events; using SlipeServer.Server.Extensions; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -83,16 +84,18 @@ public void StartFor(Player player) .SendTo(player); } - public Task StartForAsync(Player player, CancellationToken cancelationToken = default) + public async Task StartForAsync(Player player, CancellationToken cancelationToken = default) { - cancelationToken.ThrowIfCancellationRequested(); + if (player.IsDestroyed || !player.Client.IsConnected) + throw new InvalidOperationException("Player is not connected to the server."); + using var cts = new CancellationTokenSource(300_000); + using var cts2 = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancelationToken); var source = new TaskCompletionSource(); - cancelationToken.Register(() => + cts2.Token.Register(() => { - player.ResourceStarted -= HandleResourceStart; - player.Disconnected -= HandlePlayerDisconnected; + source.SetException(new TaskCanceledException()); }); player.ResourceStarted += HandleResourceStart; @@ -100,29 +103,33 @@ public Task StartForAsync(Player player, CancellationToken cancelationToken = de void HandleResourceStart(Player sender, PlayerResourceStartedEventArgs e) { - if (e.NetId != this.NetId) + if (e.NetId != this.NetId || cts2.Token.IsCancellationRequested) return; - player.ResourceStarted -= HandleResourceStart; - player.Disconnected -= HandlePlayerDisconnected; - source.SetResult(); } void HandlePlayerDisconnected(Player disconnectingPlayer, PlayerQuitEventArgs e) { - if(player != disconnectingPlayer) + if(player != disconnectingPlayer || cts2.Token.IsCancellationRequested) return; - player.ResourceStarted -= HandleResourceStart; - player.Disconnected -= HandlePlayerDisconnected; - - source.SetException(new System.Exception("Player disconnected.")); + source.SetException(new Exception("Player disconnected.")); } + cts2.Token.ThrowIfCancellationRequested(); + StartFor(player); - return source.Task; + try + { + await source.Task; + } + finally + { + player.ResourceStarted -= HandleResourceStart; + player.Disconnected -= HandlePlayerDisconnected; + } } public void StopFor(Player player) From 6b6ba16cd6c15c1d8ee86538ca909b3f2a4b785f Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Thu, 30 May 2024 08:20:13 +0200 Subject: [PATCH 2/3] Addendum --- .../Integration/Elements/PlayerTests.cs | 29 ++++++++++++++++++- SlipeServer.Server/Resources/Resource.cs | 16 ++++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs index ac7acc81..00c779b5 100644 --- a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs +++ b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs @@ -73,7 +73,7 @@ public async Task StartForAsyncShouldBeCancellable() var act = async () => await resource.StartForAsync(player, cts.Token); - await act.Should().ThrowAsync(); + await act.Should().ThrowAsync(); } [Fact] @@ -102,4 +102,31 @@ public async Task StartForAsyncShouldBeCancellable2() waitHandle.WaitOne(TimeSpan.FromSeconds(5)); cancelled.Should().BeTrue(); } + + [Fact] + public async Task ResourceShouldStopStartingWhenPlayerDisconnectFromTheServer() + { + var server = new TestingServer(); + var player = server.AddFakePlayer(); + + var resource = new Resource(server, server.RootElement, "test"); + + var cts = new CancellationTokenSource(); + + var act = async () => await resource.StartForAsync(player, cts.Token); + + var waitHandle = new ManualResetEvent(false); + var cancelled = false; + var _ = Task.Run(async () => + { + await act.Should().ThrowAsync(); + waitHandle.Set(); + cancelled = true; + }); + + await Task.Delay(200); // Let StartForAsync execute for a while to block on "await source.Task" + player.Kick(); + waitHandle.WaitOne(TimeSpan.FromSeconds(5)); + cancelled.Should().BeTrue(); + } } diff --git a/SlipeServer.Server/Resources/Resource.cs b/SlipeServer.Server/Resources/Resource.cs index 654357d9..3281251d 100644 --- a/SlipeServer.Server/Resources/Resource.cs +++ b/SlipeServer.Server/Resources/Resource.cs @@ -98,9 +98,6 @@ public async Task StartForAsync(Player player, CancellationToken cancelationToke source.SetException(new TaskCanceledException()); }); - player.ResourceStarted += HandleResourceStart; - player.Disconnected += HandlePlayerDisconnected; - void HandleResourceStart(Player sender, PlayerResourceStartedEventArgs e) { if (e.NetId != this.NetId || cts2.Token.IsCancellationRequested) @@ -117,8 +114,20 @@ void HandlePlayerDisconnected(Player disconnectingPlayer, PlayerQuitEventArgs e) source.SetException(new Exception("Player disconnected.")); } + void HandleDestroed(Element destroyedElement) + { + if(player != destroyedElement || cts2.Token.IsCancellationRequested) + return; + + source.SetException(new Exception("Player destroyed.")); + } + cts2.Token.ThrowIfCancellationRequested(); + player.ResourceStarted += HandleResourceStart; + player.Disconnected += HandlePlayerDisconnected; + player.Destroyed += HandleDestroed; + StartFor(player); try @@ -129,6 +138,7 @@ void HandlePlayerDisconnected(Player disconnectingPlayer, PlayerQuitEventArgs e) { player.ResourceStarted -= HandleResourceStart; player.Disconnected -= HandlePlayerDisconnected; + player.Destroyed -= HandleDestroed; } } From 91baa6ddd2038249cfc382417e9a793407deae61 Mon Sep 17 00:00:00 2001 From: Sebastian Jura <22455534+CrosRoad95@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:04:40 +0200 Subject: [PATCH 3/3] Increase wait time to two seconds --- SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs index 00c779b5..d980e055 100644 --- a/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs +++ b/SlipeServer.Server.Tests/Integration/Elements/PlayerTests.cs @@ -97,7 +97,7 @@ public async Task StartForAsyncShouldBeCancellable2() cancelled = true; }); - await Task.Delay(200); // Let StartForAsync execute for a while to block on "await source.Task" + await Task.Delay(2000); // Let StartForAsync execute for a while to block on "await source.Task" await cts.CancelAsync(); waitHandle.WaitOne(TimeSpan.FromSeconds(5)); cancelled.Should().BeTrue(); @@ -124,7 +124,7 @@ public async Task ResourceShouldStopStartingWhenPlayerDisconnectFromTheServer() cancelled = true; }); - await Task.Delay(200); // Let StartForAsync execute for a while to block on "await source.Task" + await Task.Delay(2000); // Let StartForAsync execute for a while to block on "await source.Task" player.Kick(); waitHandle.WaitOne(TimeSpan.FromSeconds(5)); cancelled.Should().BeTrue();