using System; using System.Threading.Tasks; using LobbyRelaySample.lobby; using Unity.Services.Lobbies.Models; namespace LobbyRelaySample { /// /// Keep updated on changes to a joined lobby, at a speed compliant with Lobby's rate limiting. /// public class LobbyContentUpdater : IReceiveMessages { private LocalLobby m_LocalLobby; private LobbyUser m_LocalUser; private bool m_ShouldPushData = false; private const float k_approvalMaxTime = 10; // Used for determining if a user should timeout if they are unable to connect. private float m_lifetime = 0; const int k_UpdateIntervalMS = 1500; public void BeginTracking(LocalLobby localLobby, LobbyUser localUser) { m_LocalUser = localUser; m_LocalLobby = localLobby; m_LocalLobby.onChanged += OnLocalLobbyChanged; m_ShouldPushData = true; Locator.Get.Messenger.Subscribe(this); #pragma warning disable 4014 UpdateLoopAsync(); #pragma warning restore 4014 m_lifetime = 0; LobbyAsyncRequests.Instance.onLobbyUpdated += OnRemoteLobbyUpdated; } public void EndTracking() { m_ShouldPushData = false; Locator.Get.Messenger.Unsubscribe(this); if (m_LocalLobby != null) m_LocalLobby.onChanged -= OnLocalLobbyChanged; LobbyAsyncRequests.Instance.onLobbyUpdated -= OnRemoteLobbyUpdated; m_LocalLobby = null; } public void OnReceiveMessage(MessageType type, object msg) { if (type == MessageType.ClientUserSeekingDisapproval) { bool shouldDisapprove = m_LocalLobby.State != LobbyState.Lobby; // By not refreshing, it's possible to have a lobby in the lobby list UI after its countdown starts and then try joining. if (shouldDisapprove) (msg as Action)?.Invoke(relay.Approval.GameAlreadyStarted); } } void OnLocalLobbyChanged(LocalLobby changed) { if (string.IsNullOrEmpty(changed.LobbyID)) // When the player leaves, their LocalLobby is cleared out but maintained. { EndTracking(); return; } if (changed.canPullUpdate) { changed.canPullUpdate = false; return; } m_ShouldPushData = true; } /// /// If there have been any data changes since the last update, push them to Lobby. Regardless, pull for the most recent data. /// (Unless we're already awaiting a query, in which case continue waiting.) /// private async Task UpdateLoopAsync() { while (m_LocalLobby != null) { if (!m_LocalUser.IsApproved && m_lifetime > k_approvalMaxTime) { Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Connection attempt timed out!"); Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.JoinMenu); } if (m_ShouldPushData) PushDataToLobby(); void PushDataToLobby() { m_ShouldPushData = false; if (m_LocalUser.IsHost) { DoLobbyDataPush(); } DoPlayerDataPush(); } void DoLobbyDataPush() { #pragma warning disable 4014 LobbyAsyncRequests.Instance.UpdateLobbyDataAsync(LobbyConverters.LocalToRemoteData(m_LocalLobby)); #pragma warning restore 4014 } void DoPlayerDataPush() { #pragma warning disable 4014 LobbyAsyncRequests.Instance.UpdatePlayerDataAsync(LobbyConverters.LocalToRemoteUserData(m_LocalUser)); #pragma warning restore 4014 } await Task.Delay(k_UpdateIntervalMS); } } void OnRemoteLobbyUpdated(Lobby lobby) { m_LocalLobby.canPullUpdate = true; //synching our local lobby LobbyConverters.RemoteToLocal(lobby, m_LocalLobby); //Dont push data this tick, since we "pulled"s if (!m_LocalUser.IsHost) { foreach (var lobbyUser in m_LocalLobby.LobbyUsers) { if (lobbyUser.Value.IsHost) return; } Locator.Get.Messenger.OnReceiveMessage(MessageType.DisplayErrorPopup, "Host left the lobby! Disconnecting..."); Locator.Get.Messenger.OnReceiveMessage(MessageType.EndGame, null); Locator.Get.Messenger.OnReceiveMessage(MessageType.ChangeMenuState, GameState.JoinMenu); } } } }