Skip to content

Commit 69cb3a6

Browse files
feat: host delete lobby when shutdown [MTT-4798] [MTT-4576] (#772)
* moving lobby error handling responsibility to LobbyServiceFacade * making host delete lobby when leaving
1 parent 586c863 commit 69cb3a6

File tree

3 files changed

+117
-61
lines changed

3 files changed

+117
-61
lines changed
Lines changed: 13 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Threading.Tasks;
4-
using Unity.BossRoom.Infrastructure;
54
using Unity.Services.Lobbies;
65
using Unity.Services.Lobbies.Models;
76
using UnityEngine;
8-
using VContainer;
97

108
namespace Unity.BossRoom.UnityServices.Lobbies
119
{
@@ -16,15 +14,11 @@ public class LobbyAPIInterface
1614
{
1715
const int k_MaxLobbiesToShow = 16; // If more are necessary, consider retrieving paginated results or using filters.
1816

19-
readonly IPublisher<UnityServiceErrorMessage> m_UnityServiceErrorMessagePublisher;
2017
readonly List<QueryFilter> m_Filters;
2118
readonly List<QueryOrder> m_Order;
2219

23-
[Inject]
24-
public LobbyAPIInterface(IPublisher<UnityServiceErrorMessage> unityServiceErrorMessagePublisher)
20+
public LobbyAPIInterface()
2521
{
26-
m_UnityServiceErrorMessagePublisher = unityServiceErrorMessagePublisher;
27-
2822
// Filter for open lobbies only
2923
m_Filters = new List<QueryFilter>()
3024
{
@@ -43,34 +37,6 @@ public LobbyAPIInterface(IPublisher<UnityServiceErrorMessage> unityServiceErrorM
4337
};
4438
}
4539

46-
async Task<T> ExceptionHandling<T>(Task<T> task)
47-
{
48-
try
49-
{
50-
return await task;
51-
}
52-
catch (Exception e)
53-
{
54-
var reason = $"{e.Message} ({e.InnerException?.Message})"; // Lobby error type, then HTTP error type.
55-
m_UnityServiceErrorMessagePublisher.Publish(new UnityServiceErrorMessage("Lobby Error", reason, UnityServiceErrorMessage.Service.Lobby, e));
56-
throw;
57-
}
58-
}
59-
60-
async Task ExceptionHandling(Task task)
61-
{
62-
try
63-
{
64-
await task;
65-
}
66-
catch (Exception e)
67-
{
68-
var reason = $"{e.Message} ({e.InnerException?.Message})"; // Lobby error type, then HTTP error type.
69-
m_UnityServiceErrorMessagePublisher.Publish(new UnityServiceErrorMessage("Lobby Error", reason, UnityServiceErrorMessage.Service.Lobby, e));
70-
throw;
71-
}
72-
}
73-
7440
public async Task<Lobby> CreateLobby(string requesterUasId, string lobbyName, int maxPlayers, bool isPrivate, Dictionary<string, PlayerDataObject> hostUserData, Dictionary<string, DataObject> lobbyData)
7541
{
7642
CreateLobbyOptions createOptions = new CreateLobbyOptions
@@ -80,24 +46,24 @@ public async Task<Lobby> CreateLobby(string requesterUasId, string lobbyName, in
8046
Data = lobbyData
8147
};
8248

83-
return await ExceptionHandling(LobbyService.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createOptions));
49+
return await LobbyService.Instance.CreateLobbyAsync(lobbyName, maxPlayers, createOptions);
8450
}
8551

8652
public async Task DeleteLobby(string lobbyId)
8753
{
88-
await ExceptionHandling(LobbyService.Instance.DeleteLobbyAsync(lobbyId));
54+
await LobbyService.Instance.DeleteLobbyAsync(lobbyId);
8955
}
9056

9157
public async Task<Lobby> JoinLobbyByCode(string requesterUasId, string lobbyCode, Dictionary<string, PlayerDataObject> localUserData)
9258
{
9359
JoinLobbyByCodeOptions joinOptions = new JoinLobbyByCodeOptions { Player = new Player(id: requesterUasId, data: localUserData) };
94-
return await ExceptionHandling(LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, joinOptions));
60+
return await LobbyService.Instance.JoinLobbyByCodeAsync(lobbyCode, joinOptions);
9561
}
9662

9763
public async Task<Lobby> JoinLobbyById(string requesterUasId, string lobbyId, Dictionary<string, PlayerDataObject> localUserData)
9864
{
9965
JoinLobbyByIdOptions joinOptions = new JoinLobbyByIdOptions { Player = new Player(id: requesterUasId, data: localUserData) };
100-
return await ExceptionHandling(LobbyService.Instance.JoinLobbyByIdAsync(lobbyId, joinOptions));
66+
return await LobbyService.Instance.JoinLobbyByIdAsync(lobbyId, joinOptions);
10167
}
10268

10369
public async Task<Lobby> QuickJoinLobby(string requesterUasId, Dictionary<string, PlayerDataObject> localUserData)
@@ -108,19 +74,19 @@ public async Task<Lobby> QuickJoinLobby(string requesterUasId, Dictionary<string
10874
Player = new Player(id: requesterUasId, data: localUserData)
10975
};
11076

111-
return await ExceptionHandling(LobbyService.Instance.QuickJoinLobbyAsync(joinRequest));
77+
return await LobbyService.Instance.QuickJoinLobbyAsync(joinRequest);
11278
}
11379

11480
public async Task<Lobby> ReconnectToLobby(string lobbyId)
11581
{
116-
return await ExceptionHandling(LobbyService.Instance.ReconnectToLobbyAsync(lobbyId));
82+
return await LobbyService.Instance.ReconnectToLobbyAsync(lobbyId);
11783
}
11884

11985
public async Task RemovePlayerFromLobby(string requesterUasId, string lobbyId)
12086
{
12187
try
12288
{
123-
await ExceptionHandling(LobbyService.Instance.RemovePlayerAsync(lobbyId, requesterUasId));
89+
await LobbyService.Instance.RemovePlayerAsync(lobbyId, requesterUasId);
12490
}
12591
catch (LobbyServiceException e)
12692
when (e is { Reason: LobbyExceptionReason.PlayerNotFound })
@@ -138,18 +104,18 @@ public async Task<QueryResponse> QueryAllLobbies()
138104
Order = m_Order
139105
};
140106

141-
return await ExceptionHandling(LobbyService.Instance.QueryLobbiesAsync(queryOptions));
107+
return await LobbyService.Instance.QueryLobbiesAsync(queryOptions);
142108
}
143109

144110
public async Task<Lobby> GetLobby(string lobbyId)
145111
{
146-
return await ExceptionHandling(LobbyService.Instance.GetLobbyAsync(lobbyId));
112+
return await LobbyService.Instance.GetLobbyAsync(lobbyId);
147113
}
148114

149115
public async Task<Lobby> UpdateLobby(string lobbyId, Dictionary<string, DataObject> data, bool shouldLock)
150116
{
151117
UpdateLobbyOptions updateOptions = new UpdateLobbyOptions { Data = data, IsLocked = shouldLock };
152-
return await ExceptionHandling(LobbyService.Instance.UpdateLobbyAsync(lobbyId, updateOptions));
118+
return await LobbyService.Instance.UpdateLobbyAsync(lobbyId, updateOptions);
153119
}
154120

155121
public async Task<Lobby> UpdatePlayer(string lobbyId, string playerId, Dictionary<string, PlayerDataObject> data, string allocationId, string connectionInfo)
@@ -160,12 +126,12 @@ public async Task<Lobby> UpdatePlayer(string lobbyId, string playerId, Dictionar
160126
AllocationId = allocationId,
161127
ConnectionInfo = connectionInfo
162128
};
163-
return await ExceptionHandling(LobbyService.Instance.UpdatePlayerAsync(lobbyId, playerId, updateOptions));
129+
return await LobbyService.Instance.UpdatePlayerAsync(lobbyId, playerId, updateOptions);
164130
}
165131

166132
public async void SendHeartbeatPing(string lobbyId)
167133
{
168-
await ExceptionHandling(LobbyService.Instance.SendHeartbeatPingAsync(lobbyId));
134+
await LobbyService.Instance.SendHeartbeatPingAsync(lobbyId);
169135
}
170136
}
171137
}

Assets/Scripts/UnityServices/Lobbies/LobbyServiceFacade.cs

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,18 @@ public Task EndTracking()
9191
{
9292
CurrentUnityLobby = null;
9393

94-
if (!string.IsNullOrEmpty(m_LocalLobby?.LobbyID))
94+
var lobbyId = m_LocalLobby?.LobbyID;
95+
96+
if (!string.IsNullOrEmpty(lobbyId))
9597
{
96-
task = LeaveLobbyAsync(m_LocalLobby?.LobbyID);
98+
if (m_LocalUser.IsHost)
99+
{
100+
task = DeleteLobbyAsync(lobbyId);
101+
}
102+
else
103+
{
104+
task = LeaveLobbyAsync(lobbyId);
105+
}
97106
}
98107

99108
m_LocalUser.ResetState();
@@ -146,6 +155,10 @@ async void UpdateLobby(float unused)
146155
{
147156
m_RateLimitQuery.PutOnCooldown();
148157
}
158+
else if (e.Reason != LobbyExceptionReason.LobbyNotFound && !m_LocalUser.IsHost) // If Lobby is not found and if we are not the host, it has already been deleted. No need to publish the error here.
159+
{
160+
PublishError(e);
161+
}
149162
}
150163
}
151164

@@ -171,6 +184,10 @@ async void UpdateLobby(float unused)
171184
{
172185
m_RateLimitHost.PutOnCooldown();
173186
}
187+
else
188+
{
189+
PublishError(e);
190+
}
174191
}
175192

176193
return (false, null);
@@ -207,6 +224,10 @@ async void UpdateLobby(float unused)
207224
{
208225
m_RateLimitJoin.PutOnCooldown();
209226
}
227+
else
228+
{
229+
PublishError(e);
230+
}
210231
}
211232

212233
return (false, null);
@@ -234,6 +255,10 @@ async void UpdateLobby(float unused)
234255
{
235256
m_RateLimitQuickJoin.PutOnCooldown();
236257
}
258+
else
259+
{
260+
PublishError(e);
261+
}
237262
}
238263

239264
return (false, null);
@@ -261,12 +286,29 @@ public async Task RetrieveAndPublishLobbyListAsync()
261286
{
262287
m_RateLimitQuery.PutOnCooldown();
263288
}
289+
else
290+
{
291+
PublishError(e);
292+
}
264293
}
265294
}
266295

267296
public async Task<Lobby> ReconnectToLobbyAsync(string lobbyId)
268297
{
269-
return await m_LobbyApiInterface.ReconnectToLobby(lobbyId);
298+
try
299+
{
300+
return await m_LobbyApiInterface.ReconnectToLobby(lobbyId);
301+
}
302+
catch (LobbyServiceException e)
303+
{
304+
// If Lobby is not found and if we are not the host, it has already been deleted. No need to publish the error here.
305+
if (e.Reason != LobbyExceptionReason.LobbyNotFound && !m_LocalUser.IsHost)
306+
{
307+
PublishError(e);
308+
}
309+
}
310+
311+
return null;
270312
}
271313

272314
/// <summary>
@@ -280,9 +322,12 @@ public async Task LeaveLobbyAsync(string lobbyId)
280322
await m_LobbyApiInterface.RemovePlayerFromLobby(uasId, lobbyId);
281323
}
282324
catch (LobbyServiceException e)
283-
when (e is { Reason: LobbyExceptionReason.LobbyNotFound })
284325
{
285-
// If Lobby is not found, it has already been deleted. No need to throw here.
326+
// If Lobby is not found and if we are not the host, it has already been deleted. No need to publish the error here.
327+
if (e.Reason != LobbyExceptionReason.LobbyNotFound && !m_LocalUser.IsHost)
328+
{
329+
PublishError(e);
330+
}
286331
}
287332

288333
}
@@ -291,19 +336,33 @@ public async void RemovePlayerFromLobbyAsync(string uasId, string lobbyId)
291336
{
292337
if (m_LocalUser.IsHost)
293338
{
294-
await m_LobbyApiInterface.RemovePlayerFromLobby(uasId, lobbyId);
339+
try
340+
{
341+
await m_LobbyApiInterface.RemovePlayerFromLobby(uasId, lobbyId);
342+
}
343+
catch (LobbyServiceException e)
344+
{
345+
PublishError(e);
346+
}
295347
}
296348
else
297349
{
298350
Debug.LogError("Only the host can remove other players from the lobby.");
299351
}
300352
}
301353

302-
public async void DeleteLobbyAsync(string lobbyId)
354+
public async Task DeleteLobbyAsync(string lobbyId)
303355
{
304356
if (m_LocalUser.IsHost)
305357
{
306-
await m_LobbyApiInterface.DeleteLobby(lobbyId);
358+
try
359+
{
360+
await m_LobbyApiInterface.DeleteLobby(lobbyId);
361+
}
362+
catch (LobbyServiceException e)
363+
{
364+
PublishError(e);
365+
}
307366
}
308367
else
309368
{
@@ -336,6 +395,10 @@ public async Task UpdatePlayerDataAsync(Dictionary<string, PlayerDataObject> dat
336395
{
337396
m_RateLimitQuery.PutOnCooldown();
338397
}
398+
else if (e.Reason != LobbyExceptionReason.LobbyNotFound && !m_LocalUser.IsHost) // If Lobby is not found and if we are not the host, it has already been deleted. No need to publish the error here.
399+
{
400+
PublishError(e);
401+
}
339402
}
340403
}
341404

@@ -359,6 +422,10 @@ public async Task UpdatePlayerRelayInfoAsync(string allocationId, string connect
359422
{
360423
m_RateLimitQuery.PutOnCooldown();
361424
}
425+
else
426+
{
427+
PublishError(e);
428+
}
362429

363430
//todo - retry logic? SDK is supposed to handle this eventually
364431
}
@@ -406,6 +473,10 @@ public async Task UpdateLobbyDataAsync(Dictionary<string, DataObject> data)
406473
{
407474
m_RateLimitQuery.PutOnCooldown();
408475
}
476+
else
477+
{
478+
PublishError(e);
479+
}
409480
}
410481
}
411482

@@ -418,8 +489,25 @@ public void DoLobbyHeartbeat(float dt)
418489
if (m_HeartbeatTime > k_HeartbeatPeriod)
419490
{
420491
m_HeartbeatTime -= k_HeartbeatPeriod;
421-
m_LobbyApiInterface.SendHeartbeatPing(CurrentUnityLobby.Id);
492+
try
493+
{
494+
m_LobbyApiInterface.SendHeartbeatPing(CurrentUnityLobby.Id);
495+
}
496+
catch (LobbyServiceException e)
497+
{
498+
// If Lobby is not found and if we are not the host, it has already been deleted. No need to publish the error here.
499+
if (e.Reason != LobbyExceptionReason.LobbyNotFound && !m_LocalUser.IsHost)
500+
{
501+
PublishError(e);
502+
}
503+
}
422504
}
423505
}
506+
507+
void PublishError(LobbyServiceException e)
508+
{
509+
var reason = $"{e.Message} ({e.InnerException?.Message})"; // Lobby error type, then HTTP error type.
510+
m_UnityServiceErrorMessagePub.Publish(new UnityServiceErrorMessage("Lobby Error", reason, UnityServiceErrorMessage.Service.Lobby, e));
511+
}
424512
}
425513
}

CHANGELOG.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
66

77
Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com).
88

9-
## [unreleased] - yyy-mm-dd
9+
## [unreleased] - aaaa-mm-dd
10+
1011
### Added
11-
*
12+
*
1213
### Changed
13-
*
14+
* Hosts now delete their lobby when shutting down instead of only leaving it (#772) Since Boss Room doesn't support host migration, there is no need to keep the lobby alive after the host shuts down. This also changes how LobbyServiceExceptions are handled to prevent popup messages on clients trying to leave a lobby that is already deleted, following the best practices outlined in this doc : https://docs.unity.com/lobby/delete-a-lobby.html
15+
*
1416
### Cleanup
15-
*
17+
*
1618
### Fixed
17-
*
19+
*
1820

1921
## [2.0.2] - 2022-11-01
2022
### Fixed

0 commit comments

Comments
 (0)