Skip to content

feat: popup panel for connection status messages #494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
508 changes: 503 additions & 5 deletions Assets/BossRoom/Prefabs/UI/SettingsPanelCanvas.prefab

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Assets/BossRoom/Scenes/Startup.unity
Git LFS file not shown
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public class ClientMainMenuState : GameStateBehaviour
[SerializeField] CanvasGroup m_MainMenuButtonsCanvasGroup;
[SerializeField] GameObject m_SignInSpinner;

PopupPanel m_PopupPanel;

void Awake()
{
m_MainMenuButtonsCanvasGroup.interactable = false;
Expand All @@ -40,7 +42,7 @@ void Awake()
}

[Inject]
void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby)
void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFacade, LocalLobbyUser localUser, LocalLobby localLobby, PopupPanel popupPanel)
{
m_Scope = new DIScope(DIScope.RootScope);

Expand All @@ -49,6 +51,8 @@ void InjectDependenciesAndInitialize(AuthenticationServiceFacade authServiceFaca

var unityAuthenticationInitOptions = new InitializationOptions();

m_PopupPanel = popupPanel;

#if UNITY_EDITOR
//The code below makes it possible for the clone instance to log in as a different user profile in Authentication service.
//This allows us to test services integration locally by utilising Parrelsync.
Expand Down Expand Up @@ -100,7 +104,7 @@ void OnAuthSignIn()

void OnSignInFailed()
{
Debug.LogError("For some reason we can't authenticate the user anonymously - that typically means that project is not properly set up with Unity services.");
m_PopupPanel.ShowPopupPanel("Authentication Error", "For some reason we can't authenticate the user anonymously - that typically means that project is not properly set up with Unity services.");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
using UnityEngine;

namespace Unity.Multiplayer.Samples.BossRoom.Visual
{
public class ConnectionStatusMessageUIManager : MonoBehaviour
{
IDisposable m_Subscriptions;
PopupPanel m_PopupPanel;

[Inject]
void InjectDependencies(ISubscriber<ConnectStatus> connectStatusSub, PopupPanel popupPanel)
{
m_Subscriptions = connectStatusSub.Subscribe(OnConnectStatus);
m_PopupPanel = popupPanel;
}

void Awake()
{
DontDestroyOnLoad(gameObject);
}

void OnDestroy()
{
m_Subscriptions?.Dispose();
}

void OnConnectStatus(ConnectStatus status)
{
switch (status)
{
case ConnectStatus.Undefined:
case ConnectStatus.UserRequestedDisconnect:
break;
case ConnectStatus.ServerFull:
m_PopupPanel.ShowPopupPanel("Connection Failed", "The Host is full and cannot accept any additional connections.");
break;
case ConnectStatus.Success:
break;
case ConnectStatus.LoggedInAgain:
m_PopupPanel.ShowPopupPanel("Connection Failed", "You have logged in elsewhere using the same account.");
break;
case ConnectStatus.GenericDisconnect:
m_PopupPanel.ShowPopupPanel("Disconnected From Host", "The connection to the host was lost");
break;
default:
Debug.LogWarning($"New ConnectStatus {status} has been added, but no connect message defined for it.");
break;
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 5 additions & 51 deletions Assets/BossRoom/Scripts/Client/UI/Lobby/LobbyUIMediator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class LobbyUIMediator : MonoBehaviour
NameGenerationData m_NameGenerationData;
GameNetPortal m_GameNetPortal;
ClientGameNetPortal m_ClientNetPortal;
PopupPanel m_PopupPanel;

[Inject]
void InjectDependenciesAndInitialize(
Expand All @@ -33,7 +34,8 @@ void InjectDependenciesAndInitialize(
LocalLobby localLobby,
NameGenerationData nameGenerationData,
GameNetPortal gameNetPortal,
ClientGameNetPortal clientGameNetPortal
ClientGameNetPortal clientGameNetPortal,
PopupPanel popupPanel
)
{
m_NameGenerationData = nameGenerationData;
Expand All @@ -42,23 +44,18 @@ ClientGameNetPortal clientGameNetPortal
m_LocalLobby = localLobby;
m_GameNetPortal = gameNetPortal;
m_ClientNetPortal = clientGameNetPortal;
m_PopupPanel = popupPanel;

RegenerateName();

m_ClientNetPortal.NetworkTimedOut += OnNetworkTimeout;
m_ClientNetPortal.ConnectFinished += OnConnectFinished;

//any disconnect reason set? Show it to the user here.
ConnectStatusToMessage(m_ClientNetPortal.DisconnectReason.Reason, false);
m_ClientNetPortal.DisconnectReason.Clear();
}

void OnDestroy()
{
if (m_ClientNetPortal != null)
{
m_ClientNetPortal.NetworkTimedOut -= OnNetworkTimeout;
m_ClientNetPortal.ConnectFinished -= OnConnectFinished;
}
}

Expand Down Expand Up @@ -157,6 +154,7 @@ void OnJoinedLobby(Lobby remoteLobby)

void OnRelayJoinFailed(string message)
{
m_PopupPanel.ShowPopupPanel("Relay join failed", message);
Debug.Log($"Relay join failed: {message}");
//leave the lobby if relay failed for some reason
m_LobbyServiceFacade.EndTracking();
Expand Down Expand Up @@ -217,15 +215,6 @@ void UnblockUIAfterLoadingIsComplete()
}
}

/// <summary>
/// Callback when the server sends us back a connection finished event.
/// </summary>
/// <param name="status"></param>
void OnConnectFinished(ConnectStatus status)
{
ConnectStatusToMessage(status, true);
}

/// <summary>
/// Invoked when the client sent a connection request to the server and didn't hear back at all.
/// This should create a UI letting the player know that something went wrong and to try again
Expand All @@ -234,40 +223,5 @@ void OnNetworkTimeout()
{
UnblockUIAfterLoadingIsComplete();
}

/// <summary>
/// Takes a ConnectStatus and shows an appropriate message to the user. This can be called on: (1) successful connect,
/// (2) failed connect, (3) disconnect.
/// </summary>
/// <param name="connecting">pass true if this is being called in response to a connect finishing.</param>
void ConnectStatusToMessage(ConnectStatus status, bool connecting)
{
switch (status)
{
case ConnectStatus.Undefined:
case ConnectStatus.UserRequestedDisconnect:
break;
case ConnectStatus.ServerFull:
Debug.Log("Connection Failed, The Host is full and cannot accept any additional connections");
break;
case ConnectStatus.Success:
if (connecting)
{
Debug.Log("Success!, Joining Now");
}
break;
case ConnectStatus.LoggedInAgain:
Debug.Log("Connection Failed, You have logged in elsewhere using the same account");
break;
case ConnectStatus.GenericDisconnect:
var title = connecting ? "Connection Failed" : "Disconnected From Host";
var text = connecting ? "Something went wrong" : "The connection to the host was lost";
Debug.Log($"{title}, {text}");
break;
default:
Debug.LogWarning($"New ConnectStatus {status} has been added, but no connect message defined for it.");
break;
}
}
}
}
19 changes: 16 additions & 3 deletions Assets/BossRoom/Scripts/Shared/ApplicationController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Unity.Multiplayer.Samples.BossRoom.Shared.Infrastructure;
using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Infrastructure;
using Unity.Multiplayer.Samples.BossRoom.Shared.Net.UnityServices.Lobbies;
using Unity.Multiplayer.Samples.BossRoom.Visual;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
Expand All @@ -15,9 +16,17 @@ namespace Unity.Multiplayer.Samples.BossRoom.Shared
/// </summary>
public class ApplicationController : MonoBehaviour
{
[SerializeField] UpdateRunner m_UpdateRunner;
[SerializeField] GameNetPortal m_GameNetPortal;
[SerializeField] ClientGameNetPortal m_ClientNetPortal;
[SerializeField]
UpdateRunner m_UpdateRunner;

[SerializeField]
GameNetPortal m_GameNetPortal;

[SerializeField]
ClientGameNetPortal m_ClientNetPortal;

[SerializeField]
PopupPanel m_PopupPanel;

LocalLobby m_LocalLobby;
LobbyServiceFacade m_LobbyServiceFacade;
Expand All @@ -37,6 +46,7 @@ private void Awake()
scope.BindInstanceAsSingle(m_UpdateRunner);
scope.BindInstanceAsSingle(m_GameNetPortal);
scope.BindInstanceAsSingle(m_ClientNetPortal);
scope.BindInstanceAsSingle(m_PopupPanel);

//the following singletons represent the local representations of the lobby that we're in and the user that we are
//they can persist longer than the lifetime of the UI in MainMenu where we set up the lobby that we create or join
Expand All @@ -46,6 +56,9 @@ private void Awake()
//this message channel is essential and persists for the lifetime of the lobby and relay services
scope.BindMessageChannel<UnityServiceErrorMessage>();

//this message channel is essential and persists for the lifetime of the lobby and relay services
scope.BindMessageChannel<ConnectStatus>();

//buffered message channels hold the latest received message in buffer and pass to any new subscribers
scope.BindBufferedMessageChannel<LobbyListFetchedMessage>();

Expand Down
64 changes: 64 additions & 0 deletions Assets/BossRoom/Scripts/Shared/Game/UI/PopupPanel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using UnityEngine;
using TMPro;

namespace Unity.Multiplayer.Samples.BossRoom.Visual
{
/// <summary>
/// responsible for driving all the functionality of the popup panel players see when connecting to the game
/// </summary>
public class PopupPanel : MonoBehaviour
{
[SerializeField]
TextMeshProUGUI m_TitleText;
[SerializeField]
TextMeshProUGUI m_MainText;

bool m_IsPopupShown;

void Awake()
{
ResetState();
}

public void OnConfirmClick()
{
ResetState();
}

/// <summary>
/// Helper method to help us reset all state for the popup manager.
/// </summary>
void ResetState()
{
m_TitleText.text = string.Empty;
m_MainText.text = string.Empty;
gameObject.SetActive(false);
m_IsPopupShown = false;
}

/// <summary>
/// Sets the panel to match the given specifications to notify the player. If display image is set to true, it will display
/// </summary>
/// <param name="titleText">The title text at the top of the panel</param>
/// <param name="mainText"> The text just under the title- the main body of text</param>
public void ShowPopupPanel(string titleText, string mainText)
{
if (m_IsPopupShown)
{
Debug.Log("Trying to show popup, but another popup is already being shown.");
Debug.Log($"{titleText}. {mainText}");
}
else
{
ResetState();

m_TitleText.text = titleText;
m_MainText.text = mainText;

gameObject.SetActive(true);
m_IsPopupShown = true;
}
}
}
}
11 changes: 11 additions & 0 deletions Assets/BossRoom/Scripts/Shared/Game/UI/PopupPanel.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,27 @@ public class ClientGameNetPortal : MonoBehaviour
/// <summary>
/// If a disconnect occurred this will be populated with any contextual information that was available to explain why.
/// </summary>
public DisconnectReason DisconnectReason { get; private set; } = new DisconnectReason();
public DisconnectReason DisconnectReason { get; } = new DisconnectReason();

/// <summary>
/// Time in seconds before the client considers a lack of server response a timeout
/// </summary>
private const int k_TimeoutDuration = 10;

public event Action<ConnectStatus> ConnectFinished;

/// <summary>
/// This event fires when the client sent out a request to start the client, but failed to hear back after an allotted amount of
/// time from the host.
/// </summary>
public event Action NetworkTimedOut;

private LobbyServiceFacade m_LobbyServiceFacade;
IPublisher<ConnectStatus> m_ConnectStatusPub;

[Inject]
private void InjectDependencies(LobbyServiceFacade lobbyServiceFacade)
private void InjectDependencies(LobbyServiceFacade lobbyServiceFacade, IPublisher<ConnectStatus> connectStatusPub)
{
m_LobbyServiceFacade = lobbyServiceFacade;
m_ConnectStatusPub = connectStatusPub;
}

private void Awake()
Expand Down Expand Up @@ -107,8 +107,6 @@ public void OnConnectFinished(ConnectStatus status)
//this indicates a game level failure, rather than a network failure. See note in ServerGameNetPortal.
DisconnectReason.SetDisconnectReason(status);
}

ConnectFinished?.Invoke(status);
}

private void OnDisconnectReasonReceived(ConnectStatus status)
Expand All @@ -133,14 +131,15 @@ private void OnDisconnectOrTimeout(ulong clientID)
//disconnect that happened for some other reason than user UI interaction--should display a message.
DisconnectReason.SetDisconnectReason(ConnectStatus.GenericDisconnect);
}

SceneManager.LoadScene("MainMenu");
}
else if (DisconnectReason.Reason == ConnectStatus.GenericDisconnect || DisconnectReason.Reason == ConnectStatus.Undefined)
{
// only call this if generic disconnect. Else if there's a reason, there's already code handling that popup
NetworkTimedOut?.Invoke();
}
m_ConnectStatusPub.Publish(DisconnectReason.Reason);
DisconnectReason.Clear();
}
}

Expand Down