File size: 7,790 Bytes
05c9ac2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
using System;
using System.Linq;
using System.IO;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEditor;
using UnityEngine.UIElements;

[assembly: InternalsVisibleTo("Unity.ML-Agents.DevTests.Editor")]
namespace Unity.MLAgents.Editor
{
    internal class MLAgentsSettingsProvider : SettingsProvider, IDisposable
    {
        const string k_SettingsPath = "Project/ML-Agents";
        private static MLAgentsSettingsProvider s_Instance;
        private string[] m_AvailableSettingsAssets;
        private int m_CurrentSelectedSettingsAsset;
        private SerializedObject m_SettingsObject;
        [SerializeField]
        private MLAgentsSettings m_Settings;


        private MLAgentsSettingsProvider(string path, SettingsScope scope = SettingsScope.Project)
            : base(path, scope)
        {
            s_Instance = this;
        }

        [SettingsProvider]
        public static SettingsProvider CreateMLAgentsSettingsProvider()
        {
            return new MLAgentsSettingsProvider(k_SettingsPath, SettingsScope.Project);
        }

        public override void OnActivate(string searchContext, VisualElement rootElement)
        {
            base.OnActivate(searchContext, rootElement);
            MLAgentsSettingsManager.OnSettingsChange += Reinitialize;
        }

        public override void OnDeactivate()
        {
            base.OnDeactivate();
            MLAgentsSettingsManager.OnSettingsChange -= Reinitialize;
        }

        public void Dispose()
        {
            m_SettingsObject?.Dispose();
        }

        public override void OnTitleBarGUI()
        {
            if (EditorGUILayout.DropdownButton(EditorGUIUtility.IconContent("_Popup"), FocusType.Passive, EditorStyles.label))
            {
                var menu = new GenericMenu();
                for (var i = 0; i < m_AvailableSettingsAssets.Length; i++)
                {
                    menu.AddItem(ExtractDisplayName(m_AvailableSettingsAssets[i]), m_CurrentSelectedSettingsAsset == i, (path) =>
                    {
                        MLAgentsSettingsManager.Settings = AssetDatabase.LoadAssetAtPath<MLAgentsSettings>((string)path);
                    }, m_AvailableSettingsAssets[i]);
                }
                menu.AddSeparator("");
                menu.AddItem(new GUIContent("New Settings Asset…"), false, CreateNewSettingsAsset);
                menu.ShowAsContext();
                Event.current.Use();
            }
        }

        private GUIContent ExtractDisplayName(string name)
        {
            if (name.StartsWith("Assets/"))
                name = name.Substring("Assets/".Length);
            if (name.EndsWith(".asset"))
                name = name.Substring(0, name.Length - ".asset".Length);
            if (name.EndsWith(".mlagents.settings"))
                name = name.Substring(0, name.Length - ".mlagents.settings".Length);

            // Ugly hack: GenericMenu interprets "/" as a submenu path. But luckily, "/" is not the only slash we have in Unicode.
            return new GUIContent(name.Replace("/", "\u29f8"));
        }

        private void CreateNewSettingsAsset()
        {
            // Asset database always use forward slashes. Use forward slashes for all the paths.
            var projectName = PlayerSettings.productName;
            var path = EditorUtility.SaveFilePanel("Create ML-Agents Settings File", "Assets",
                projectName + ".mlagents.settings", "asset");
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            path = path.Replace("\\", "/"); // Make sure we only get '/' separators.
            var assetPath = Application.dataPath + "/";
            if (!path.StartsWith(assetPath, StringComparison.CurrentCultureIgnoreCase))
            {
                Debug.LogError(string.Format(
                    "Settings must be stored in Assets folder of the project (got: '{0}')", path));
                return;
            }

            var extension = Path.GetExtension(path);
            if (string.Compare(extension, ".asset", StringComparison.InvariantCultureIgnoreCase) != 0)
            {
                path += ".asset";
            }
            var relativePath = "Assets/" + path.Substring(assetPath.Length);
            CreateNewSettingsAsset(relativePath);
        }

        private static void CreateNewSettingsAsset(string relativePath)
        {
            var settings = ScriptableObject.CreateInstance<MLAgentsSettings>();
            AssetDatabase.CreateAsset(settings, relativePath);
            EditorGUIUtility.PingObject(settings);
            // Install the settings. This will lead to an MLAgentsManager.OnSettingsChange event
            // which in turn will cause this Provider to reinitialize
            MLAgentsSettingsManager.Settings = settings;
        }

        public override void OnGUI(string searchContext)
        {
            if (m_Settings == null)
            {
                InitializeWithCurrentSettings();
            }

            if (m_AvailableSettingsAssets.Length == 0)
            {
                EditorGUILayout.HelpBox(
                    "Click the button below to create a settings asset you can edit.",
                    MessageType.Info);
                if (GUILayout.Button("Create settings asset", GUILayout.Height(30)))
                    CreateNewSettingsAsset();
                GUILayout.Space(20);
            }

            using (new EditorGUI.DisabledScope(m_AvailableSettingsAssets.Length == 0))
            {
                EditorGUI.BeginChangeCheck();
                EditorGUILayout.LabelField("Trainer Settings", EditorStyles.boldLabel);
                EditorGUI.indentLevel++;
                EditorGUILayout.PropertyField(m_SettingsObject.FindProperty("m_ConnectTrainer"), new GUIContent("Connect to Trainer"));
                EditorGUILayout.PropertyField(m_SettingsObject.FindProperty("m_EditorPort"), new GUIContent("Editor Training Port"));
                EditorGUI.indentLevel--;
                if (EditorGUI.EndChangeCheck())
                    m_SettingsObject.ApplyModifiedProperties();
            }
        }

        internal void InitializeWithCurrentSettings()
        {
            m_AvailableSettingsAssets = FindSettingsInProject();

            m_Settings = MLAgentsSettingsManager.Settings;
            var currentSettingsPath = AssetDatabase.GetAssetPath(m_Settings);
            if (string.IsNullOrEmpty(currentSettingsPath))
            {
                if (m_AvailableSettingsAssets.Length > 0)
                {
                    m_CurrentSelectedSettingsAsset = 0;
                    m_Settings = AssetDatabase.LoadAssetAtPath<MLAgentsSettings>(m_AvailableSettingsAssets[0]);
                    MLAgentsSettingsManager.Settings = m_Settings;
                }
            }
            else
            {
                var settingsList = m_AvailableSettingsAssets.ToList();
                m_CurrentSelectedSettingsAsset = settingsList.IndexOf(currentSettingsPath);

                EditorBuildSettings.AddConfigObject(MLAgentsSettingsManager.EditorBuildSettingsConfigKey, m_Settings, true);
            }

            m_SettingsObject = new SerializedObject(m_Settings);
        }

        private static string[] FindSettingsInProject()
        {
            var guids = AssetDatabase.FindAssets("t:MLAgentsSettings");
            return guids.Select(guid => AssetDatabase.GUIDToAssetPath(guid)).ToArray();
        }

        private void Reinitialize()
        {
            if (m_Settings != null && MLAgentsSettingsManager.Settings != m_Settings)
            {
                InitializeWithCurrentSettings();
            }
            Repaint();
        }
    }
}