using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using Directory = UnityEngine.Windows.Directory; using File = UnityEngine.Windows.File; namespace Unity.Netcode.Editor.Configuration { internal static class NetcodeSettingsProvider { private const float k_MaxLabelWidth = 450f; private static float s_MaxLabelWidth; private static bool s_ShowEditorSettingFields = true; private static bool s_ShowProjectSettingFields = true; [SettingsProvider] public static SettingsProvider CreateNetcodeSettingsProvider() { // First parameter is the path in the Settings window. // Second parameter is the scope of this setting: it only appears in the Settings window for the Project scope. var provider = new SettingsProvider("Project/Multiplayer/NetcodeForGameObjects", SettingsScope.Project) { label = "Netcode for GameObjects", keywords = new[] { "netcode", "editor" }, guiHandler = OnGuiHandler, deactivateHandler = OnDeactivate }; return provider; } private static void OnDeactivate() { var settings = NetcodeForGameObjectsProjectSettings.instance; if (settings.TempNetworkPrefabsPath != settings.NetworkPrefabsPath) { var newPath = settings.TempNetworkPrefabsPath; if (newPath == "") { newPath = NetcodeForGameObjectsProjectSettings.DefaultNetworkPrefabsPath; settings.TempNetworkPrefabsPath = newPath; } var oldPath = settings.NetworkPrefabsPath; settings.NetworkPrefabsPath = settings.TempNetworkPrefabsPath; var dirName = Path.GetDirectoryName(newPath); if (!Directory.Exists(dirName)) { var dirs = dirName.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); var dirsQueue = new Queue(dirs); var parent = dirsQueue.Dequeue(); while (dirsQueue.Count != 0) { var child = dirsQueue.Dequeue(); var together = Path.Combine(parent, child); if (!Directory.Exists(together)) { AssetDatabase.CreateFolder(parent, child); } parent = together; } } if (Directory.Exists(dirName)) { if (File.Exists(oldPath)) { AssetDatabase.MoveAsset(oldPath, newPath); if (File.Exists(oldPath)) { File.Delete(oldPath); } AssetDatabase.Refresh(); } } settings.SaveSettings(); } } internal static NetcodeSettingsLabel NetworkObjectsSectionLabel; internal static NetcodeSettingsToggle AutoAddNetworkObjectToggle; internal static NetcodeSettingsToggle CheckForNetworkObjectToggle; internal static NetcodeSettingsLabel MultiplayerToolsLabel; internal static NetcodeSettingsToggle MultiplayerToolTipStatusToggle; /// /// Creates an instance of the settings UI Elements if they don't already exist. /// /// /// We have to construct any NetcodeGUISettings derived classes here because in /// version 2020.x.x EditorStyles.label does not exist yet (higher versions it does) /// private static void CheckForInitialize() { if (NetworkObjectsSectionLabel == null) { NetworkObjectsSectionLabel = new NetcodeSettingsLabel("NetworkObject Helper Settings", 20); } if (AutoAddNetworkObjectToggle == null) { AutoAddNetworkObjectToggle = new NetcodeSettingsToggle("Auto-Add NetworkObject Component", "When enabled, NetworkObject components are automatically added to GameObjects when NetworkBehaviour components are added first.", 20); } if (CheckForNetworkObjectToggle == null) { CheckForNetworkObjectToggle = new NetcodeSettingsToggle("Check for NetworkObject Component", "When disabled, the automatic check on NetworkBehaviours for an associated NetworkObject component will not be performed and Auto-Add NetworkObject Component will be disabled.", 20); } if (MultiplayerToolsLabel == null) { MultiplayerToolsLabel = new NetcodeSettingsLabel("Multiplayer Tools", 20); } if (MultiplayerToolTipStatusToggle == null) { MultiplayerToolTipStatusToggle = new NetcodeSettingsToggle("Multiplayer Tools Install Reminder", "When enabled, the NetworkManager will display the notification to install the multiplayer tools package.", 20); } } private static void OnGuiHandler(string obj) { // Make sure all NetcodeGUISettings derived classes are instantiated first CheckForInitialize(); var autoAddNetworkObjectSetting = NetcodeForGameObjectsEditorSettings.GetAutoAddNetworkObjectSetting(); var checkForNetworkObjectSetting = NetcodeForGameObjectsEditorSettings.GetCheckForNetworkObjectSetting(); var multiplayerToolsTipStatus = NetcodeForGameObjectsEditorSettings.GetNetcodeInstallMultiplayerToolTips() == 0; var settings = NetcodeForGameObjectsProjectSettings.instance; var generateDefaultPrefabs = settings.GenerateDefaultNetworkPrefabs; var networkPrefabsPath = settings.TempNetworkPrefabsPath; EditorGUI.BeginChangeCheck(); GUILayout.BeginVertical("Box"); s_ShowEditorSettingFields = EditorGUILayout.BeginFoldoutHeaderGroup(s_ShowEditorSettingFields, "Editor Settings"); if (s_ShowEditorSettingFields) { GUILayout.BeginVertical("Box"); NetworkObjectsSectionLabel.DrawLabel(); autoAddNetworkObjectSetting = AutoAddNetworkObjectToggle.DrawToggle(autoAddNetworkObjectSetting, checkForNetworkObjectSetting); checkForNetworkObjectSetting = CheckForNetworkObjectToggle.DrawToggle(checkForNetworkObjectSetting); if (autoAddNetworkObjectSetting && !checkForNetworkObjectSetting) { autoAddNetworkObjectSetting = false; } GUILayout.EndVertical(); GUILayout.BeginVertical("Box"); MultiplayerToolsLabel.DrawLabel(); multiplayerToolsTipStatus = MultiplayerToolTipStatusToggle.DrawToggle(multiplayerToolsTipStatus); GUILayout.EndVertical(); } EditorGUILayout.EndFoldoutHeaderGroup(); GUILayout.EndVertical(); GUILayout.BeginVertical("Box"); s_ShowProjectSettingFields = EditorGUILayout.BeginFoldoutHeaderGroup(s_ShowProjectSettingFields, "Project Settings"); if (s_ShowProjectSettingFields) { GUILayout.BeginVertical("Box"); const string generateNetworkPrefabsString = "Generate Default Network Prefabs List"; const string networkPrefabsLocationString = "Default Network Prefabs List path"; if (s_MaxLabelWidth == 0) { s_MaxLabelWidth = EditorStyles.label.CalcSize(new GUIContent(generateNetworkPrefabsString)).x; s_MaxLabelWidth = Mathf.Min(k_MaxLabelWidth, s_MaxLabelWidth); } EditorGUIUtility.labelWidth = s_MaxLabelWidth; GUILayout.Label("Network Prefabs", EditorStyles.boldLabel); generateDefaultPrefabs = EditorGUILayout.Toggle( new GUIContent( generateNetworkPrefabsString, "When enabled, a default NetworkPrefabsList object will be added to your project and kept up " + "to date with all NetworkObject prefabs."), generateDefaultPrefabs, GUILayout.Width(s_MaxLabelWidth + 20)); GUI.SetNextControlName("Location"); networkPrefabsPath = EditorGUILayout.TextField( new GUIContent( networkPrefabsLocationString, "The path to the asset the default NetworkPrefabList object should be stored in."), networkPrefabsPath, GUILayout.Width(s_MaxLabelWidth + 270)); GUILayout.EndVertical(); } EditorGUILayout.EndFoldoutHeaderGroup(); GUILayout.EndVertical(); if (EditorGUI.EndChangeCheck()) { NetcodeForGameObjectsEditorSettings.SetAutoAddNetworkObjectSetting(autoAddNetworkObjectSetting); NetcodeForGameObjectsEditorSettings.SetCheckForNetworkObjectSetting(checkForNetworkObjectSetting); NetcodeForGameObjectsEditorSettings.SetNetcodeInstallMultiplayerToolTips(multiplayerToolsTipStatus ? 0 : 1); settings.GenerateDefaultNetworkPrefabs = generateDefaultPrefabs; settings.TempNetworkPrefabsPath = networkPrefabsPath; settings.SaveSettings(); } } } internal class NetcodeSettingsLabel : NetcodeGUISettings { private string m_LabelContent; public void DrawLabel() { EditorGUIUtility.labelWidth = m_LabelSize; GUILayout.Label(m_LabelContent, EditorStyles.boldLabel, m_LayoutWidth); } public NetcodeSettingsLabel(string labelText, float layoutOffset = 0.0f) { m_LabelContent = labelText; AdjustLabelSize(labelText, layoutOffset); } } internal class NetcodeSettingsToggle : NetcodeGUISettings { private GUIContent m_ToggleContent; public bool DrawToggle(bool currentSetting, bool enabled = true) { EditorGUIUtility.labelWidth = m_LabelSize; GUI.enabled = enabled; var returnValue = EditorGUILayout.Toggle(m_ToggleContent, currentSetting, m_LayoutWidth); GUI.enabled = true; return returnValue; } public NetcodeSettingsToggle(string labelText, string toolTip, float layoutOffset) { AdjustLabelSize(labelText, layoutOffset); m_ToggleContent = new GUIContent(labelText, toolTip); } } internal class NetcodeGUISettings { private const float k_MaxLabelWidth = 450f; protected float m_LabelSize { get; private set; } protected GUILayoutOption m_LayoutWidth { get; private set; } protected void AdjustLabelSize(string labelText, float offset = 0.0f) { m_LabelSize = Mathf.Min(k_MaxLabelWidth, EditorStyles.label.CalcSize(new GUIContent(labelText)).x); m_LayoutWidth = GUILayout.Width(m_LabelSize + offset); } } }