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 77ebd880d..d2dd47aa4 100644 --- a/Unity/Assets/Runtime/Messaging/NetworkScene.cs +++ b/Unity/Assets/Runtime/Messaging/NetworkScene.cs @@ -250,7 +250,7 @@ public void AddConnection(INetworkConnection 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 ResetConnections() + public void ClearConnections() { foreach (var c in connections) { @@ -349,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 0f3159c1a..99b6ab84b 100644 --- a/Unity/Assets/Runtime/Rooms/RoomClient.cs +++ b/Unity/Assets/Runtime/Rooms/RoomClient.cs @@ -1,14 +1,12 @@ -using Codice.Client.Commands; -using JetBrains.Annotations; -using System; +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 { @@ -243,18 +241,16 @@ public IEnumerable Peers public static float HeartbeatTimeout = 5f; public static float HeartbeatInterval = 1f; - // Parameters private and public for the reconnection process. - private enum ReconnectionStatus { Off, Reset, Received, Rejoining}; - private ReconnectionStatus reconnectionStatus = ReconnectionStatus.Off; - private float lastResetTime = 0; - private float timeSinceLastReset => Time.realtimeSinceStartup - lastResetTime; - private float lastRejoinTime = 0; - private float timeSinceLastRejoin => Time.realtimeSinceStartup - lastRejoinTime; - private Guid previousRoomGUID = Guid.Empty; - public static bool AttemtReconnecting = true; // Whether the RoomClient attemtps to reconnect to the server on connection loss. - public static float ReconnectTimeout = 5f; // How long a timeout can last until the reconnect procedure is triggered. - public static float ReconnectInterval = 5f; // The intervals in which reconnect attempts are done. - public static float RejoinInterval = 5f; // The intervals in which rejoin attempts are done. + 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(); @@ -278,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)"; + } } } } @@ -564,11 +568,6 @@ protected void ProcessMessage(ReferenceCountedSceneGraphMessage message) { pingReceived = Time.realtimeSinceStartup; - // Set flag for first ping after connection loss - if (reconnectionStatus == ReconnectionStatus.Reset) - reconnectionStatus = ReconnectionStatus.Received; - - PlayerNotifications.Delete(ref notification); var response = JsonUtility.FromJson(container.args); OnPingResponse(response); } @@ -671,7 +670,7 @@ public void ResetAndReconnect() public void ResetAndReconnect(ConnectionDefinition[] connectionDefinitions) { // Drop all connections - scene.ResetConnections(); + scene.ClearConnections(); // Reconnect all connections foreach (var item in connectionDefinitions) @@ -727,55 +726,19 @@ 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)); } - } - // Reconnection behaviour - // Test if dynamic reconnection is enabled - if(AttemtReconnecting) - { - // If enabled, check for current reconnection status - switch (reconnectionStatus) + if (heartbeatReceived > nextReconnectTimeout + && reconnectBehaviour != ReconnectBehaviour.None) { - // Reconnection off: If reconnect timeout is exceeded, old room GUID is stored, reset initiated, and next state is initiaded. - case ReconnectionStatus.Off: - if(heartbeatReceived > ReconnectTimeout) - { - previousRoomGUID = new Guid(room.UUID); - ResetAndReconnect(); - lastResetTime = Time.realtimeSinceStartup; - reconnectionStatus = ReconnectionStatus.Reset; - } - break; - // Reconnection Reset: Connection is reset. While no response has been received, reset connection again in regular intervals. - case ReconnectionStatus.Reset: - if (timeSinceLastReset > ReconnectInterval) - { - ResetAndReconnect(); - lastResetTime = Time.realtimeSinceStartup; - } - break; - // Reconnection Received: A response by Nexus has received. Attempt at rejoining room. - case ReconnectionStatus.Received: - Join(previousRoomGUID); - lastRejoinTime = Time.realtimeSinceStartup; - reconnectionStatus = ReconnectionStatus.Rejoining; - break; - // Reconnection Rejoining: Re-attempt rejoining room at regular intervals until succesful. - case ReconnectionStatus.Rejoining: - if(room.UUID == previousRoomGUID.ToString()) - { - reconnectionStatus = ReconnectionStatus.Off; - } - else if(timeSinceLastRejoin > RejoinInterval) - { - Join(previousRoomGUID); - lastRejoinTime = Time.realtimeSinceStartup; - } - break; + ResetAndReconnect(); + nextReconnectTimeout += reconnectTimeout; } } } @@ -857,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