diff --git a/Unity/Assets/Editor/Rooms/RoomClientEditor.cs b/Unity/Assets/Editor/Rooms/RoomClientEditor.cs index d9bfdeb4d..6866f15fa 100644 --- a/Unity/Assets/Editor/Rooms/RoomClientEditor.cs +++ b/Unity/Assets/Editor/Rooms/RoomClientEditor.cs @@ -92,6 +92,8 @@ public override void OnInspectorGUI() EditorGUILayout.HelpBox("Joined Room " + component.Room.UUID, MessageType.Info); } + EditorGUILayout.PropertyField(serializedObject.FindProperty("reconnectBehaviour")); + foldoutRooms = EditorGUILayout.BeginFoldoutHeaderGroup(foldoutRooms, "Available Rooms"); if (foldoutRooms) diff --git a/Unity/Assets/Runtime/Messaging/NetworkScene.cs b/Unity/Assets/Runtime/Messaging/NetworkScene.cs index 8ba7f3d75..d2dd47aa4 100644 --- a/Unity/Assets/Runtime/Messaging/NetworkScene.cs +++ b/Unity/Assets/Runtime/Messaging/NetworkScene.cs @@ -246,6 +246,26 @@ public void AddConnection(INetworkConnection connection) connections.Add(connection); } + /// + /// Public method instructing the network scene to drop all current connections and dispose of them. + /// Used to recover from a connection loss to Nexus. + /// + public void ClearConnections() + { + foreach (var c in connections) + { + try + { + c.Dispose(); + } + catch + { + + } + } + connections.Clear(); + } + private void Update() { OnUpdate.Invoke(); @@ -329,18 +349,7 @@ public void SendJson(NetworkId objectid, T message) private void OnDestroy() { - foreach (var c in connections) - { - try - { - c.Dispose(); - } - catch - { - - } - } - connections.Clear(); + ClearConnections(); } } } \ No newline at end of file diff --git a/Unity/Assets/Runtime/Rooms/RoomClient.cs b/Unity/Assets/Runtime/Rooms/RoomClient.cs index 6fde83595..99b6ab84b 100644 --- a/Unity/Assets/Runtime/Rooms/RoomClient.cs +++ b/Unity/Assets/Runtime/Rooms/RoomClient.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.Linq; using Ubiq.Dictionaries; using Ubiq.Messaging; using Ubiq.Networking; using Ubiq.Rooms.Messages; using Ubiq.XR.Notifications; using UnityEngine; +using UnityEngine.SceneManagement; namespace Ubiq.Rooms { @@ -240,6 +240,18 @@ public IEnumerable Peers private float heartbeatSent => Time.realtimeSinceStartup - pingSent; public static float HeartbeatTimeout = 5f; public static float HeartbeatInterval = 1f; + + public enum ReconnectBehaviour + { + None, + Reconnect, + ReconnectAndReloadScenes + } + + public ReconnectBehaviour reconnectBehaviour = ReconnectBehaviour.None; + public static float reconnectTimeout = 10.0f; + private float nextReconnectTimeout = reconnectTimeout; + private PeerInterfaceFriend me = new PeerInterfaceFriend(Guid.NewGuid().ToString()); private RoomInterfaceFriend room = new RoomInterfaceFriend(); private NetworkScene scene; @@ -262,7 +274,15 @@ public override string Message { get { - return $"No Connection ({ client.heartbeatReceived.ToString("0") } seconds ago)"; + if (client.reconnectBehaviour == ReconnectBehaviour.None) + { + return $"Connection lost ({ client.heartbeatReceived.ToString("0") } seconds ago)"; + } + else + { + var timeToReconnect = Mathf.Max(0,client.nextReconnectTimeout - client.heartbeatReceived); + return $"Connection lost (Next reconnect attempt in { timeToReconnect.ToString("0") } seconds)"; + } } } } @@ -547,7 +567,7 @@ protected void ProcessMessage(ReferenceCountedSceneGraphMessage message) case "Ping": { pingReceived = Time.realtimeSinceStartup; - PlayerNotifications.Delete(ref notification); + var response = JsonUtility.FromJson(container.args); OnPingResponse(response); } @@ -635,6 +655,38 @@ public void Connect(ConnectionDefinition connection) scene.AddConnection(Connections.Resolve(connection)); } + /// + /// Method to reset all current connections and reconnect to the ones defined by the user in the Unity UI. + /// + public void ResetAndReconnect() + { + ResetAndReconnect(servers); + } + + /// + /// Method to reset all current connections and reconnect to the ones defined in the connection definition passed as argument. + /// + /// The connection definition that will be connected after the reset. + public void ResetAndReconnect(ConnectionDefinition[] connectionDefinitions) + { + // Drop all connections + scene.ClearConnections(); + + // Reconnect all connections + foreach (var item in connectionDefinitions) + { + try + { + Connect(item); + } + catch(Exception e) + { + Debug.LogError(e.ToString()); + } + } + } + + private void Update() { actions.ForEach(a => a()); @@ -674,10 +726,20 @@ private void Update() if (heartbeatReceived > HeartbeatTimeout) { + // There's been a long interval between server responses + // We may be disconnected, or there may be network issues + if (notification == null) { notification = PlayerNotifications.Show(new TimeoutNotification(this)); } + + if (heartbeatReceived > nextReconnectTimeout + && reconnectBehaviour != ReconnectBehaviour.None) + { + ResetAndReconnect(); + nextReconnectTimeout += reconnectTimeout; + } } } @@ -758,9 +820,30 @@ public void Ping() private void OnPingResponse(PingResponseArgs args) { + PlayerNotifications.Delete(ref notification); + nextReconnectTimeout = reconnectTimeout; + if(SessionId != args.sessionId && SessionId != null) { - Join(name:"",publish:false); // The RoomClient has re-established connectivity with the RoomServer, but under a different state. So, leave the room and let the user code re-establish any state. + // The RoomClient has re-established connectivity with + // the RoomServer, but under a different state. + if (reconnectBehaviour == ReconnectBehaviour.ReconnectAndReloadScenes) + { + var scenes = new Scene[SceneManager.sceneCount]; + for (int i = 0; i < SceneManager.sceneCount; i++) + { + scenes[i] = SceneManager.GetSceneAt(i); + } + + var first = true; + for (int i = 0; i < scenes.Length; i++) + { + SceneManager.LoadScene(scenes[i].buildIndex,mode: first + ? LoadSceneMode.Single + : LoadSceneMode.Additive); + first = false; + } + } } SessionId = args.sessionId; diff --git a/Unity/Assets/Samples/Start Here.unity b/Unity/Assets/Samples/Start Here.unity index dfd101542..32feff72b 100644 --- a/Unity/Assets/Samples/Start Here.unity +++ b/Unity/Assets/Samples/Start Here.unity @@ -413,6 +413,7 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -461,6 +462,7 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: -1.214, y: 0.634, z: -0.99} m_LocalScale: {x: 0.25361246, y: 0.25361246, z: 0.25361246} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 4 @@ -707,6 +709,11 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.z value: 0 objectReference: {fileID: 0} + - target: {fileID: 2642065778884258271, guid: 46c1f5d5a11cf5042886aabd56e7b9d7, + type: 3} + propertyPath: m_Enabled + value: 1 + objectReference: {fileID: 0} - target: {fileID: 2642065778884258271, guid: 46c1f5d5a11cf5042886aabd56e7b9d7, type: 3} propertyPath: servers.Array.size diff --git a/Unity/Assets/Samples/_Common/Prefabs/Network Scene.prefab b/Unity/Assets/Samples/_Common/Prefabs/Network Scene.prefab index c9ad23e8a..b690f6abe 100644 --- a/Unity/Assets/Samples/_Common/Prefabs/Network Scene.prefab +++ b/Unity/Assets/Samples/_Common/Prefabs/Network Scene.prefab @@ -27,6 +27,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7068762446378101729} m_RootOrder: 4 @@ -72,6 +73,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7068762446378101729} m_RootOrder: 0 @@ -118,6 +120,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: - {fileID: 7068762446051028576} - {fileID: 7068762446560339092} @@ -160,6 +163,7 @@ MonoBehaviour: m_EditorClassIdentifier: servers: - {fileID: -7849211376014683480, guid: 1c91df7c43c1dbe4fb0fb303e71a2790, type: 2} + reconnectBehaviour: 2 --- !u!114 &7068762446378101731 MonoBehaviour: m_ObjectHideFlags: 0 @@ -217,6 +221,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7068762446378101729} m_RootOrder: 1 @@ -260,6 +265,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7068762446378101729} m_RootOrder: 2 @@ -309,6 +315,7 @@ Transform: 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_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 7068762446378101729} m_RootOrder: 3