diff --git a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs index 685b9111aa..d3bcac547f 100644 --- a/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs +++ b/com.unity.multiplayer.mlapi/Editor/NetworkManagerEditor.cs @@ -3,423 +3,436 @@ using UnityEditor; using UnityEngine; using UnityEditorInternal; -using MLAPI; using MLAPI.Configuration; using MLAPI.Transports; -[CustomEditor(typeof(NetworkManager), true)] -[CanEditMultipleObjects] -public class NetworkManagerEditor : Editor +namespace MLAPI.Editor { - // Properties - private SerializedProperty m_DontDestroyOnLoadProperty; - private SerializedProperty m_RunInBackgroundProperty; - private SerializedProperty m_LogLevelProperty; - - // NetworkConfig - private SerializedProperty m_NetworkConfigProperty; - - // NetworkConfig fields - private SerializedProperty m_ProtocolVersionProperty; - private SerializedProperty m_AllowRuntimeSceneChangesProperty; - private SerializedProperty m_NetworkTransportProperty; - private SerializedProperty m_ReceiveTickrateProperty; - private SerializedProperty m_NetworkTickIntervalSecProperty; - private SerializedProperty m_MaxReceiveEventsPerTickRateProperty; - private SerializedProperty m_EventTickrateProperty; - private SerializedProperty m_MaxObjectUpdatesPerTickProperty; - private SerializedProperty m_ClientConnectionBufferTimeoutProperty; - private SerializedProperty m_ConnectionApprovalProperty; - private SerializedProperty m_EnableTimeResyncProperty; - private SerializedProperty m_TimeResyncIntervalProperty; - private SerializedProperty m_EnableNetworkVariableProperty; - private SerializedProperty m_EnsureNetworkVariableLengthSafetyProperty; - private SerializedProperty m_CreatePlayerPrefabProperty; - private SerializedProperty m_ForceSamePrefabsProperty; - private SerializedProperty m_EnableSceneManagementProperty; - private SerializedProperty m_RecycleNetworkIdsProperty; - private SerializedProperty m_NetworkIdRecycleDelayProperty; - private SerializedProperty m_RpcHashSizeProperty; - private SerializedProperty m_LoadSceneTimeOutProperty; - private SerializedProperty m_EnableMessageBufferingProperty; - private SerializedProperty m_MessageBufferTimeoutProperty; - - private ReorderableList m_NetworkPrefabsList; - private ReorderableList m_RegisteredScenesList; - - private NetworkManager m_NetworkManager; - private bool m_Initialized; - - private readonly List m_TransportTypes = new List(); - private string[] m_TransportNames = { "Select transport..." }; - - private void ReloadTransports() + [CustomEditor(typeof(NetworkManager), true)] + [CanEditMultipleObjects] + public class NetworkManagerEditor : UnityEditor.Editor { - m_TransportTypes.Clear(); - - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach (var assembly in assemblies) + // Properties + private SerializedProperty m_DontDestroyOnLoadProperty; + private SerializedProperty m_RunInBackgroundProperty; + private SerializedProperty m_LogLevelProperty; + + // NetworkConfig + private SerializedProperty m_NetworkConfigProperty; + + // NetworkConfig fields + private SerializedProperty m_PlayerPrefabProperty; + private SerializedProperty m_ProtocolVersionProperty; + private SerializedProperty m_AllowRuntimeSceneChangesProperty; + private SerializedProperty m_NetworkTransportProperty; + private SerializedProperty m_ReceiveTickrateProperty; + private SerializedProperty m_NetworkTickIntervalSecProperty; + private SerializedProperty m_MaxReceiveEventsPerTickRateProperty; + private SerializedProperty m_EventTickrateProperty; + private SerializedProperty m_MaxObjectUpdatesPerTickProperty; + private SerializedProperty m_ClientConnectionBufferTimeoutProperty; + private SerializedProperty m_ConnectionApprovalProperty; + private SerializedProperty m_EnableTimeResyncProperty; + private SerializedProperty m_TimeResyncIntervalProperty; + private SerializedProperty m_EnableNetworkVariableProperty; + private SerializedProperty m_EnsureNetworkVariableLengthSafetyProperty; + private SerializedProperty m_ForceSamePrefabsProperty; + private SerializedProperty m_EnableSceneManagementProperty; + private SerializedProperty m_RecycleNetworkIdsProperty; + private SerializedProperty m_NetworkIdRecycleDelayProperty; + private SerializedProperty m_RpcHashSizeProperty; + private SerializedProperty m_LoadSceneTimeOutProperty; + private SerializedProperty m_EnableMessageBufferingProperty; + private SerializedProperty m_MessageBufferTimeoutProperty; + + private ReorderableList m_NetworkPrefabsList; + private ReorderableList m_RegisteredScenesList; + + private NetworkManager m_NetworkManager; + private bool m_Initialized; + + private readonly List m_TransportTypes = new List(); + private string[] m_TransportNames = { "Select transport..." }; + + private void ReloadTransports() { - var types = assembly.GetTypes(); + m_TransportTypes.Clear(); + + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var type in types) + foreach (var assembly in assemblies) { - if (type.IsSubclassOf(typeof(NetworkTransport))) + var types = assembly.GetTypes(); + + foreach (var type in types) { - m_TransportTypes.Add(type); + if (type.IsSubclassOf(typeof(NetworkTransport))) + { + m_TransportTypes.Add(type); + } } } - } - m_TransportNames = new string[m_TransportTypes.Count + 1]; - m_TransportNames[0] = "Select transport..."; + m_TransportNames = new string[m_TransportTypes.Count + 1]; + m_TransportNames[0] = "Select transport..."; - for (int i = 0; i < m_TransportTypes.Count; i++) - { - m_TransportNames[i + 1] = m_TransportTypes[i].Name; + for (int i = 0; i < m_TransportTypes.Count; i++) + { + m_TransportNames[i + 1] = m_TransportTypes[i].Name; + } } - } - private void Init() - { - if (m_Initialized) + private void Initialize() { - return; - } + if (m_Initialized) + { + return; + } - m_Initialized = true; - m_NetworkManager = (NetworkManager)target; - - // Base properties - m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy)); - m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground)); - m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel)); - m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)); - - // NetworkConfig properties - m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); - m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); - m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); - m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate"); - m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec"); - m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate"); - m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate"); - m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); - m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval"); - m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync"); - m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval"); - m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable"); - m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety"); - m_CreatePlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative("CreatePlayerPrefab"); - m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs"); - m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement"); - m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds"); - m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay"); - m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize"); - m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut"); - m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering"); - m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout"); - - - ReloadTransports(); - } + m_Initialized = true; + m_NetworkManager = (NetworkManager)target; + + // Base properties + m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy)); + m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground)); + m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel)); + m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)); + + // NetworkConfig properties + m_PlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.PlayerPrefab)); + m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); + m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); + m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); + m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate"); + m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec"); + m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate"); + m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate"); + m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); + m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval"); + m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync"); + m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval"); + m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable"); + m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety"); + m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs"); + m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement"); + m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds"); + m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay"); + m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize"); + m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut"); + m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering"); + m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout"); + + + ReloadTransports(); + } - private void CheckNullProperties() - { - // Base properties - m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy)); - m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground)); - m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel)); - m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)); - - // NetworkConfig properties - m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); - m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); - m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); - m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate"); - m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec"); - m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate"); - m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate"); - m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); - m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval"); - m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync"); - m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval"); - m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable"); - m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety"); - m_CreatePlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative("CreatePlayerPrefab"); - m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs"); - m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement"); - m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds"); - m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay"); - m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize"); - m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut"); - m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering"); - m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout"); - } + private void CheckNullProperties() + { + // Base properties + m_DontDestroyOnLoadProperty = serializedObject.FindProperty(nameof(NetworkManager.DontDestroy)); + m_RunInBackgroundProperty = serializedObject.FindProperty(nameof(NetworkManager.RunInBackground)); + m_LogLevelProperty = serializedObject.FindProperty(nameof(NetworkManager.LogLevel)); + m_NetworkConfigProperty = serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)); + + // NetworkConfig properties + m_PlayerPrefabProperty = m_NetworkConfigProperty.FindPropertyRelative(nameof(NetworkConfig.PlayerPrefab)); + m_ProtocolVersionProperty = m_NetworkConfigProperty.FindPropertyRelative("ProtocolVersion"); + m_AllowRuntimeSceneChangesProperty = m_NetworkConfigProperty.FindPropertyRelative("AllowRuntimeSceneChanges"); + m_NetworkTransportProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTransport"); + m_ReceiveTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("ReceiveTickrate"); + m_NetworkTickIntervalSecProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkTickIntervalSec"); + m_MaxReceiveEventsPerTickRateProperty = m_NetworkConfigProperty.FindPropertyRelative("MaxReceiveEventsPerTickRate"); + m_EventTickrateProperty = m_NetworkConfigProperty.FindPropertyRelative("EventTickrate"); + m_ClientConnectionBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("ClientConnectionBufferTimeout"); + m_ConnectionApprovalProperty = m_NetworkConfigProperty.FindPropertyRelative("ConnectionApproval"); + m_EnableTimeResyncProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableTimeResync"); + m_TimeResyncIntervalProperty = m_NetworkConfigProperty.FindPropertyRelative("TimeResyncInterval"); + m_EnableNetworkVariableProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableNetworkVariable"); + m_EnsureNetworkVariableLengthSafetyProperty = m_NetworkConfigProperty.FindPropertyRelative("EnsureNetworkVariableLengthSafety"); + m_ForceSamePrefabsProperty = m_NetworkConfigProperty.FindPropertyRelative("ForceSamePrefabs"); + m_EnableSceneManagementProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableSceneManagement"); + m_RecycleNetworkIdsProperty = m_NetworkConfigProperty.FindPropertyRelative("RecycleNetworkIds"); + m_NetworkIdRecycleDelayProperty = m_NetworkConfigProperty.FindPropertyRelative("NetworkIdRecycleDelay"); + m_RpcHashSizeProperty = m_NetworkConfigProperty.FindPropertyRelative("RpcHashSize"); + m_LoadSceneTimeOutProperty = m_NetworkConfigProperty.FindPropertyRelative("LoadSceneTimeOut"); + m_EnableMessageBufferingProperty = m_NetworkConfigProperty.FindPropertyRelative("EnableMessageBuffering"); + m_MessageBufferTimeoutProperty = m_NetworkConfigProperty.FindPropertyRelative("MessageBufferTimeout"); + } - private void OnEnable() - { - m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true); - m_NetworkPrefabsList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => + private void OnEnable() { - for (int i = 0; i < m_NetworkManager.NetworkConfig.NetworkPrefabs.Count; i++) + m_NetworkPrefabsList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.NetworkPrefabs)), true, true, true, true); + m_NetworkPrefabsList.elementHeightCallback = index => { - // Find the first playerPrefab - if (m_NetworkManager.NetworkConfig.NetworkPrefabs[i].IsPlayer) + var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index); + var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override)); + var networkOverrideInt = networkOverrideProp.enumValueIndex; + + return 10 + (networkOverrideInt == 0 ? EditorGUIUtility.singleLineHeight : (EditorGUIUtility.singleLineHeight * 2) + 5); + }; + m_NetworkPrefabsList.drawElementCallback = (rect, index, isActive, isFocused) => + { + rect.y += 5; + + var networkPrefab = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index); + var networkPrefabProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Prefab)); + var networkSourceHashProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.SourceHashToOverride)); + var networkSourcePrefabProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.SourcePrefabToOverride)); + var networkTargetPrefabProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.OverridingTargetPrefab)); + var networkOverrideProp = networkPrefab.FindPropertyRelative(nameof(NetworkPrefab.Override)); + var networkOverrideInt = networkOverrideProp.enumValueIndex; + var networkOverrideEnum = (NetworkPrefabOverride)networkOverrideInt; + EditorGUI.LabelField(new Rect(rect.x + rect.width - 70, rect.y, 60, EditorGUIUtility.singleLineHeight), "Override"); + if (networkOverrideEnum == NetworkPrefabOverride.None) { - // Iterate over all other and set player prefab to false - for (int j = 0; j < m_NetworkManager.NetworkConfig.NetworkPrefabs.Count; j++) + if (EditorGUI.Toggle(new Rect(rect.x + rect.width - 15, rect.y, 10, EditorGUIUtility.singleLineHeight), false)) { - if (j != i && m_NetworkManager.NetworkConfig.NetworkPrefabs[j].IsPlayer) - { - m_NetworkManager.NetworkConfig.NetworkPrefabs[j].IsPlayer = false; - } + networkOverrideProp.enumValueIndex = (int)NetworkPrefabOverride.Prefab; } - - break; } - } - - var element = m_NetworkPrefabsList.serializedProperty.GetArrayElementAtIndex(index); - int firstLabelWidth = 50; - int secondLabelWidth = 140; - float secondFieldWidth = 10; - int reduceFirstWidth = 45; - - EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Prefab"); - EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - secondLabelWidth - secondFieldWidth - reduceFirstWidth, - EditorGUIUtility.singleLineHeight), element.FindPropertyRelative(nameof(NetworkPrefab.Prefab)), GUIContent.none); - - EditorGUI.LabelField(new Rect(rect.width - secondLabelWidth - secondFieldWidth, rect.y, secondLabelWidth, EditorGUIUtility.singleLineHeight), "Default Player Prefab"); - - int playerPrefabIndex = -1; - - for (int i = 0; i < m_NetworkManager.NetworkConfig.NetworkPrefabs.Count; i++) - { - if (m_NetworkManager.NetworkConfig.NetworkPrefabs[i].IsPlayer) + else { - playerPrefabIndex = i; - break; + if (!EditorGUI.Toggle(new Rect(rect.x + rect.width - 15, rect.y, 10, EditorGUIUtility.singleLineHeight), true)) + { + networkOverrideProp.enumValueIndex = 0; + networkOverrideEnum = NetworkPrefabOverride.None; + } } - } - - using (new EditorGUI.DisabledScope(playerPrefabIndex != -1 && playerPrefabIndex != index)) - { - EditorGUI.PropertyField(new Rect(rect.width - secondFieldWidth, rect.y, secondFieldWidth, - EditorGUIUtility.singleLineHeight), element.FindPropertyRelative(nameof(NetworkPrefab.IsPlayer)), GUIContent.none); - } - }; - - m_NetworkPrefabsList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "NetworkPrefabs"); }; + if (networkOverrideEnum == NetworkPrefabOverride.None) + { + EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width - 80, EditorGUIUtility.singleLineHeight), networkPrefabProp, GUIContent.none); + } + else + { + networkOverrideProp.enumValueIndex = GUI.Toolbar(new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight), networkOverrideInt - 1, new[] { "Prefab", "Hash" }) + 1; - m_RegisteredScenesList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredScenes)), true, true, true, true); - m_RegisteredScenesList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => - { - var element = m_RegisteredScenesList.serializedProperty.GetArrayElementAtIndex(index); - int firstLabelWidth = 50; - int padding = 20; + if (networkOverrideEnum == NetworkPrefabOverride.Prefab) + { + EditorGUI.PropertyField(new Rect(rect.x + 110, rect.y, rect.width - 190, EditorGUIUtility.singleLineHeight), networkSourcePrefabProp, GUIContent.none); + } + else + { + EditorGUI.PropertyField(new Rect(rect.x + 110, rect.y, rect.width - 190, EditorGUIUtility.singleLineHeight), networkSourceHashProp, GUIContent.none); + } - EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Name"); - EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding, - EditorGUIUtility.singleLineHeight), element, GUIContent.none); - }; + rect.y += EditorGUIUtility.singleLineHeight + 5; - m_RegisteredScenesList.drawHeaderCallback = (Rect rect) => { EditorGUI.LabelField(rect, "Registered Scene Names"); }; - } + EditorGUI.LabelField(new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight), "Overriding Prefab"); + EditorGUI.PropertyField(new Rect(rect.x + 110, rect.y, rect.width - 110, EditorGUIUtility.singleLineHeight), networkTargetPrefabProp, GUIContent.none); + } + }; + m_NetworkPrefabsList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "NetworkPrefabs"); - public override void OnInspectorGUI() - { - Init(); - CheckNullProperties(); + m_RegisteredScenesList = new ReorderableList(serializedObject, serializedObject.FindProperty(nameof(NetworkManager.NetworkConfig)).FindPropertyRelative(nameof(NetworkConfig.RegisteredScenes)), true, true, true, true); + m_RegisteredScenesList.drawElementCallback = (rect, index, isActive, isFocused) => + { + var element = m_RegisteredScenesList.serializedProperty.GetArrayElementAtIndex(index); + int firstLabelWidth = 50; + int padding = 20; + + EditorGUI.LabelField(new Rect(rect.x, rect.y, firstLabelWidth, EditorGUIUtility.singleLineHeight), "Name"); + EditorGUI.PropertyField(new Rect(rect.x + firstLabelWidth, rect.y, rect.width - firstLabelWidth - padding, EditorGUIUtility.singleLineHeight), element, GUIContent.none); + }; + m_RegisteredScenesList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Registered Scene Names"); + } + public override void OnInspectorGUI() { - var iterator = serializedObject.GetIterator(); + Initialize(); + CheckNullProperties(); - for (bool enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false) { - using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath)) + var iterator = serializedObject.GetIterator(); + + for (bool enterChildren = true; iterator.NextVisible(enterChildren); enterChildren = false) { - EditorGUILayout.PropertyField(iterator, false); + using (new EditorGUI.DisabledScope("m_Script" == iterator.propertyPath)) + { + EditorGUILayout.PropertyField(iterator, false); + } } } - } - if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient) - { - serializedObject.Update(); - EditorGUILayout.PropertyField(m_DontDestroyOnLoadProperty); - EditorGUILayout.PropertyField(m_RunInBackgroundProperty); - EditorGUILayout.PropertyField(m_LogLevelProperty); - - EditorGUILayout.Space(); - m_NetworkPrefabsList.DoLayoutList(); - - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) + if (!m_NetworkManager.IsServer && !m_NetworkManager.IsClient) { - m_RegisteredScenesList.DoLayoutList(); + serializedObject.Update(); + EditorGUILayout.PropertyField(m_DontDestroyOnLoadProperty); + EditorGUILayout.PropertyField(m_RunInBackgroundProperty); + EditorGUILayout.PropertyField(m_LogLevelProperty); EditorGUILayout.Space(); - } + EditorGUILayout.PropertyField(m_PlayerPrefabProperty); + EditorGUILayout.Space(); + + m_NetworkPrefabsList.DoLayoutList(); + EditorGUILayout.Space(); - EditorGUILayout.LabelField("General", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_ProtocolVersionProperty); + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) + { + m_RegisteredScenesList.DoLayoutList(); + EditorGUILayout.Space(); + } - EditorGUILayout.PropertyField(m_NetworkTransportProperty); - if (m_NetworkTransportProperty.objectReferenceValue == null) - { - EditorGUILayout.HelpBox("You have no transport selected. A transport is required for the MLAPI to work. Which one do you want?", MessageType.Warning); + EditorGUILayout.LabelField("General", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_ProtocolVersionProperty); - int selection = EditorGUILayout.Popup(0, m_TransportNames); + EditorGUILayout.PropertyField(m_NetworkTransportProperty); - if (selection > 0) + if (m_NetworkTransportProperty.objectReferenceValue == null) { - ReloadTransports(); + EditorGUILayout.HelpBox("You have no transport selected. A transport is required for the MLAPI to work. Which one do you want?", MessageType.Warning); - var transportComponent = m_NetworkManager.gameObject.GetComponent(m_TransportTypes[selection - 1]); + int selection = EditorGUILayout.Popup(0, m_TransportNames); - if (transportComponent == null) + if (selection > 0) { - transportComponent = m_NetworkManager.gameObject.AddComponent(m_TransportTypes[selection - 1]); - } + ReloadTransports(); - m_NetworkTransportProperty.objectReferenceValue = transportComponent; + var transportComponent = m_NetworkManager.gameObject.GetComponent(m_TransportTypes[selection - 1]); - Repaint(); - } - } + if (transportComponent == null) + { + transportComponent = m_NetworkManager.gameObject.AddComponent(m_TransportTypes[selection - 1]); + } - EditorGUILayout.PropertyField(m_EnableTimeResyncProperty); + m_NetworkTransportProperty.objectReferenceValue = transportComponent; - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableTimeResync)) - { - EditorGUILayout.PropertyField(m_TimeResyncIntervalProperty); - } + Repaint(); + } + } - EditorGUILayout.LabelField("Performance", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_ReceiveTickrateProperty); - EditorGUILayout.PropertyField(m_NetworkTickIntervalSecProperty); - EditorGUILayout.PropertyField(m_MaxReceiveEventsPerTickRateProperty); - EditorGUILayout.PropertyField(m_EventTickrateProperty); - EditorGUILayout.PropertyField(m_EnableNetworkVariableProperty); + EditorGUILayout.PropertyField(m_EnableTimeResyncProperty); - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableNetworkVariable)) - { - if (m_MaxObjectUpdatesPerTickProperty != null) + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableTimeResync)) { - EditorGUILayout.PropertyField(m_MaxObjectUpdatesPerTickProperty); + EditorGUILayout.PropertyField(m_TimeResyncIntervalProperty); } - EditorGUILayout.PropertyField(m_EnsureNetworkVariableLengthSafetyProperty); - } - - EditorGUILayout.LabelField("Connection", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_ConnectionApprovalProperty); + EditorGUILayout.LabelField("Performance", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_ReceiveTickrateProperty); + EditorGUILayout.PropertyField(m_NetworkTickIntervalSecProperty); + EditorGUILayout.PropertyField(m_MaxReceiveEventsPerTickRateProperty); + EditorGUILayout.PropertyField(m_EventTickrateProperty); + EditorGUILayout.PropertyField(m_EnableNetworkVariableProperty); - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.ConnectionApproval)) - { - EditorGUILayout.PropertyField(m_ClientConnectionBufferTimeoutProperty); - } + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableNetworkVariable)) + { + if (m_MaxObjectUpdatesPerTickProperty != null) + { + EditorGUILayout.PropertyField(m_MaxObjectUpdatesPerTickProperty); + } - EditorGUILayout.LabelField("Spawning", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_CreatePlayerPrefabProperty); - EditorGUILayout.PropertyField(m_ForceSamePrefabsProperty); + EditorGUILayout.PropertyField(m_EnsureNetworkVariableLengthSafetyProperty); + } - EditorGUILayout.PropertyField(m_RecycleNetworkIdsProperty); + EditorGUILayout.LabelField("Connection", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_ConnectionApprovalProperty); - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.RecycleNetworkIds)) - { - EditorGUILayout.PropertyField(m_NetworkIdRecycleDelayProperty); - } + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.ConnectionApproval)) + { + EditorGUILayout.PropertyField(m_ClientConnectionBufferTimeoutProperty); + } - EditorGUILayout.PropertyField(m_EnableMessageBufferingProperty); + EditorGUILayout.LabelField("Spawning", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_ForceSamePrefabsProperty); - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableMessageBuffering)) - { - EditorGUILayout.PropertyField(m_MessageBufferTimeoutProperty); - } - EditorGUILayout.LabelField("Bandwidth", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_RpcHashSizeProperty); + EditorGUILayout.PropertyField(m_RecycleNetworkIdsProperty); - EditorGUILayout.LabelField("Scene Management", EditorStyles.boldLabel); - EditorGUILayout.PropertyField(m_EnableSceneManagementProperty); + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.RecycleNetworkIds)) + { + EditorGUILayout.PropertyField(m_NetworkIdRecycleDelayProperty); + } - using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) - { - EditorGUILayout.PropertyField(m_LoadSceneTimeOutProperty); - EditorGUILayout.PropertyField(m_AllowRuntimeSceneChangesProperty); - } + EditorGUILayout.PropertyField(m_EnableMessageBufferingProperty); - serializedObject.ApplyModifiedProperties(); + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableMessageBuffering)) + { + EditorGUILayout.PropertyField(m_MessageBufferTimeoutProperty); + } + EditorGUILayout.LabelField("Bandwidth", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_RpcHashSizeProperty); - // Start buttons below - { - string buttonDisabledReasonSuffix = ""; + EditorGUILayout.LabelField("Scene Management", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(m_EnableSceneManagementProperty); - if (!EditorApplication.isPlaying) + using (new EditorGUI.DisabledScope(!m_NetworkManager.NetworkConfig.EnableSceneManagement)) { - buttonDisabledReasonSuffix = ". This can only be done in play mode"; - GUI.enabled = false; + EditorGUILayout.PropertyField(m_LoadSceneTimeOutProperty); + EditorGUILayout.PropertyField(m_AllowRuntimeSceneChangesProperty); } - if (GUILayout.Button(new GUIContent("Start Host", "Starts a host instance" + buttonDisabledReasonSuffix))) - { - m_NetworkManager.StartHost(); - } + serializedObject.ApplyModifiedProperties(); - if (GUILayout.Button(new GUIContent("Start Server", "Starts a server instance" + buttonDisabledReasonSuffix))) - { - m_NetworkManager.StartServer(); - } - if (GUILayout.Button(new GUIContent("Start Client", "Starts a client instance" + buttonDisabledReasonSuffix))) + // Start buttons below { - m_NetworkManager.StartClient(); - } + string buttonDisabledReasonSuffix = ""; - if (!EditorApplication.isPlaying) - { - GUI.enabled = true; - } - } - } - else - { - string instanceType = string.Empty; + if (!EditorApplication.isPlaying) + { + buttonDisabledReasonSuffix = ". This can only be done in play mode"; + GUI.enabled = false; + } - if (m_NetworkManager.IsHost) - { - instanceType = "Host"; - } - else if (m_NetworkManager.IsServer) - { - instanceType = "Server"; - } - else if (m_NetworkManager.IsClient) - { - instanceType = "Client"; - } + if (GUILayout.Button(new GUIContent("Start Host", "Starts a host instance" + buttonDisabledReasonSuffix))) + { + m_NetworkManager.StartHost(); + } + + if (GUILayout.Button(new GUIContent("Start Server", "Starts a server instance" + buttonDisabledReasonSuffix))) + { + m_NetworkManager.StartServer(); + } - EditorGUILayout.HelpBox("You cannot edit the NetworkConfig when a " + instanceType + " is running.", MessageType.Info); + if (GUILayout.Button(new GUIContent("Start Client", "Starts a client instance" + buttonDisabledReasonSuffix))) + { + m_NetworkManager.StartClient(); + } - if (GUILayout.Button(new GUIContent("Stop " + instanceType, "Stops the " + instanceType + " instance."))) + if (!EditorApplication.isPlaying) + { + GUI.enabled = true; + } + } + } + else { + string instanceType = string.Empty; + if (m_NetworkManager.IsHost) { - m_NetworkManager.StopHost(); + instanceType = "Host"; } else if (m_NetworkManager.IsServer) { - m_NetworkManager.StopServer(); + instanceType = "Server"; } else if (m_NetworkManager.IsClient) { - m_NetworkManager.StopClient(); + instanceType = "Client"; + } + + EditorGUILayout.HelpBox("You cannot edit the NetworkConfig when a " + instanceType + " is running.", MessageType.Info); + + if (GUILayout.Button(new GUIContent("Stop " + instanceType, "Stops the " + instanceType + " instance."))) + { + if (m_NetworkManager.IsHost) + { + m_NetworkManager.StopHost(); + } + else if (m_NetworkManager.IsServer) + { + m_NetworkManager.StopServer(); + } + else if (m_NetworkManager.IsClient) + { + m_NetworkManager.StopClient(); + } } } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs index 278c6e7b03..2e74c3f7fe 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkConfig.cs @@ -42,22 +42,23 @@ public class NetworkConfig public bool AllowRuntimeSceneChanges = false; /// - /// A list of spawnable prefabs + /// The default player prefab /// - [Tooltip("The prefabs that can be spawned across the network")] - public List NetworkPrefabs = new List(); + [Tooltip("When set, NetworkManager will automatically create and spawn the assigned player prefab. This can be overridden by adding it to the NetworkPrefabs list and selecting override.")] + public GameObject PlayerPrefab; /// - /// The default player prefab + /// A list of spawnable prefabs /// - [SerializeReference] - internal uint PlayerPrefabHash; + [SerializeField] + [Tooltip("The prefabs that can be spawned across the network")] + internal List NetworkPrefabs = new List(); /// - /// Whether or not a player object should be created by default. This value can be overridden on a case by case basis with ConnectionApproval. + /// This dictionary provides a quick way to check and see if a NetworkPrefab has a NetworkPrefab override. + /// Generated at runtime and OnValidate /// - [Tooltip("Whether or not a player object should be created by default. This value can be overridden on a case by case basis with ConnectionApproval.")] - public bool CreatePlayerPrefab = true; + internal Dictionary NetworkPrefabOverrideLinks = new Dictionary(); /// /// Amount of times per second the receive queue is emptied and all messages inside are processed. @@ -300,10 +301,10 @@ public ulong GetConfig(bool cache = true) if (ForceSamePrefabs) { - var sortedPrefabList = NetworkPrefabs.OrderBy(x => x.Hash).ToList(); - for (int i = 0; i < sortedPrefabList.Count; i++) + var sortedDictionary = NetworkPrefabOverrideLinks.OrderBy(x => x.Key); + foreach (var sortedEntry in sortedDictionary) { - writer.WriteUInt32Packed(sortedPrefabList[i].Hash); + writer.WriteUInt32Packed(sortedEntry.Key); } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkPrefab.cs b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkPrefab.cs index c7292b0722..82787a9241 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkPrefab.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Configuration/NetworkPrefab.cs @@ -4,49 +4,43 @@ namespace MLAPI.Configuration { + internal enum NetworkPrefabOverride + { + None, + Prefab, + Hash + } + /// /// Class that represents a NetworkPrefab /// [Serializable] - public class NetworkPrefab + internal class NetworkPrefab { + /// + /// The override setttings for this NetworkPrefab + /// + public NetworkPrefabOverride Override; + /// /// Asset reference of the network prefab /// public GameObject Prefab; /// - /// Whether or not this is a player prefab + /// Used when prefab is selected for the source prefab to override value (i.e. direct reference, the prefab is within the same project) + /// We keep a separate value as the user might want to have something different than the default Prefab for the SourcePrefabToOverride /// - public bool IsPlayer; - - internal uint Hash - { - get - { - if (Prefab == null) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkPrefab)} does not have a prefab assigned"); - } + public GameObject SourcePrefabToOverride; - return 0; - } - - var networkObject = Prefab.GetComponent(); - if (networkObject == null) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"{nameof(NetworkPrefab)} {Prefab.name} does not have a {nameof(NetworkObject)} component"); - } - - return 0; - } + /// + /// Used when hash is selected for the source prefab to override value (i.e. a direct reference is not possible such as in a multi-project pattern) + /// + public uint SourceHashToOverride; - return networkObject.GlobalObjectIdHash; - } - } + /// + /// The prefab to replace (override) the source prefab with + /// + public GameObject OverridingTargetPrefab; } } diff --git a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs index 450e0e3b42..2419eb0f87 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Core/NetworkManager.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using System.Diagnostics; using UnityEngine; -using System.Linq; using MLAPI.Logging; using MLAPI.Configuration; using MLAPI.Internal; @@ -216,7 +215,7 @@ private void OnValidate() { if (NetworkConfig == null) { - return; //May occur when the component is added + return; // May occur when the component is added } if (GetComponentInChildren() != null) @@ -247,39 +246,58 @@ private void OnValidate() }; } + // During OnValidate we will always clear out NetworkPrefabOverrideLinks and rebuild it + NetworkConfig.NetworkPrefabOverrideLinks.Clear(); + + // Check network prefabs and assign to dictionary for quick look up for (int i = 0; i < NetworkConfig.NetworkPrefabs.Count; i++) { if (NetworkConfig.NetworkPrefabs[i] != null && NetworkConfig.NetworkPrefabs[i].Prefab != null) { - if (NetworkConfig.NetworkPrefabs[i].Prefab.GetComponent() == null) + var networkObject = NetworkConfig.NetworkPrefabs[i].Prefab.GetComponent(); + if (networkObject == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) { NetworkLog.LogWarning($"{nameof(NetworkPrefab)} [{i}] does not have a {nameof(NetworkObject)} component"); } } - } - } + else + { + // Default to the standard NetworkPrefab.Prefab's NetworkObject first + var globalObjectIdHash = networkObject.GlobalObjectIdHash; - int playerPrefabCount = NetworkConfig.NetworkPrefabs.Count(x => x.IsPlayer); + // Now check to see if it has an override + switch (NetworkConfig.NetworkPrefabs[i].Override) + { + case NetworkPrefabOverride.Prefab: + { + if (NetworkConfig.NetworkPrefabs[i].SourcePrefabToOverride == null && NetworkConfig.NetworkPrefabs[i].Prefab != null) + { + NetworkConfig.NetworkPrefabs[i].SourcePrefabToOverride = NetworkConfig.NetworkPrefabs[i].Prefab; + } + globalObjectIdHash = NetworkConfig.NetworkPrefabs[i].SourcePrefabToOverride.GetComponent().GlobalObjectIdHash; + } + break; + case NetworkPrefabOverride.Hash: + globalObjectIdHash = NetworkConfig.NetworkPrefabs[i].SourceHashToOverride; + break; + } - if (playerPrefabCount == 0 && !NetworkConfig.ConnectionApproval && NetworkConfig.CreatePlayerPrefab) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"There is no {nameof(NetworkPrefab)} marked as a {nameof(NetworkPrefab.IsPlayer)}"); - } - } - else if (playerPrefabCount > 1) - { - if (NetworkLog.CurrentLogLevel <= LogLevel.Normal) - { - NetworkLog.LogWarning($"Only one {nameof(NetworkPrefab)} can be marked as a {nameof(NetworkPrefab.IsPlayer)}"); + // Add to the NetworkPrefabOverrideLinks or handle a new (blank) entries + if (!NetworkConfig.NetworkPrefabOverrideLinks.ContainsKey(globalObjectIdHash)) + { + NetworkConfig.NetworkPrefabOverrideLinks.Add(globalObjectIdHash, NetworkConfig.NetworkPrefabs[i]); + } + else + { + // Duplicate entries can happen when adding a new entry into a list of existing entries + // Either this is user error or a new entry, either case we replace it with a new, blank, NetworkPrefab under this condition + NetworkConfig.NetworkPrefabs[i] = new NetworkPrefab(); + } + } } } - - var networkPrefab = NetworkConfig.NetworkPrefabs.FirstOrDefault(x => x.IsPlayer); - NetworkConfig.PlayerPrefabHash = networkPrefab?.Hash ?? (uint)0; } #endif @@ -326,7 +344,7 @@ private void Init(bool server) return; } - //This 'if' should never enter + // This 'if' should never enter if (NetworkTickSystem != null) { NetworkTickSystem.Dispose(); @@ -335,7 +353,7 @@ private void Init(bool server) NetworkTickSystem = new NetworkTickSystem(NetworkConfig.NetworkTickIntervalSec); - //This should never happen, but in the event that it does there should be (at a minimum) a unity error logged. + // This should never happen, but in the event that it does there should be (at a minimum) a unity error logged. if (RpcQueueContainer != null) { UnityEngine.Debug.LogError("Init was invoked, but rpcQueueContainer was already initialized! (destroying previous instance)"); @@ -343,8 +361,8 @@ private void Init(bool server) RpcQueueContainer = null; } - //The RpcQueueContainer must be initialized within the Init method ONLY - //It should ONLY be shutdown and destroyed in the Shutdown method (other than just above) + // The RpcQueueContainer must be initialized within the Init method ONLY + // It should ONLY be shutdown and destroyed in the Shutdown method (other than just above) RpcQueueContainer = new RpcQueueContainer(this); // Register INetworkUpdateSystem (always register this after rpcQueueContainer has been instantiated) @@ -365,23 +383,98 @@ private void Init(bool server) SceneManager.SetCurrentSceneIndex(); } + // This is used to remove entries not needed or invalid + var removeEmptyPrefabs = new List(); + + // Always clear our prefab override links before building + NetworkConfig.NetworkPrefabOverrideLinks.Clear(); + + // Build the NetworkPrefabOverrideLinks dictionary for (int i = 0; i < NetworkConfig.NetworkPrefabs.Count; i++) { if (NetworkConfig.NetworkPrefabs[i] == null || NetworkConfig.NetworkPrefabs[i].Prefab == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { - NetworkLog.LogError($"{nameof(NetworkPrefab)} cannot be null ({nameof(NetworkPrefab)} at index: {i})"); + NetworkLog.LogWarning($"{nameof(NetworkPrefab)} cannot be null ({nameof(NetworkPrefab)} at index: {i})"); } + + // Provide the name of the prefab with issues so the user can more easily find the prefab and fix it + UnityEngine.Debug.LogWarning($"{nameof(NetworkPrefab)} (\"{NetworkConfig.NetworkPrefabs[i].Prefab.name}\") will be removed and ignored."); + removeEmptyPrefabs.Add(i); + + continue; } else if (NetworkConfig.NetworkPrefabs[i].Prefab.GetComponent() == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { - NetworkLog.LogError($"{nameof(NetworkPrefab)} (\"{NetworkConfig.NetworkPrefabs[i].Prefab.name}\") is missing a {nameof(NetworkObject)} component"); + NetworkLog.LogWarning($"{nameof(NetworkPrefab)} (\"{NetworkConfig.NetworkPrefabs[i].Prefab.name}\") is missing a {nameof(NetworkObject)} component"); + } + + // Provide the name of the prefab with issues so the user can more easily find the prefab and fix it + UnityEngine.Debug.LogWarning($"{nameof(NetworkPrefab)} (\"{NetworkConfig.NetworkPrefabs[i].Prefab.name}\") will be removed and ignored."); + removeEmptyPrefabs.Add(i); + + continue; + } + + var networkObject = NetworkConfig.NetworkPrefabs[i].Prefab.GetComponent(); + + // Assign the appropriate GlobalObjectIdHash to the appropriate NetworkPrefab + if (!NetworkConfig.NetworkPrefabOverrideLinks.ContainsKey(networkObject.GlobalObjectIdHash)) + { + switch (NetworkConfig.NetworkPrefabs[i].Override) + { + default: + case NetworkPrefabOverride.None: + NetworkConfig.NetworkPrefabOverrideLinks.Add(networkObject.GlobalObjectIdHash, NetworkConfig.NetworkPrefabs[i]); + break; + case NetworkPrefabOverride.Prefab: + NetworkConfig.NetworkPrefabOverrideLinks.Add(NetworkConfig.NetworkPrefabs[i].SourcePrefabToOverride.GetComponent().GlobalObjectIdHash, NetworkConfig.NetworkPrefabs[i]); + break; + case NetworkPrefabOverride.Hash: + NetworkConfig.NetworkPrefabOverrideLinks.Add(NetworkConfig.NetworkPrefabs[i].SourceHashToOverride, NetworkConfig.NetworkPrefabs[i]); + break; + } + } + else + { + // This should never happen, but in the case it somehow does log an error and remove the duplicate entry + UnityEngine.Debug.LogError($"{nameof(NetworkPrefab)} (\"{NetworkConfig.NetworkPrefabs[i].Prefab.name}\") has a duplicate {nameof(NetworkObject.GlobalObjectIdHash)} {networkObject.GlobalObjectIdHash} entry! Removing entry from list!"); + removeEmptyPrefabs.Add(i); + } + } + + // If we have a player prefab, then we need to verify it is in the list of NetworkPrefabOverrideLinks for client side spawning. + if (NetworkConfig.PlayerPrefab != null) + { + var playerPrefabNetworkObject = NetworkConfig.PlayerPrefab.GetComponent(); + if (playerPrefabNetworkObject != null) + { + //In the event there is no NetworkPrefab entry (i.e. no override for default player prefab) + if (!NetworkConfig.NetworkPrefabOverrideLinks.ContainsKey(playerPrefabNetworkObject.GlobalObjectIdHash)) + { + //Then add a new entry for the player prefab + var playerNetworkPrefab = new NetworkPrefab(); + playerNetworkPrefab.Prefab = NetworkConfig.PlayerPrefab; + NetworkConfig.NetworkPrefabs.Insert(0, playerNetworkPrefab); + NetworkConfig.NetworkPrefabOverrideLinks.Add(playerPrefabNetworkObject.GlobalObjectIdHash, playerNetworkPrefab); } } + else + { + // Provide the name of the prefab with issues so the user can more easily find the prefab and fix it + UnityEngine.Debug.LogError($"{nameof(NetworkConfig.PlayerPrefab)} (\"{NetworkConfig.PlayerPrefab.name}\") has no NetworkObject assigned to it!."); + } + } + + // Clear out anything that is invalid or not used (for invalid entries we already logged warnings to the user earlier) + foreach (var networkPrefabIndexToRemove in removeEmptyPrefabs) + { + NetworkConfig.NetworkPrefabs.RemoveAt(networkPrefabIndexToRemove); } + removeEmptyPrefabs.Clear(); NetworkConfig.NetworkTransport.OnTransportEvent += HandleRawTransportPoll; @@ -604,7 +697,7 @@ public SocketTasks StartHost() } else { - HandleApproval(ServerClientId, NetworkConfig.CreatePlayerPrefab, null, true, null, null); + HandleApproval(ServerClientId, NetworkConfig.PlayerPrefab != null, null, true, null, null); } SpawnManager.ServerSpawnSceneObjectsOnStartSweep(); @@ -1477,7 +1570,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? if (createPlayerObject) { - var networkObject = SpawnManager.CreateLocalNetworkObject(false, playerPrefabHash ?? NetworkConfig.PlayerPrefabHash, ownerClientId, null, position, rotation); + var networkObject = SpawnManager.CreateLocalNetworkObject(false, playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash, ownerClientId, null, position, rotation); SpawnManager.SpawnNetworkObjectLocally(networkObject, SpawnManager.GetNetworkObjectId(), false, true, ownerClientId, null, false, 0, false, false); ConnectedClients[ownerClientId].PlayerObject = networkObject; @@ -1566,7 +1659,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? OnClientConnectedCallback?.Invoke(ownerClientId); - if (!createPlayerObject || (playerPrefabHash == null && NetworkConfig.PlayerPrefabHash == 0)) + if (!createPlayerObject || (playerPrefabHash == null && NetworkConfig.PlayerPrefab == null)) { return; } @@ -1594,7 +1687,7 @@ internal void HandleApproval(ulong ownerClientId, bool createPlayerObject, uint? // This is not a scene object writer.WriteBool(false); - writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefabHash); + writer.WriteUInt32Packed(playerPrefabHash ?? NetworkConfig.PlayerPrefab.GetComponent().GlobalObjectIdHash); if (ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning == null || ConnectedClients[ownerClientId].PlayerObject.IncludeTransformWhenSpawning(ownerClientId)) { diff --git a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs index 448909768c..cbca3be8d6 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Messaging/InternalMessageHandler.cs @@ -81,7 +81,7 @@ public void HandleConnectionRequest(ulong clientId, Stream stream) } else { - NetworkManager.HandleApproval(clientId, NetworkManager.NetworkConfig.CreatePlayerPrefab, null, true, null, null); + NetworkManager.HandleApproval(clientId, NetworkManager.NetworkConfig.PlayerPrefab != null, null, true, null, null); } } #if DEVELOPMENT_BUILD || UNITY_EDITOR diff --git a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs index c4d051b0f1..7f322ae693 100644 --- a/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/SceneManagement/NetworkSceneManager.cs @@ -304,31 +304,31 @@ private void OnSceneUnloadServer(Guid switchSceneGuid) } else { - //We do have a parent + // We do have a parent writer.WriteBool(true); - //Write the parent's NetworkObjectId to be used for linking back to the child + // Write the parent's NetworkObjectId to be used for linking back to the child writer.WriteUInt64Packed(parentNetworkObject.NetworkObjectId); } writer.WriteUInt32Packed(newSceneObjects[i].GlobalObjectIdHash); if (newSceneObjects[i].IncludeTransformWhenSpawning == null || newSceneObjects[i].IncludeTransformWhenSpawning(newSceneObjects[i].OwnerClientId)) { - //Set the position and rotation data marker to true (i.e. flag to know, when reading from the stream, that postion and roation data follows). + // Set the position and rotation data marker to true (i.e. flag to know, when reading from the stream, that postion and roation data follows). writer.WriteBool(true); - //Write position + // Write position writer.WriteSinglePacked(newSceneObjects[i].transform.position.x); writer.WriteSinglePacked(newSceneObjects[i].transform.position.y); writer.WriteSinglePacked(newSceneObjects[i].transform.position.z); - //Write rotation + // Write rotation writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.x); writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.y); writer.WriteSinglePacked(newSceneObjects[i].transform.rotation.eulerAngles.z); } else { - //Set the position and rotation data marker to false (i.e. flag to know, when reading from the stream, that postion and roation data *was not included*) + // Set the position and rotation data marker to false (i.e. flag to know, when reading from the stream, that postion and roation data *was not included*) writer.WriteBool(false); } @@ -344,7 +344,7 @@ private void OnSceneUnloadServer(Guid switchSceneGuid) } } - //Tell server that scene load is completed + // Tell server that scene load is completed if (m_NetworkManager.IsHost) { OnClientSwitchSceneCompleted(m_NetworkManager.LocalClientId, switchSceneGuid); @@ -381,7 +381,7 @@ private void OnSceneUnloadClient(Guid switchSceneGuid, Stream objectStream) Vector3? position = null; Quaternion? rotation = null; - //Check to see if we have position and rotation values that follows + // Check to see if we have position and rotation values that follows if (reader.ReadBool()) { position = new Vector3(reader.ReadSinglePacked(), reader.ReadSinglePacked(), reader.ReadSinglePacked()); @@ -425,7 +425,7 @@ internal void OnClientSwitchSceneCompleted(ulong clientId, Guid switchSceneGuid) { if (switchSceneGuid == Guid.Empty) { - //If Guid is empty it means the client has loaded the start scene of the server and the server would never have a switchSceneProgresses created for the start scene. + // If Guid is empty it means the client has loaded the start scene of the server and the server would never have a switchSceneProgresses created for the start scene. return; } diff --git a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs index 5900bcc256..1835782bcf 100644 --- a/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs +++ b/com.unity.multiplayer.mlapi/Runtime/Spawning/NetworkSpawnManager.cs @@ -57,34 +57,6 @@ internal ulong GetNetworkObjectId() return m_NetworkObjectIdCounter; } - /// - /// Gets the prefab index of a given prefab hash - /// - /// The hash of the prefab - /// The index of the prefab - public int GetNetworkPrefabIndexOfHash(uint hash) - { - for (int i = 0; i < NetworkManager.NetworkConfig.NetworkPrefabs.Count; i++) - { - if (NetworkManager.NetworkConfig.NetworkPrefabs[i].Hash == hash) - { - return i; - } - } - - return -1; - } - - /// - /// Returns the prefab hash for the NetworkPrefab with a given index - /// - /// The NetworkPrefab index - /// The prefab hash for the given prefab index - public uint GetPrefabHashFromIndex(int index) - { - return NetworkManager.NetworkConfig.NetworkPrefabs[index].Hash; - } - /// /// Returns the local player object or null if one does not exist /// @@ -181,7 +153,9 @@ internal void ChangeOwnership(NetworkObject networkObject, ulong clientId) } } - // Only ran on Client + /// + /// Should only run on the client + /// internal NetworkObject CreateLocalNetworkObject(bool softCreate, uint prefabHash, ulong ownerClientId, ulong? parentNetworkId, Vector3? position, Quaternion? rotation) { NetworkObject parentNetworkObject = null; @@ -198,12 +172,12 @@ internal NetworkObject CreateLocalNetworkObject(bool softCreate, uint prefabHash } } - if (!NetworkManager.NetworkConfig.EnableSceneManagement || !softCreate) { - // Create the object + // If the prefab hash has a registered INetworkPrefabInstanceHandler derived class if (NetworkManager.PrefabHandler.ContainsHandler(prefabHash)) { + // Let the handler spawn the NetworkObject var networkObject = NetworkManager.PrefabHandler.HandleNetworkPrefabSpawn(prefabHash, ownerClientId, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity)); if (parentNetworkObject != null) @@ -220,20 +194,36 @@ internal NetworkObject CreateLocalNetworkObject(bool softCreate, uint prefabHash } else { - var prefabIndex = GetNetworkPrefabIndexOfHash(prefabHash); + // See if there is a valid registered NetworkPrefabOverrideLink associated with the provided prefabHash + GameObject networkPrefabReference = null; + if(NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks.ContainsKey(prefabHash)) + { + switch (NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks[prefabHash].Override) + { + default: + case NetworkPrefabOverride.None: + networkPrefabReference = NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks[prefabHash].Prefab; + break; + case NetworkPrefabOverride.Hash: + case NetworkPrefabOverride.Prefab: + networkPrefabReference = NetworkManager.NetworkConfig.NetworkPrefabOverrideLinks[prefabHash].OverridingTargetPrefab; + break; + } + } - if (prefabIndex < 0) + // If not, then there is an issue (user possibly didn't register the prefab properly?) + if (networkPrefabReference == null) { if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { - NetworkLog.LogError($"Failed to create object locally. [{nameof(prefabHash)}={prefabHash}]. Hash could not be found. Is the prefab registered?"); + NetworkLog.LogError($"Failed to create object locally. [{nameof(prefabHash)}={prefabHash}]. {nameof(NetworkPrefab)} could not be found. Is the prefab registered with {nameof(NetworkManager)}?"); } return null; } - var prefab = NetworkManager.NetworkConfig.NetworkPrefabs[prefabIndex].Prefab; - var networkObject = ((position == null && rotation == null) ? UnityEngine.Object.Instantiate(prefab) : UnityEngine.Object.Instantiate(prefab, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity))).GetComponent(); + // Otherwise, instantiate an instance of the NetworkPrefab linked to the prefabHash + var networkObject = ((position == null && rotation == null) ? UnityEngine.Object.Instantiate(networkPrefabReference) : UnityEngine.Object.Instantiate(networkPrefabReference, position.GetValueOrDefault(Vector3.zero), rotation.GetValueOrDefault(Quaternion.identity))).GetComponent(); if (parentNetworkObject != null) { @@ -250,13 +240,12 @@ internal NetworkObject CreateLocalNetworkObject(bool softCreate, uint prefabHash } else { - // SoftSync them by mapping + // Check to see if the hash is in our pending in-scene objects waiting to be "soft synchronized" if (!PendingSoftSyncObjects.ContainsKey(prefabHash)) { - // TODO: Fix this message if (NetworkLog.CurrentLogLevel <= LogLevel.Error) { - NetworkLog.LogError("Cannot find pending soft sync object. Is the projects the same?"); + NetworkLog.LogError($"{nameof(NetworkPrefab)} hash was not found! In-Scene placed {nameof(NetworkObject)} soft synchronization failure for Hash: {prefabHash}!"); } return null; } diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/GlobalObjectIdHash/NetworkPrefabGlobalObjectIdHashTests.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/GlobalObjectIdHash/NetworkPrefabGlobalObjectIdHashTests.cs index 97117a8b32..1271b761d0 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/GlobalObjectIdHash/NetworkPrefabGlobalObjectIdHashTests.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/GlobalObjectIdHash/NetworkPrefabGlobalObjectIdHashTests.cs @@ -61,9 +61,9 @@ public void VerifyUniquenessOfNetworkPrefabs() Assert.That(networkManager.NetworkConfig.NetworkPrefabs.Count, Is.GreaterThan(1)); var hashSet = new HashSet(); - foreach (var networkPrefab in networkManager.NetworkConfig.NetworkPrefabs) + foreach (var networkPrefab in networkManager.NetworkConfig.NetworkPrefabOverrideLinks) { - var idHash = networkPrefab.Hash; + var idHash = networkPrefab.Key; Assert.That(idHash, Is.Not.EqualTo(0)); Assert.That(hashSet.Contains(idHash), Is.False); hashSet.Add(idHash); diff --git a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkManagerHelper.cs b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkManagerHelper.cs index 3ce0d2b65d..71da5f438f 100644 --- a/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkManagerHelper.cs +++ b/com.unity.multiplayer.mlapi/Tests/Runtime/NetworkManagerHelper.cs @@ -74,7 +74,6 @@ public static bool StartNetworkManager(out NetworkManager networkManager, Networ NetworkManagerObject.NetworkConfig = new Configuration.NetworkConfig { - CreatePlayerPrefab = false, EnableSceneManagement = false, RegisteredScenes = new List(){SceneManager.GetActiveScene().name} }; diff --git a/testproject/Assets/ManualTests/RpcTesting.unity b/testproject/Assets/ManualTests/RpcTesting.unity index bfca9b276b..69c351c7d9 100644 --- a/testproject/Assets/ManualTests/RpcTesting.unity +++ b/testproject/Assets/ManualTests/RpcTesting.unity @@ -422,7 +422,7 @@ PrefabInstance: - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} propertyPath: m_AnchorMax.y - value: 0.5 + value: 1 objectReference: {fileID: 0} - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} @@ -432,7 +432,7 @@ PrefabInstance: - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} propertyPath: m_AnchorMin.y - value: 0.5 + value: 1 objectReference: {fileID: 0} - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} @@ -487,7 +487,7 @@ PrefabInstance: - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} propertyPath: m_AnchoredPosition.y - value: 0 + value: -90 objectReference: {fileID: 0} - target: {fileID: 6633621479308595792, guid: d725b5588e1b956458798319e6541d84, type: 3} @@ -808,11 +808,18 @@ MonoBehaviour: RegisteredScenes: - RpcTesting AllowRuntimeSceneChanges: 0 + PlayerPrefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, + type: 3} NetworkPrefabs: - Prefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, type: 3} - IsPlayer: 1 - PlayerPrefabHash: 315939022 + Override: 0 + OverridingSourcePrefab: {fileID: 4700706668509470175, guid: 7eeaaf9e50c0afc4dab93584a54fb0d6, + type: 3} + OverridingSourceHash: 0 + OverridingTargetPrefab: {fileID: 8685790303553767886, guid: 96e0a72e30d0c46c8a5c9a750e8f5807, + type: 3} + IsPlayer: 0 CreatePlayerPrefab: 1 ReceiveTickrate: 64 NetworkTickIntervalSec: 0.05 @@ -827,7 +834,6 @@ MonoBehaviour: EnsureNetworkVariableLengthSafety: 0 EnableSceneManagement: 1 ForceSamePrefabs: 1 - UsePrefabSync: 0 RecycleNetworkIds: 1 NetworkIdRecycleDelay: 120 RpcHashSize: 0