diff --git a/Assets/BossRoom/Prefabs/State/MainMenuState.prefab b/Assets/BossRoom/Prefabs/State/MainMenuState.prefab new file mode 100644 index 000000000..36b3aa2bd --- /dev/null +++ b/Assets/BossRoom/Prefabs/State/MainMenuState.prefab @@ -0,0 +1,45 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &8576152884213668003 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8576152884213668001} + - component: {fileID: 8576152884213668000} + m_Layer: 0 + m_Name: MainMenuState + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &8576152884213668001 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8576152884213668003} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8576152884213668000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8576152884213668003} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ab10fd7c6aa7d36438f5ac1c0ebfadbb, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/BossRoom/Prefabs/State/MainMenuState.prefab.meta b/Assets/BossRoom/Prefabs/State/MainMenuState.prefab.meta new file mode 100644 index 000000000..3aea49d17 --- /dev/null +++ b/Assets/BossRoom/Prefabs/State/MainMenuState.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 30b6e45e8e2b596449e225cafe2a05a2 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BossRoom/Scenes/MainMenu.unity b/Assets/BossRoom/Scenes/MainMenu.unity index 894b3023e..b466242cc 100644 --- a/Assets/BossRoom/Scenes/MainMenu.unity +++ b/Assets/BossRoom/Scenes/MainMenu.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5060647f2ae656c8a84051a839ff727d64afdd7194347967b5cd1c909f083681 -size 46738 +oid sha256:7fde64095d2daa9da6cc92ac5c7998230bbe2386a14c4179ec5bc0f0316ef26f +size 49225 diff --git a/Assets/BossRoom/Scripts/Client/Game/State/ClientMainMenuState.cs b/Assets/BossRoom/Scripts/Client/Game/State/ClientMainMenuState.cs index 2602d53b4..929a6296b 100644 --- a/Assets/BossRoom/Scripts/Client/Game/State/ClientMainMenuState.cs +++ b/Assets/BossRoom/Scripts/Client/Game/State/ClientMainMenuState.cs @@ -19,5 +19,4 @@ public override void NetworkStart() //fortunately we know you are a client, because all players are clients when sitting at the main menu screen. } } - } diff --git a/Assets/BossRoom/Scripts/Client/Game/State/ClientPostGameState.cs b/Assets/BossRoom/Scripts/Client/Game/State/ClientPostGameState.cs index 5fcd9fedf..fe2d98919 100644 --- a/Assets/BossRoom/Scripts/Client/Game/State/ClientPostGameState.cs +++ b/Assets/BossRoom/Scripts/Client/Game/State/ClientPostGameState.cs @@ -1,3 +1,5 @@ +using UnityEngine; + namespace BossRoom.Client { /// @@ -8,6 +10,19 @@ public class ClientPostGameState : GameStateBehaviour { public override GameState ActiveState { get { return GameState.PostGame; } } + protected override void Start() + { + base.Start(); + + //it is common for the user to get dumped back to main menu from here (i.e., if the host decides not to play again), and + //it is a little funny to display a "Connection to Host Lost" message in that case. The best thing would probably be to + //display a "Host Abandoned the Game" message, but this would require some more plumbing (an RPC from the host before it quit, + //containing that information). + //In the meantime, we just set "UserRequested" to suppress the Disconnected error popup. + var portalGO = GameObject.FindGameObjectWithTag("GameNetPortal"); + portalGO.GetComponent().DisconnectReason.SetDisconnectReason(DisconnectReasonType.UserRequested); + } + public override void NetworkStart() { base.NetworkStart(); diff --git a/Assets/BossRoom/Scripts/Client/Net/ClientGameNetPortal.cs b/Assets/BossRoom/Scripts/Client/Net/ClientGameNetPortal.cs index 2e69faacd..c88604a00 100644 --- a/Assets/BossRoom/Scripts/Client/Net/ClientGameNetPortal.cs +++ b/Assets/BossRoom/Scripts/Client/Net/ClientGameNetPortal.cs @@ -18,6 +18,11 @@ public class ClientGameNetPortal : MonoBehaviour { private GameNetPortal m_Portal; + /// + /// If a disconnect occurred this will be populated with any contextual information that was available to explain why. + /// + public DisconnectReason DisconnectReason { get; private set; } = new DisconnectReason(); + /// /// Time in seconds before the client considers a lack of server response a timeout /// @@ -42,6 +47,8 @@ void Start() private void NetworkStart() { + DisconnectReason.Clear(); + if (!m_Portal.NetManager.IsClient) { enabled = false; @@ -54,7 +61,7 @@ private void NetworkStart() //only do this if a pure client, so as not to overlap with host behavior in ServerGameNetPortal. m_Portal.UserDisconnectRequested += OnUserDisconnectRequest; } - m_Portal.NetManager.OnClientDisconnectCallback += OnClientDisconnect; + SceneManager.sceneLoaded += OnSceneLoaded; } } @@ -71,23 +78,11 @@ private void OnUserDisconnectRequest() { if( m_Portal.NetManager.IsClient ) { + DisconnectReason.SetDisconnectReason(DisconnectReasonType.UserRequested); m_Portal.NetManager.StopClient(); } } - /// - /// Invoked whenever a client disconnects from the host. - /// - private void OnClientDisconnect(ulong clientId) - { - if( clientId == m_Portal.NetManager.LocalClientId ) - { - SceneManager.sceneLoaded -= OnSceneLoaded; - m_Portal.UserDisconnectRequested -= OnUserDisconnectRequest; - m_Portal.NetManager.OnClientDisconnectCallback -= OnClientDisconnect; - } - } - private void OnConnectFinished(ConnectStatus status) { //on success, there is nothing to do (the MLAPI scene management system will take us to the next scene). @@ -99,8 +94,13 @@ private void OnConnectFinished(ConnectStatus status) private void OnDisconnectOrTimeout(ulong clientID) { - if(clientID == MLAPI.NetworkManager.Singleton.LocalClientId ) + // we could also check whether the disconnect was us or the host, but the "interesting" question is whether + //following the disconnect, we're no longer a Connected Client, so we just explicitly check that scenario. + if ( !MLAPI.NetworkManager.Singleton.IsConnectedClient ) { + SceneManager.sceneLoaded -= OnSceneLoaded; + m_Portal.UserDisconnectRequested -= OnUserDisconnectRequest; + //On a client disconnect we want to take them back to the main menu. //We have to check here in SceneManager if our active scene is the main menu, as if it is, it means we timed out rather than a raw disconnect; if (UnityEngine.SceneManagement.SceneManager.GetActiveScene().name != "MainMenu") @@ -108,6 +108,11 @@ private void OnDisconnectOrTimeout(ulong clientID) // we're not at the main menu, so we obviously had a connection before... thus, we aren't in a timeout scenario. // Just shut down networking and switch back to main menu. MLAPI.NetworkManager.Singleton.Shutdown(); + if( !DisconnectReason.HasTransitionReason ) + { + //disconnect that happened for some other reason than user UI interaction--should display a message. + DisconnectReason.SetDisconnectReason(DisconnectReasonType.Disconnect); + } SceneManager.LoadScene("MainMenu"); } else @@ -129,7 +134,6 @@ private void OnDisconnectOrTimeout(ulong clientID) /// The port of the host to connect to. public static void StartClient(GameNetPortal portal, string ipaddress, int port) { - //DMW_NOTE: non-portable. We need to be updated when moving to UTP transport. var chosenTransport = NetworkManager.Singleton.gameObject.GetComponent().IpHostTransport; NetworkManager.Singleton.NetworkConfig.NetworkTransport = chosenTransport; diff --git a/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs b/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs new file mode 100644 index 000000000..57c7228db --- /dev/null +++ b/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs @@ -0,0 +1,53 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace BossRoom.Client +{ + /// + /// enum that records additional context for why a user was disconnected. The primary use case for this + /// is to allow the MainMenu to display an appropriate message after a disconnect event. + /// + public enum DisconnectReasonType + { + Undefined, //no reason has been set. + UserRequested, //user explicitly requested a disconnect. + Disconnect, //client unexpectedly lost connection with host. + } + + /// + /// This class provides some additional context for the connection managed by the ClientGameNetPortal. If a disconnect occurrs, or is expected to occur, client + /// code can set the reason why here. Then subsequent code can interrogate this class to get the disconnect reason, and display appropriate information to + /// the user, even after a scene transition has occurred. The state is set back to Undefined if a new connection is begun. + /// + public class DisconnectReason + { + /// + /// When a disconnect is detected (or expected), set this to provide some context for why it occurred. + /// + public void SetDisconnectReason( DisconnectReasonType reason) + { + //using an explicit setter here rather than the auto-property, to make the code locations where disconnect information is set more obvious. + Reason = reason; + } + + /// + /// The reason why a disconnect occurred, or Undefined if not set. + /// + public DisconnectReasonType Reason { get; private set; } = DisconnectReasonType.Undefined; + + /// + /// Clear the DisconnectReason, returning it to Undefined. + /// + public void Clear() + { + Reason = DisconnectReasonType.Undefined; + } + + /// + /// Has a TransitionReason already be set? (The TransitionReason provides context for why someone transition back to the MainMenu, and is a one-use item + /// that is unset as soon as it is read). + /// + public bool HasTransitionReason => Reason != DisconnectReasonType.Undefined; + } +} diff --git a/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs.meta b/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs.meta new file mode 100644 index 000000000..fb7d32177 --- /dev/null +++ b/Assets/BossRoom/Scripts/Client/Net/DisconnectReason.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c92998344b3ecfa4da078bd59ebd469e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/BossRoom/Scripts/Client/UI/MainMenuUI.cs b/Assets/BossRoom/Scripts/Client/UI/MainMenuUI.cs index fd05216c8..b8fddfbaa 100644 --- a/Assets/BossRoom/Scripts/Client/UI/MainMenuUI.cs +++ b/Assets/BossRoom/Scripts/Client/UI/MainMenuUI.cs @@ -33,6 +33,11 @@ void Start() m_ClientNetPortal.NetworkTimedOut += OnNetworkTimeout; m_ClientNetPortal.ConnectFinished += OnConnectFinished; + + if (m_ClientNetPortal.DisconnectReason.Reason == DisconnectReasonType.Disconnect ) + { + m_ResponsePopup.SetupNotifierDisplay("Disconnected From Host", "The connection to the Host was lost", false, true ); + } } public void OnHostClicked() diff --git a/Packages/com.unity.multiplayer.samples.coop/Third Party Notices.md.meta b/Packages/com.unity.multiplayer.samples.coop/Third Party Notices.md.meta new file mode 100644 index 000000000..626bc1164 --- /dev/null +++ b/Packages/com.unity.multiplayer.samples.coop/Third Party Notices.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bd16abfad80aa0a46a5c1914f7a336be +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: