Skip to content

Commit 5a61f8e

Browse files
committed
update lobby scene
1 parent 486e884 commit 5a61f8e

5 files changed

Lines changed: 94 additions & 80 deletions

File tree

samples/SpaceWar.Lobby/Models/User.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@ public sealed class User
77
public required Guid PeerId { get; init; }
88
public required Guid Token { get; init; }
99
public required string Username { get; init; }
10-
public required string LobbyName { get; init; }
1110
public required IPAddress IP { get; init; }
1211
}

samples/SpaceWar.Lobby/Scenes/LobbyScene.cs

Lines changed: 44 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public sealed class LobbyScene(PlayerMode mode) : Scene
2222
PlayerMode mode = mode;
2323

2424
readonly TimeSpan refreshInterval = TimeSpan.FromSeconds(2);
25-
readonly TimeSpan pingInterval = TimeSpan.FromMilliseconds(300);
25+
readonly TimeSpan handShakeInterval = TimeSpan.FromMilliseconds(300);
2626
readonly KeyboardController keyboard = new();
2727
readonly CancellationTokenSource cts = new();
2828

@@ -33,8 +33,7 @@ public override void Initialize()
3333
lobbyUdpClient = new(Config.LocalPort, Config.ServerUrl, Config.ServerUdpPort);
3434
keyboard.Update();
3535

36-
StartPingTimer();
37-
StartLobbyRefreshTimer();
36+
_ = StartTimers();
3837
}
3938

4039
public override void Update(GameTime gameTime)
@@ -59,7 +58,7 @@ async Task ToggleReady()
5958
{
6059
if (!AllReachable()) return;
6160

62-
await client.ToggleReady();
61+
await client.ToggleReady(cts.Token);
6362
ready = true;
6463
}
6564

@@ -295,7 +294,7 @@ void DrawError(SpriteBatch spriteBatch)
295294

296295
async Task RequestLobby()
297296
{
298-
user = await client.EnterLobby(Config.LobbyName, Config.Username, mode);
297+
user = await client.EnterLobby(Config.LobbyName, Config.Username, mode, cts.Token);
299298
await RefreshLobby();
300299

301300
if (Array.Exists(lobbyInfo.Spectators, s => s.PeerId == user.PeerId))
@@ -306,13 +305,20 @@ async Task RequestLobby()
306305

307306
async Task RefreshLobby()
308307
{
309-
lobbyInfo = await client.GetLobby();
308+
if (await client.GetLobby(cts.Token) is not { } lobby)
309+
{
310+
SetError("Can't find Lobby...");
311+
await Task.Delay(TimeSpan.FromSeconds(1.5));
312+
lobbyInfo = null;
313+
LoadScene(new ChooseLobbyScene());
314+
return;
315+
}
310316

317+
lobbyInfo = lobby;
311318
await lobbyUdpClient.HandShake(user);
312319

313320
if (connected) return;
314-
connected = lobbyInfo.Players.SingleOrDefault(x => x.PeerId == user.PeerId) is
315-
{ Connected: true };
321+
connected = lobbyInfo.Players.SingleOrDefault(x => x.PeerId == user.PeerId) is { Connected: true };
316322
}
317323

318324
void CheckPlayersReady()
@@ -416,90 +422,63 @@ NetcodeSessionBuilder<PlayerInputs> NetcodeSessionBuilder() =>
416422

417423
bool PendingNetworkCall()
418424
{
419-
if (networkCall is null)
420-
return false;
421-
422-
if (!networkCall.IsCompleted)
423-
return true;
424-
425-
if (networkCall.IsFaulted)
426-
SetError(networkCall.Exception);
425+
if (networkCall is null) return false;
426+
if (!networkCall.IsCompleted) return true;
427+
if (networkCall is { IsFaulted: true, Exception.InnerException: not TaskCanceledException })
428+
SetError(networkCall.Exception.InnerException);
427429

428430
networkCall = null;
429431
return true;
430432
}
431433

432-
void SetError(Exception ex)
434+
void SetError(Exception ex) => SetError(ex.ToString());
435+
436+
void SetError(string message)
433437
{
434438
currentState = LobbyState.Error;
435-
errorMessage =
436-
ex?.InnerException?.Message
437-
?? networkCall.Exception?.Message;
439+
errorMessage = message;
438440
}
439441

440-
public void StartPingTimer() => Task.Run(async () =>
442+
async Task StartTimers()
441443
{
442-
using PeriodicTimer timer = new(pingInterval);
443-
444-
try
445-
{
446-
while (await timer.WaitForNextTickAsync(cts.Token))
444+
var handShakeTimer = AsyncTimer.Create(handShakeInterval, async () =>
447445
{
448-
if (lobbyInfo is null || lobbyInfo.Ready) continue;
449-
await Task.WhenAll(
450-
lobbyUdpClient.Ping(user, lobbyInfo.Players, cts.Token),
451-
lobbyUdpClient.Ping(user, lobbyInfo.Spectators, cts.Token)
452-
);
453-
}
454-
}
455-
catch (OperationCanceledException)
456-
{
457-
// skip
458-
}
459-
catch (Exception ex)
460-
{
461-
SetError(ex);
462-
}
463-
});
446+
if (lobbyInfo is not null && !lobbyInfo.Ready)
447+
await Task.WhenAll(
448+
lobbyUdpClient.HandShake(user, lobbyInfo.Players, cts.Token),
449+
lobbyUdpClient.HandShake(user, lobbyInfo.Spectators, cts.Token)
450+
);
451+
},
452+
SetError, cts.Token);
453+
454+
var refreshTimer = AsyncTimer.Create(refreshInterval, async () =>
455+
{
456+
if (currentState is LobbyState.Waiting)
457+
await RefreshLobby();
458+
},
459+
SetError, cts.Token);
464460

465-
public void StartLobbyRefreshTimer() => Task.Run(async () =>
466-
{
467-
using PeriodicTimer timer = new(refreshInterval);
468461

469-
try
470-
{
471-
while (await timer.WaitForNextTickAsync(cts.Token))
472-
{
473-
if (currentState is not LobbyState.Waiting) continue;
474-
await RefreshLobby();
475-
}
476-
}
477-
catch (OperationCanceledException)
478-
{
479-
// skip
480-
}
481-
catch (Exception ex)
482-
{
483-
SetError(ex);
484-
}
485-
});
462+
await Task.Run(() => Task.WhenAll(handShakeTimer, refreshTimer), cts.Token);
463+
}
486464

487465
protected override void Dispose(bool disposing)
488466
{
489467
try
490468
{
469+
if (user is not null && lobbyInfo is { Ready: false })
470+
client.LeaveLobby(cts.Token).GetAwaiter().GetResult();
471+
491472
cts.Dispose();
492473
lobbyUdpClient.Dispose();
493-
if (user is not null && lobbyInfo is { Ready: false })
494-
client.LeaveLobby().GetAwaiter().GetResult();
495474
}
496475
catch (Exception e)
497476
{
498477
Console.WriteLine($"Error leaving lobby: {e}");
499478
}
500479
}
501480

502-
public enum LobbyState
481+
enum LobbyState
503482
{
504483
Loading,
505484
Waiting,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace SpaceWar.Services;
2+
3+
public static class AsyncTimer
4+
{
5+
public static async Task Create(
6+
TimeSpan interval,
7+
Func<Task> main,
8+
Action<Exception>? errorHandler = null,
9+
CancellationToken ct = default
10+
)
11+
{
12+
ArgumentNullException.ThrowIfNull(main);
13+
using PeriodicTimer timer = new(interval);
14+
15+
try
16+
{
17+
while (await timer.WaitForNextTickAsync(ct))
18+
await main.Invoke();
19+
}
20+
catch (OperationCanceledException)
21+
{
22+
// skip
23+
}
24+
catch (Exception ex)
25+
{
26+
errorHandler?.Invoke(ex);
27+
}
28+
}
29+
}

samples/SpaceWar.Lobby/Services/LobbyHttpClient.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,53 +27,60 @@ public sealed class LobbyHttpClient(AppSettings appSettings)
2727

2828
readonly Guid recreationKey = Guid.NewGuid();
2929

30-
public async Task<User> EnterLobby(string lobbyName, string username, PlayerMode mode)
30+
public async Task<User> EnterLobby(
31+
string lobbyName, string username, PlayerMode mode,
32+
CancellationToken ct = default
33+
)
3134
{
32-
var localEndpoint = await GetLocalEndpoint();
35+
var localEndpoint = await GetLocalEndpoint(ct);
3336
var response = await client.PostAsJsonAsync($"/{appSettings.GameId}/lobby", new
3437
{
3538
lobbyName,
3639
username,
3740
mode,
3841
localEndpoint,
3942
recreationKey,
40-
}, jsonOptions);
43+
}, jsonOptions, ct);
4144

4245
if (response.StatusCode is HttpStatusCode.UnprocessableEntity)
4346
throw new InvalidOperationException("Failed to enter lobby");
4447

4548
response.EnsureSuccessStatusCode();
4649

47-
var result = await response.Content.ReadFromJsonAsync<User>(jsonOptions)
50+
var result = await response.Content.ReadFromJsonAsync<User>(jsonOptions, ct)
4851
?? throw new InvalidOperationException();
4952

5053
client.DefaultRequestHeaders.Clear();
5154
client.DefaultRequestHeaders.Add("token", result.Token.ToString());
5255
return result;
5356
}
5457

55-
public async Task<Lobby> GetLobby() =>
56-
await client.GetFromJsonAsync<Lobby>("/entry/lobby", jsonOptions)
57-
?? throw new InvalidOperationException();
58+
public async Task<Lobby?> GetLobby(CancellationToken ct = default)
59+
{
60+
var response = await client.GetAsync("/entry/lobby", ct);
61+
if (response.StatusCode is HttpStatusCode.NotFound) return null;
62+
response.EnsureSuccessStatusCode();
63+
return await response.Content.ReadFromJsonAsync<Lobby>(jsonOptions, ct);
64+
}
5865

59-
public async Task LeaveLobby()
66+
public async Task LeaveLobby(CancellationToken ct = default)
6067
{
61-
var response = await client.DeleteAsync("/entry");
68+
var response = await client.DeleteAsync("/entry", ct);
6269
response.EnsureSuccessStatusCode();
6370
}
6471

65-
public async Task ToggleReady()
72+
public async Task ToggleReady(CancellationToken ct = default)
6673
{
67-
var response = await client.PutAsync("/entry/ready", null);
74+
var response = await client.PutAsync("/entry/ready", null, ct);
6875
response.EnsureSuccessStatusCode();
6976
}
7077

71-
async Task<IPEndPoint?> GetLocalEndpoint()
78+
async Task<IPEndPoint?> GetLocalEndpoint(CancellationToken ct = default)
7279
{
7380
try
7481
{
7582
using Socket socket = new(AddressFamily.InterNetwork, SocketType.Dgram, 0);
76-
await socket.ConnectAsync("8.8.8.8", 65530);
83+
await socket.ConnectAsync("8.8.8.8", 65530, ct);
7784
if (socket.LocalEndPoint is not IPEndPoint { Address: { } ipAddress })
7885
return null;
7986

samples/SpaceWar.Lobby/Services/LobbyUdpClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public async Task HandShake(User user, CancellationToken ct = default)
3232
await socket.SendToAsync(sendBuffer.AsMemory()[..bytesWritten], serverEndpoint, ct);
3333
}
3434

35-
public async Task Ping(User user, Peer[] peers, CancellationToken ct = default)
35+
public async Task HandShake(User user, Peer[] peers, CancellationToken ct = default)
3636
{
3737
if (peers.Length is 0 || !user.PeerId.TryWriteBytes(sendBuffer, true, out var bytesWritten) ||
3838
bytesWritten is 0)

0 commit comments

Comments
 (0)