Assignment for RMIT Mixed Reality in 2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1270 lines
40 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Text.RegularExpressions;
  7. using System.Threading;
  8. using UnityEditor;
  9. using UnityEngine;
  10. using UnityEngine.Networking;
  11. namespace Assets.Oculus.VR.Editor
  12. {
  13. public class OVRPlatformTool : EditorWindow
  14. {
  15. public enum TargetPlatform
  16. {
  17. Rift,
  18. OculusGoGearVR,
  19. Quest,
  20. None,
  21. };
  22. const string urlPlatformUtil =
  23. "https://www.oculus.com/download_app/?id=1076686279105243";
  24. static private Process ovrPlatUtilProcess;
  25. Vector2 commandMenuScroll;
  26. Vector2 debugLogScroll;
  27. static public string log;
  28. private static bool activeProcess = false;
  29. private static bool ranSelfUpdate = false;
  30. private static int retryCount = 0;
  31. private static string appToken;
  32. private const float buttonPadding = 5.0f;
  33. private bool showOptionalCommands = false;
  34. private bool show2DCommands = false;
  35. private bool showExpansionFileCommands = false;
  36. private bool showRedistCommands = false;
  37. private const float INDENT_SPACING = 15f;
  38. private const float SINGLE_LINE_SPACING = 18f;
  39. private const float ASSET_CONFIG_BACKGROUND_PADDING = 10f;
  40. private const float DEFAULT_LABEL_WIDTH = 180f;
  41. private const int MAX_DOWNLOAD_RETRY_COUNT = 2;
  42. private static GUIStyle boldFoldoutStyle;
  43. string[] platformOptions = new string[]
  44. {
  45. "Oculus Rift",
  46. "Oculus Go | Gear VR",
  47. "Oculus Quest"
  48. };
  49. string[] gamepadOptions = new string[]
  50. {
  51. "Off",
  52. "Twinstick",
  53. "Right D Pad",
  54. "Left D Pad"
  55. };
  56. [MenuItem("Oculus/Tools/Oculus Platform Tool")]
  57. static void Init()
  58. {
  59. OVRPlatformTool.log = string.Empty;
  60. // Get existing open window or if none, make a new one:
  61. EditorWindow.GetWindow(typeof(OVRPlatformTool));
  62. // Populate initial target platform value based on OVRDeviceSelector
  63. #if UNITY_ANDROID
  64. if (OVRDeviceSelector.isTargetDeviceQuest)
  65. {
  66. OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Quest;
  67. }
  68. else
  69. {
  70. OVRPlatformToolSettings.TargetPlatform = TargetPlatform.OculusGoGearVR;
  71. }
  72. #else
  73. OVRPlatformToolSettings.TargetPlatform = TargetPlatform.Rift;
  74. #endif
  75. EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
  76. // Load redist packages by calling list-redists in the CLI
  77. string dataPath = Application.dataPath;
  78. var thread = new Thread(delegate () {
  79. retryCount = 0;
  80. LoadRedistPackages(dataPath);
  81. });
  82. thread.Start();
  83. OVRPlugin.SendEvent("oculus_platform_tool", "show_window");
  84. }
  85. void OnGUI()
  86. {
  87. if (boldFoldoutStyle == null)
  88. {
  89. boldFoldoutStyle = new GUIStyle(EditorStyles.foldout);
  90. boldFoldoutStyle.fontStyle = FontStyle.Bold;
  91. }
  92. EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
  93. GUILayout.Label("OVR Platform Tool", EditorStyles.boldLabel);
  94. this.titleContent.text = "OVR Platform Tool";
  95. GUIContent TargetPlatformLabel = new GUIContent("Target Oculus Platform");
  96. OVRPlatformToolSettings.TargetPlatform = (TargetPlatform)MakePopup(TargetPlatformLabel, (int)OVRPlatformToolSettings.TargetPlatform, platformOptions);
  97. SetOVRProjectConfig(OVRPlatformToolSettings.TargetPlatform);
  98. SetDirtyOnGUIChange();
  99. commandMenuScroll = EditorGUILayout.BeginScrollView(commandMenuScroll, GUILayout.Height(Screen.height / 2));
  100. {
  101. // Add the UI Form
  102. EditorGUI.BeginChangeCheck();
  103. GUILayout.Space(15.0f);
  104. // App ID
  105. GUIContent AppIDLabel = new GUIContent("Oculus Application ID [?]: ",
  106. "This AppID will be used when uploading the build.");
  107. OVRPlatformToolSettings.AppID = MakeTextBox(AppIDLabel, OVRPlatformToolSettings.AppID);
  108. // App Token
  109. GUIContent AppTokenLabel = new GUIContent("Oculus App Token [?]: ",
  110. "You can get your app token from your app's Oculus API Dashboard.");
  111. appToken = MakePasswordBox(AppTokenLabel, appToken);
  112. // Release Channel
  113. GUIContent ReleaseChannelLabel = new GUIContent("Release Channel [?]: ",
  114. "Specify the releaes channel of the new build, you can reassign to other channels after upload.");
  115. OVRPlatformToolSettings.ReleaseChannel = MakeTextBox(ReleaseChannelLabel, OVRPlatformToolSettings.ReleaseChannel);
  116. // Releaes Note
  117. GUIContent ReleaseNoteLabel = new GUIContent("Release Note: ");
  118. OVRPlatformToolSettings.ReleaseNote = MakeTextBox(ReleaseNoteLabel, OVRPlatformToolSettings.ReleaseNote);
  119. // Platform specific fields
  120. if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
  121. {
  122. GUIContent BuildDirLabel = new GUIContent("Rift Build Directory [?]: ",
  123. "The full path to the directory containing your Rift build files.");
  124. OVRPlatformToolSettings.RiftBuildDirectory = MakeFileDirectoryField(BuildDirLabel, OVRPlatformToolSettings.RiftBuildDirectory,
  125. "Choose Rifle Build Directory");
  126. GUIContent BuildVersionLabel = new GUIContent("Build Version [?]: ",
  127. "The version number shown to users.");
  128. OVRPlatformToolSettings.RiftBuildVersion = MakeTextBox(BuildVersionLabel, OVRPlatformToolSettings.RiftBuildVersion);
  129. GUIContent LaunchFileLabel = new GUIContent("Launch File Path [?]: ",
  130. "The full path to the executable that launches your app.");
  131. OVRPlatformToolSettings.RiftLaunchFile = MakeFileDirectoryField(LaunchFileLabel, OVRPlatformToolSettings.RiftLaunchFile,
  132. "Choose Launch File", true, "exe");
  133. }
  134. else
  135. {
  136. GUIContent ApkPathLabel = new GUIContent("Build APK File Path [?]: ",
  137. "The full path to the APK file.");
  138. OVRPlatformToolSettings.ApkBuildPath = MakeFileDirectoryField(ApkPathLabel, OVRPlatformToolSettings.ApkBuildPath,
  139. "Choose APK File", true, "apk");
  140. if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.OculusGoGearVR)
  141. {
  142. // Go and Gear VR specific fields
  143. }
  144. else if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
  145. {
  146. // Quest specific fields
  147. }
  148. }
  149. showOptionalCommands = EditorGUILayout.Foldout(showOptionalCommands, "Optional Commands", boldFoldoutStyle);
  150. if (showOptionalCommands)
  151. {
  152. IncrementIndent();
  153. if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Rift)
  154. {
  155. // Launch Parameters
  156. GUIContent LaunchParamLabel = new GUIContent("Launch Parameters [?]: ",
  157. "Specifies any arguments passed to the launcher.");
  158. OVRPlatformToolSettings.RiftLaunchParams = MakeTextBox(LaunchParamLabel, OVRPlatformToolSettings.RiftLaunchParams);
  159. GUIContent FirewallExceptionLabel = new GUIContent("Firewall Exception [?]: ",
  160. "Specifies if a Windows Firewall exception is required.");
  161. OVRPlatformToolSettings.RiftFirewallException = MakeToggleBox(FirewallExceptionLabel, OVRPlatformToolSettings.RiftFirewallException);
  162. GUIContent GamepadEmulationLabel = new GUIContent("Gamepad Emulation [?]: ",
  163. "Specifies the type of gamepad emulation used by the Oculus Touch controllers.");
  164. OVRPlatformToolSettings.RiftGamepadEmulation = (OVRPlatformToolSettings.GamepadType)MakePopup(GamepadEmulationLabel, (int)OVRPlatformToolSettings.RiftGamepadEmulation, gamepadOptions);
  165. show2DCommands = EditorGUILayout.Foldout(show2DCommands, "2D", boldFoldoutStyle);
  166. if (show2DCommands)
  167. {
  168. IncrementIndent();
  169. // 2D Launch File
  170. GUIContent LaunchFile2DLabel = new GUIContent("2D Launch File [?]: ",
  171. "The full path to the executable that launches your app in 2D mode.");
  172. OVRPlatformToolSettings.Rift2DLaunchFile = MakeFileDirectoryField(LaunchFile2DLabel, OVRPlatformToolSettings.Rift2DLaunchFile,
  173. "Choose 2D Launch File", true, "exe");
  174. // 2D Launch Parameters
  175. GUIContent LaunchParam2DLabel = new GUIContent("2D Launch Parameters [?]: ",
  176. "Specifies any arguments passed to the launcher in 2D mode.");
  177. OVRPlatformToolSettings.Rift2DLaunchParams = MakeTextBox(LaunchParam2DLabel, OVRPlatformToolSettings.Rift2DLaunchParams);
  178. DecrementIndent();
  179. }
  180. showRedistCommands = EditorGUILayout.Foldout(showRedistCommands, "Redistributable Packages", boldFoldoutStyle);
  181. if (showRedistCommands)
  182. {
  183. IncrementIndent();
  184. for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
  185. {
  186. GUIContent RedistPackageLabel = new GUIContent(OVRPlatformToolSettings.RiftRedistPackages[i].name);
  187. OVRPlatformToolSettings.RiftRedistPackages[i].include = MakeToggleBox(RedistPackageLabel, OVRPlatformToolSettings.RiftRedistPackages[i].include);
  188. }
  189. DecrementIndent();
  190. }
  191. showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
  192. if (showExpansionFileCommands)
  193. {
  194. IncrementIndent();
  195. // Language Pack Directory
  196. GUIContent LanguagePackLabel = new GUIContent("Language Pack Directory [?]: ",
  197. "The full path to the directory containing the language packs");
  198. OVRPlatformToolSettings.LanguagePackDirectory = MakeFileDirectoryField(LanguagePackLabel, OVRPlatformToolSettings.LanguagePackDirectory,
  199. "Choose Language Pack Directory");
  200. }
  201. }
  202. else
  203. {
  204. if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.OculusGoGearVR)
  205. {
  206. // Go and Gear VR specific optional fields
  207. }
  208. else if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
  209. {
  210. // Quest specific optional fields
  211. }
  212. showExpansionFileCommands = EditorGUILayout.Foldout(showExpansionFileCommands, "Expansion Files", boldFoldoutStyle);
  213. if (showExpansionFileCommands)
  214. {
  215. IncrementIndent();
  216. // OBB File Path
  217. GUIContent ObbPathLabel = new GUIContent("OBB File Path [?]: ",
  218. "The full path to the OBB file.");
  219. OVRPlatformToolSettings.ObbFilePath = MakeFileDirectoryField(ObbPathLabel, OVRPlatformToolSettings.ObbFilePath,
  220. "Choose OBB File", true, "obb");
  221. }
  222. }
  223. if (showExpansionFileCommands)
  224. {
  225. // Assets Directory
  226. GUIContent AssetsDirLabel = new GUIContent("Assets Directory [?]: ",
  227. "The full path to the directory with DLCs for this build.");
  228. string assetsDirectory = MakeFileDirectoryField(AssetsDirLabel, OVRPlatformToolSettings.AssetsDirectory,
  229. "Choose Assets Directory");
  230. if (assetsDirectory != OVRPlatformToolSettings.AssetsDirectory)
  231. {
  232. OVRPlatformToolSettings.AssetsDirectory = assetsDirectory;
  233. OVRPlatformToolSettings.AssetConfigs.Clear();
  234. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
  235. {
  236. DirectoryInfo dirInfo = new DirectoryInfo(OVRPlatformToolSettings.AssetsDirectory);
  237. FileInfo[] assetFiles = dirInfo.GetFiles();
  238. foreach (FileInfo f in assetFiles)
  239. {
  240. OVRPlatformToolSettings.AssetConfigs.Add(new AssetConfig(f.Name));
  241. }
  242. }
  243. EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
  244. }
  245. // Display bordered asset configuration list
  246. GUILayout.Space(3f);
  247. Rect rect = GUILayoutUtility.GetRect(0, GetAssetConfigElementHeight() + (ASSET_CONFIG_BACKGROUND_PADDING * 2),
  248. GUILayout.ExpandWidth(true));
  249. rect.x += (EditorGUI.indentLevel * INDENT_SPACING + 5);
  250. rect.width -= (EditorGUI.indentLevel * INDENT_SPACING + 10);
  251. DrawAssetConfigList(rect);
  252. DecrementIndent();
  253. }
  254. EditorGUI.indentLevel--;
  255. }
  256. if (EditorGUI.EndChangeCheck())
  257. {
  258. EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
  259. }
  260. }
  261. EditorGUILayout.EndScrollView();
  262. GUILayout.Space(SINGLE_LINE_SPACING);
  263. GUILayout.FlexibleSpace();
  264. // Run OVR Lint Option
  265. EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH;
  266. GUIContent RunOvrLintLabel = new GUIContent("Run OVR Lint (Recommended) [?]: ",
  267. "Run OVR Lint tool to ensure project is optimized for performance and meets Oculus packaging requirement for publishing.");
  268. OVRPlatformToolSettings.RunOvrLint = MakeToggleBox(RunOvrLintLabel, OVRPlatformToolSettings.RunOvrLint);
  269. // Add an Upload button
  270. GUI.enabled = !activeProcess;
  271. GUIContent btnTxt = new GUIContent("Upload");
  272. var rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
  273. var btnYPos = rt.center.y;
  274. rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 - rt.width / 2 - buttonPadding, btnYPos);
  275. if (GUI.Button(rt, btnTxt, GUI.skin.button))
  276. {
  277. OVRPlugin.SendEvent("oculus_platform_tool", "upload");
  278. OVRPlatformTool.log = string.Empty;
  279. OnUpload(OVRPlatformToolSettings.TargetPlatform);
  280. }
  281. // Add a cancel button
  282. GUI.enabled = activeProcess;
  283. btnTxt = new GUIContent("Cancel");
  284. rt = GUILayoutUtility.GetRect(btnTxt, GUI.skin.button, GUILayout.ExpandWidth(false));
  285. rt.center = new Vector2(EditorGUIUtility.currentViewWidth / 2 + rt.width / 2 + buttonPadding, btnYPos);
  286. if (GUI.Button(rt, btnTxt, GUI.skin.button))
  287. {
  288. if (EditorUtility.DisplayDialog("Cancel Upload Process", "Are you sure you want to cancel the upload process?", "Yes", "No"))
  289. {
  290. if (ovrPlatUtilProcess != null)
  291. {
  292. ovrPlatUtilProcess.Kill();
  293. OVRPlatformTool.log += "Upload process was canceled\n";
  294. }
  295. }
  296. }
  297. GUI.enabled = true;
  298. GUILayout.FlexibleSpace();
  299. GUILayout.Space(SINGLE_LINE_SPACING);
  300. debugLogScroll = EditorGUILayout.BeginScrollView(debugLogScroll);
  301. GUIStyle logBoxStyle = new GUIStyle();
  302. logBoxStyle.margin.left = 5;
  303. logBoxStyle.wordWrap = true;
  304. logBoxStyle.normal.textColor = logBoxStyle.focused.textColor = EditorStyles.label.normal.textColor;
  305. EditorGUILayout.SelectableLabel(OVRPlatformTool.log, logBoxStyle, GUILayout.Height(position.height - 30));
  306. EditorGUILayout.EndScrollView();
  307. }
  308. private void SetOVRProjectConfig(TargetPlatform targetPlatform)
  309. {
  310. #if UNITY_ANDROID
  311. var targetDeviceTypes = new List<OVRProjectConfig.DeviceType>();
  312. if (targetPlatform == TargetPlatform.Quest && !OVRDeviceSelector.isTargetDeviceQuest)
  313. {
  314. targetDeviceTypes.Add(OVRProjectConfig.DeviceType.Quest);
  315. }
  316. else if (targetPlatform == TargetPlatform.OculusGoGearVR && !OVRDeviceSelector.isTargetDeviceGearVrOrGo)
  317. {
  318. targetDeviceTypes.Add(OVRProjectConfig.DeviceType.GearVrOrGo);
  319. }
  320. if (targetDeviceTypes.Count != 0)
  321. {
  322. OVRProjectConfig projectConfig = OVRProjectConfig.GetProjectConfig();
  323. projectConfig.targetDeviceTypes = targetDeviceTypes;
  324. OVRProjectConfig.CommitProjectConfig(projectConfig);
  325. }
  326. #endif
  327. }
  328. private void IncrementIndent()
  329. {
  330. EditorGUI.indentLevel++;
  331. EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
  332. }
  333. private void DecrementIndent()
  334. {
  335. EditorGUI.indentLevel--;
  336. EditorGUIUtility.labelWidth = DEFAULT_LABEL_WIDTH - (EditorGUI.indentLevel * INDENT_SPACING);
  337. }
  338. private void OnUpload(TargetPlatform targetPlatform)
  339. {
  340. OVRPlatformTool.log = string.Empty;
  341. SetDirtyOnGUIChange();
  342. var lintCount = 0;
  343. if (OVRPlatformToolSettings.RunOvrLint)
  344. {
  345. lintCount = OVRLint.RunCheck();
  346. }
  347. if (lintCount != 0)
  348. {
  349. OVRPlatformTool.log += lintCount.ToString() + " lint suggestions are found. \n" +
  350. "Please run Oculus\\Tools\\OVR Performance Lint Tool to review and fix lint errors. \n" +
  351. "You can uncheck Run OVR Lint to bypass lint errors. \n";
  352. OVRPlugin.SendEvent("oculus_platform_tool_lint", lintCount.ToString());
  353. }
  354. else
  355. {
  356. // Continue uploading process
  357. ExecuteCommand(targetPlatform);
  358. }
  359. }
  360. static void ExecuteCommand(TargetPlatform targetPlatform)
  361. {
  362. string dataPath = Application.dataPath;
  363. // If we already have a copy of the platform util, check if it needs to be updated
  364. if (!ranSelfUpdate && File.Exists(dataPath + "/Oculus/VR/Editor/Tools/ovr-platform-util.exe"))
  365. {
  366. ranSelfUpdate = true;
  367. activeProcess = true;
  368. var updateThread = new Thread(delegate () {
  369. retryCount = 0;
  370. CheckForUpdate(dataPath);
  371. });
  372. updateThread.Start();
  373. }
  374. string uploadCommand;
  375. if (genUploadCommand(targetPlatform, out uploadCommand))
  376. {
  377. var thread = new Thread(delegate ()
  378. {
  379. // Wait for update process to finish before starting upload process
  380. while (activeProcess)
  381. {
  382. Thread.Sleep(100);
  383. }
  384. retryCount = 0;
  385. Command(targetPlatform, dataPath, uploadCommand);
  386. });
  387. thread.Start();
  388. }
  389. else
  390. {
  391. UnityEngine.Debug.LogError("Failed to generated upload command.");
  392. }
  393. }
  394. private static string CheckForPlatformUtil(string dataPath)
  395. {
  396. string toolDataPath = dataPath + "/Oculus/VR/Editor/Tools";
  397. if (!Directory.Exists(toolDataPath))
  398. {
  399. Directory.CreateDirectory(toolDataPath);
  400. }
  401. string platformUtil = toolDataPath + "/ovr-platform-util.exe";
  402. if (!System.IO.File.Exists(platformUtil))
  403. {
  404. OVRPlugin.SendEvent("oculus_platform_tool", "provision_util");
  405. EditorCoroutine downloadCoroutine = EditorCoroutine.Start(ProvisionPlatformUtil(platformUtil));
  406. while (!downloadCoroutine.GetCompleted()) { }
  407. }
  408. return platformUtil;
  409. }
  410. private static void InitializePlatformUtilProcess(string path, string args)
  411. {
  412. ovrPlatUtilProcess = new Process();
  413. var processInfo = new ProcessStartInfo(path, args);
  414. processInfo.CreateNoWindow = true;
  415. processInfo.UseShellExecute = false;
  416. processInfo.RedirectStandardError = true;
  417. processInfo.RedirectStandardOutput = true;
  418. ovrPlatUtilProcess.StartInfo = processInfo;
  419. ovrPlatUtilProcess.EnableRaisingEvents = true;
  420. }
  421. static void CheckForUpdate(string dataPath)
  422. {
  423. string platformUtilPath = CheckForPlatformUtil(dataPath);
  424. InitializePlatformUtilProcess(platformUtilPath, "self-update");
  425. OVRPlatformTool.log += "Checking for update...\n";
  426. ovrPlatUtilProcess.Exited += new EventHandler(
  427. (s, e) =>
  428. {
  429. if (File.Exists(dataPath + ".ovr-platform-util.exe"))
  430. {
  431. OVRPlatformTool.log += "Cleaning up...\n";
  432. while (File.Exists(dataPath + ".ovr-platform-util.exe")) { }
  433. OVRPlatformTool.log += "Finished updating platform utility.\n";
  434. }
  435. activeProcess = false;
  436. }
  437. );
  438. ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
  439. (s, e) =>
  440. {
  441. if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
  442. {
  443. OVRPlatformTool.log += e.Data + "\n";
  444. }
  445. }
  446. );
  447. try
  448. {
  449. ovrPlatUtilProcess.Start();
  450. ovrPlatUtilProcess.BeginOutputReadLine();
  451. }
  452. catch
  453. {
  454. if (ThrowPlatformUtilStartupError(platformUtilPath))
  455. {
  456. CheckForUpdate(dataPath);
  457. }
  458. }
  459. }
  460. static void LoadRedistPackages(string dataPath)
  461. {
  462. // Check / Download the platform util and call list-redists on it
  463. activeProcess = true;
  464. string platformUtilPath = CheckForPlatformUtil(dataPath);
  465. InitializePlatformUtilProcess(platformUtilPath, "list-redists");
  466. OVRPlatformTool.log += "Loading redistributable packages...\n";
  467. List<RedistPackage> redistPacks = new List<RedistPackage>();
  468. ovrPlatUtilProcess.Exited += new EventHandler(
  469. (s, e) =>
  470. {
  471. activeProcess = false;
  472. }
  473. );
  474. ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
  475. (s, e) =>
  476. {
  477. if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b") && !e.Data.Contains("ID"))
  478. {
  479. // Get the name / ID pair from the CLI and create a redist package instance
  480. string[] terms = e.Data.Split('|');
  481. if (terms.Length == 2)
  482. {
  483. RedistPackage redistPack = new RedistPackage(terms[1], terms[0]);
  484. redistPacks.Add(redistPack);
  485. }
  486. }
  487. }
  488. );
  489. try
  490. {
  491. ovrPlatUtilProcess.Start();
  492. ovrPlatUtilProcess.BeginOutputReadLine();
  493. ovrPlatUtilProcess.WaitForExit();
  494. if (redistPacks.Count != OVRPlatformToolSettings.RiftRedistPackages.Count)
  495. {
  496. OVRPlatformTool.log += "Successfully updated redistributable packages.\n";
  497. OVRPlatformToolSettings.RiftRedistPackages = redistPacks;
  498. }
  499. else
  500. {
  501. OVRPlatformTool.log += "Redistributable packages up to date.\n";
  502. }
  503. }
  504. catch
  505. {
  506. if (ThrowPlatformUtilStartupError(platformUtilPath))
  507. {
  508. LoadRedistPackages(dataPath);
  509. }
  510. }
  511. }
  512. static void Command(TargetPlatform targetPlatform, string dataPath, string uploadCommand)
  513. {
  514. string platformUtilPath = CheckForPlatformUtil(dataPath);
  515. activeProcess = true;
  516. InitializePlatformUtilProcess(platformUtilPath, uploadCommand);
  517. ovrPlatUtilProcess.Exited += new EventHandler(
  518. (s, e) =>
  519. {
  520. activeProcess = false;
  521. }
  522. );
  523. ovrPlatUtilProcess.OutputDataReceived += new DataReceivedEventHandler(
  524. (s, e) =>
  525. {
  526. if (e.Data != null && e.Data.Length != 0 && !e.Data.Contains("\u001b"))
  527. {
  528. OVRPlatformTool.log += e.Data + "\n";
  529. }
  530. }
  531. );
  532. ovrPlatUtilProcess.ErrorDataReceived += new DataReceivedEventHandler(
  533. (s, e) =>
  534. {
  535. OVRPlatformTool.log += e.Data + "\n";
  536. }
  537. );
  538. try
  539. {
  540. ovrPlatUtilProcess.Start();
  541. ovrPlatUtilProcess.BeginOutputReadLine();
  542. ovrPlatUtilProcess.BeginErrorReadLine();
  543. }
  544. catch
  545. {
  546. if (ThrowPlatformUtilStartupError(platformUtilPath))
  547. {
  548. Command(targetPlatform, dataPath, uploadCommand);
  549. }
  550. }
  551. }
  552. private static bool genUploadCommand(TargetPlatform targetPlatform, out string command)
  553. {
  554. bool success = true;
  555. command = "";
  556. switch (targetPlatform)
  557. {
  558. case TargetPlatform.Rift:
  559. command = "upload-rift-build";
  560. break;
  561. case TargetPlatform.OculusGoGearVR:
  562. command = "upload-mobile-build";
  563. break;
  564. case TargetPlatform.Quest:
  565. command = "upload-quest-build";
  566. break;
  567. default:
  568. OVRPlatformTool.log += "ERROR: Invalid target platform selected";
  569. success = false;
  570. break;
  571. }
  572. // Add App ID
  573. ValidateTextField(AppIDFieldValidator, OVRPlatformToolSettings.AppID, "App ID", ref success);
  574. command += " --app-id \"" + OVRPlatformToolSettings.AppID + "\"";
  575. // Add App Token
  576. ValidateTextField(GenericFieldValidator, appToken, "App Token", ref success);
  577. command += " --app-secret \"" + appToken + "\"";
  578. // Add Platform specific fields
  579. if (targetPlatform == TargetPlatform.Rift)
  580. {
  581. // Add Rift Build Directory
  582. ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.RiftBuildDirectory, "Rift Build Directory", ref success);
  583. command += " --build-dir \"" + OVRPlatformToolSettings.RiftBuildDirectory + "\"";
  584. // Add Rift Launch File
  585. ValidateTextField(FileValidator, OVRPlatformToolSettings.RiftLaunchFile, "Rift Launch File Path", ref success);
  586. command += " --launch-file \"" + OVRPlatformToolSettings.RiftLaunchFile + "\"";
  587. // Add Rift Build Version
  588. ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.RiftBuildVersion, "Build Version", ref success);
  589. command += " --version \"" + OVRPlatformToolSettings.RiftBuildVersion + "\"";
  590. // Add Rift Launch Parameters
  591. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.RiftLaunchParams))
  592. {
  593. ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.RiftLaunchParams, "Launch Parameters", ref success);
  594. command += " --launch_params \"" + OVRPlatformToolSettings.RiftLaunchParams + "\"";
  595. }
  596. // Add 2D Launch File
  597. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchFile))
  598. {
  599. ValidateTextField(FileValidator, OVRPlatformToolSettings.Rift2DLaunchFile, "2D Launch File", ref success);
  600. command += " --launch_file_2d \"" + OVRPlatformToolSettings.Rift2DLaunchFile + "\"";
  601. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.Rift2DLaunchParams))
  602. {
  603. ValidateTextField(LaunchParameterValidator, OVRPlatformToolSettings.Rift2DLaunchParams, "2D Launch Parameters", ref success);
  604. command += " --launch_params_2d \"" + OVRPlatformToolSettings.Rift2DLaunchParams + "\"";
  605. }
  606. }
  607. // Add Firewall Exception
  608. if (OVRPlatformToolSettings.RiftFirewallException)
  609. {
  610. command += " --firewall_exceptions true";
  611. }
  612. // Add Redistributable Packages
  613. List<string> redistCommandIds = new List<string>();
  614. for (int i = 0; i < OVRPlatformToolSettings.RiftRedistPackages.Count; i++)
  615. {
  616. if (OVRPlatformToolSettings.RiftRedistPackages[i].include)
  617. {
  618. redistCommandIds.Add(OVRPlatformToolSettings.RiftRedistPackages[i].id);
  619. }
  620. }
  621. if (redistCommandIds.Count > 0)
  622. {
  623. command += " --redistributables \"" + string.Join(",", redistCommandIds.ToArray()) + "\"";
  624. }
  625. // Add Gamepad Emulation
  626. if (OVRPlatformToolSettings.RiftGamepadEmulation > OVRPlatformToolSettings.GamepadType.OFF &&
  627. OVRPlatformToolSettings.RiftGamepadEmulation <= OVRPlatformToolSettings.GamepadType.LEFT_D_PAD)
  628. {
  629. command += " --gamepad-emulation ";
  630. switch (OVRPlatformToolSettings.RiftGamepadEmulation)
  631. {
  632. case OVRPlatformToolSettings.GamepadType.TWINSTICK: command += "TWINSTICK"; break;
  633. case OVRPlatformToolSettings.GamepadType.RIGHT_D_PAD: command += "RIGHT_D_PAD"; break;
  634. case OVRPlatformToolSettings.GamepadType.LEFT_D_PAD: command += "LEFT_D_PAD"; break;
  635. default: command += "OFF"; break;
  636. }
  637. }
  638. // Add Rift Language Pack Directory
  639. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.LanguagePackDirectory))
  640. {
  641. ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.LanguagePackDirectory, "Language Pack Directory", ref success);
  642. command += " --language_packs_dir \"" + OVRPlatformToolSettings.LanguagePackDirectory + "\"";
  643. }
  644. }
  645. else
  646. {
  647. // Add APK Build Path
  648. ValidateTextField(FileValidator, OVRPlatformToolSettings.ApkBuildPath, "APK Build Path", ref success);
  649. command += " --apk \"" + OVRPlatformToolSettings.ApkBuildPath + "\"";
  650. // Add OBB File Path
  651. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ObbFilePath))
  652. {
  653. ValidateTextField(FileValidator, OVRPlatformToolSettings.ObbFilePath, "OBB File Path", ref success);
  654. command += " --obb \"" + OVRPlatformToolSettings.ObbFilePath + "\"";
  655. }
  656. if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.OculusGoGearVR)
  657. {
  658. // Go and Gear VR specific commands
  659. }
  660. else if (OVRPlatformToolSettings.TargetPlatform == TargetPlatform.Quest)
  661. {
  662. // Quest specific commands
  663. }
  664. }
  665. // Add Assets Directory
  666. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.AssetsDirectory))
  667. {
  668. ValidateTextField(DirectoryValidator, OVRPlatformToolSettings.AssetsDirectory, "Assets Directory", ref success);
  669. command += " --assets-dir \"" + OVRPlatformToolSettings.AssetsDirectory + "\"";
  670. // Add Asset Configurations
  671. if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
  672. {
  673. List<string> assetConfigs = new List<string>();
  674. for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
  675. {
  676. List<string> configParameters = new List<string>();
  677. AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
  678. if (config.required)
  679. {
  680. configParameters.Add("\\\"required\\\":true");
  681. }
  682. if (config.type > AssetConfig.AssetType.DEFAULT)
  683. {
  684. string typeCommand = "\\\"type\\\":";
  685. switch (config.type)
  686. {
  687. case AssetConfig.AssetType.LANGUAGE_PACK:
  688. configParameters.Add(typeCommand + "\\\"LANGUAGE_PACK\\\"");
  689. break;
  690. case AssetConfig.AssetType.STORE:
  691. configParameters.Add(typeCommand + "\\\"STORE\\\"");
  692. break;
  693. default:
  694. configParameters.Add(typeCommand + "\\\"DEFAULT\\\"");
  695. break;
  696. }
  697. }
  698. if (!string.IsNullOrEmpty(config.sku))
  699. {
  700. configParameters.Add("\\\"sku\\\":\\\"" + config.sku + "\\\"");
  701. }
  702. if (configParameters.Count > 0)
  703. {
  704. string configString = "\\\"" + config.name + "\\\":{" + string.Join(",", configParameters.ToArray()) + "}";
  705. assetConfigs.Add(configString);
  706. }
  707. }
  708. if (assetConfigs.Count > 0)
  709. {
  710. command += " --asset_files_config {" + string.Join(",", assetConfigs.ToArray()) + "}";
  711. }
  712. }
  713. }
  714. // Add Release Channel
  715. ValidateTextField(GenericFieldValidator, OVRPlatformToolSettings.ReleaseChannel, "Release Channel", ref success);
  716. command += " --channel \"" + OVRPlatformToolSettings.ReleaseChannel + "\"";
  717. // Add Notes
  718. if (!string.IsNullOrEmpty(OVRPlatformToolSettings.ReleaseNote))
  719. {
  720. string sanatizedReleaseNote = OVRPlatformToolSettings.ReleaseNote;
  721. sanatizedReleaseNote = sanatizedReleaseNote.Replace("\"", "\"\"");
  722. command += " --notes \"" + sanatizedReleaseNote + "\"";
  723. }
  724. return success;
  725. }
  726. // Private delegate for text field validation functions
  727. private delegate TSuccess FieldValidatorDelegate<in TText, TError, out TSuccess>(TText text, ref TError error);
  728. // Validate the text using a given field validator function. An error message will be printed if validation fails. Success will ONLY be modified to false if validation fails.
  729. static void ValidateTextField(FieldValidatorDelegate<string, string, bool> fieldValidator, string fieldText, string fieldName, ref bool success)
  730. {
  731. string error = "";
  732. if (!fieldValidator(fieldText, ref error))
  733. {
  734. OVRPlatformTool.log += "ERROR: Please verify that the " + fieldName + " is correct. ";
  735. OVRPlatformTool.log += string.IsNullOrEmpty(error) ? "\n" : error + "\n";
  736. success = false;
  737. }
  738. }
  739. // Checks if the text is null or empty
  740. static bool GenericFieldValidator(string fieldText, ref string error)
  741. {
  742. if (string.IsNullOrEmpty(fieldText))
  743. {
  744. error = "The field is empty.";
  745. return false;
  746. }
  747. return true;
  748. }
  749. // Checks if the App ID contains only numbers
  750. static bool AppIDFieldValidator(string fieldText, ref string error)
  751. {
  752. if (string.IsNullOrEmpty(fieldText))
  753. {
  754. error = "The field is empty.";
  755. return false;
  756. }
  757. else if (!Regex.IsMatch(OVRPlatformToolSettings.AppID, "^[0-9]+$"))
  758. {
  759. error = "The field contains invalid characters.";
  760. return false;
  761. }
  762. return true;
  763. }
  764. // Check that the directory exists
  765. static bool DirectoryValidator(string path, ref string error)
  766. {
  767. if (!Directory.Exists(path))
  768. {
  769. error = "The directory does not exist.";
  770. return false;
  771. }
  772. return true;
  773. }
  774. // Check that the file exists
  775. static bool FileValidator(string path, ref string error)
  776. {
  777. if (!File.Exists(path))
  778. {
  779. error = "The file does not exist.";
  780. return false;
  781. }
  782. return true;
  783. }
  784. // Check if the launch parameter string contains illegal characters
  785. static bool LaunchParameterValidator(string fieldText, ref string error)
  786. {
  787. if (fieldText.Contains("\""))
  788. {
  789. error = "The field contains illegal characters.";
  790. return false;
  791. }
  792. return true;
  793. }
  794. void OnInspectorUpdate()
  795. {
  796. Repaint();
  797. }
  798. private static bool ThrowPlatformUtilStartupError(string utilPath)
  799. {
  800. if (retryCount < MAX_DOWNLOAD_RETRY_COUNT)
  801. {
  802. retryCount++;
  803. OVRPlatformTool.log += "There was a problem starting Oculus Platform Util. Restarting provision process...\n";
  804. File.Delete(utilPath);
  805. return true;
  806. }
  807. else
  808. {
  809. OVRPlatformTool.log += "OVR Platform Tool had a problem with downloading a valid executable after several trys. Please reopen the tool to try again.\n";
  810. return false;
  811. }
  812. }
  813. private string MakeTextBox(GUIContent label, string variable)
  814. {
  815. string result = string.Empty;
  816. EditorGUILayout.BeginHorizontal();
  817. EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
  818. result = EditorGUILayout.TextField(variable);
  819. EditorGUILayout.EndHorizontal();
  820. return result;
  821. }
  822. private string MakePasswordBox(GUIContent label, string variable)
  823. {
  824. string result = string.Empty;
  825. EditorGUILayout.BeginHorizontal();
  826. EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
  827. result = EditorGUILayout.PasswordField(variable);
  828. EditorGUILayout.EndHorizontal();
  829. return result;
  830. }
  831. private bool MakeToggleBox(GUIContent label, bool variable)
  832. {
  833. bool result = false;
  834. EditorGUILayout.BeginHorizontal();
  835. EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
  836. result = EditorGUILayout.Toggle(variable);
  837. EditorGUILayout.EndHorizontal();
  838. return result;
  839. }
  840. private int MakePopup(GUIContent label, int variable, string[] options)
  841. {
  842. int result = 0;
  843. EditorGUILayout.BeginHorizontal();
  844. EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
  845. result = EditorGUILayout.Popup(variable, options);
  846. EditorGUILayout.EndHorizontal();
  847. return result;
  848. }
  849. private string MakeFileDirectoryField(GUIContent label, string variable, string title, bool isFile = false, string extension = "")
  850. {
  851. EditorGUILayout.BeginHorizontal();
  852. EditorGUILayout.LabelField(label, GUILayout.ExpandWidth(false));
  853. Rect rect = GUILayoutUtility.GetRect(0, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(true));
  854. EditorGUI.SelectableLabel(rect, variable);
  855. // Create X button if there is a valid path in the field
  856. string result = variable;
  857. if (!string.IsNullOrEmpty(variable))
  858. {
  859. Color defaultColor = GUI.backgroundColor;
  860. GUI.backgroundColor = new Color(.9f, 0.5f, 0.5f);
  861. rect = GUILayoutUtility.GetRect(SINGLE_LINE_SPACING, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
  862. if (GUI.Button(rect, "X"))
  863. {
  864. result = string.Empty;
  865. }
  866. GUI.backgroundColor = defaultColor;
  867. }
  868. // Create the Choose button to initiate the file explorer
  869. rect = GUILayoutUtility.GetRect(75f, SINGLE_LINE_SPACING, GUILayout.ExpandWidth(false));
  870. if (GUI.Button(rect, "Choose ..."))
  871. {
  872. string newPath = string.Empty;
  873. string path = string.IsNullOrEmpty(variable) ? Application.dataPath : variable;
  874. if (isFile)
  875. {
  876. newPath = EditorUtility.OpenFilePanel(title, path, extension);
  877. }
  878. else
  879. {
  880. newPath = EditorUtility.OpenFolderPanel(title, path, string.Empty);
  881. }
  882. if (newPath.Length > 0)
  883. {
  884. result = newPath;
  885. }
  886. }
  887. GUILayout.Space(5f);
  888. EditorGUILayout.EndHorizontal();
  889. // If the path has changed, deselect the selectable field so that it can update.
  890. if (result != variable)
  891. {
  892. GUIUtility.hotControl = 0;
  893. GUIUtility.keyboardControl = 0;
  894. }
  895. return result;
  896. }
  897. private static void SetDirtyOnGUIChange()
  898. {
  899. if (GUI.changed)
  900. {
  901. EditorUtility.SetDirty(OVRPlatformToolSettings.Instance);
  902. GUI.changed = false;
  903. }
  904. }
  905. private static IEnumerator ProvisionPlatformUtil(string dataPath)
  906. {
  907. UnityEngine.Debug.Log("Started Provisioning Oculus Platform Util");
  908. #if UNITY_2018_3_OR_NEWER
  909. var webRequest = new UnityWebRequest(urlPlatformUtil, UnityWebRequest.kHttpVerbGET);
  910. string path = dataPath;
  911. webRequest.downloadHandler = new DownloadHandlerFile(path);
  912. // WWW request timeout in seconds
  913. webRequest.timeout = 60;
  914. UnityWebRequestAsyncOperation webOp = webRequest.SendWebRequest();
  915. while (!webOp.isDone) { }
  916. if (webRequest.isNetworkError || webRequest.isHttpError)
  917. {
  918. var networkErrorMsg = "Failed to provision Oculus Platform Util\n";
  919. UnityEngine.Debug.LogError(networkErrorMsg);
  920. OVRPlatformTool.log += networkErrorMsg;
  921. }
  922. else
  923. {
  924. OVRPlatformTool.log += "Completed Provisioning Oculus Platform Util\n";
  925. }
  926. SetDirtyOnGUIChange();
  927. yield return webOp;
  928. #else
  929. using (WWW www = new WWW(urlPlatformUtil))
  930. {
  931. float timer = 0;
  932. float timeOut = 60;
  933. yield return www;
  934. while (!www.isDone && timer < timeOut)
  935. {
  936. timer += Time.deltaTime;
  937. if (www.error != null)
  938. {
  939. UnityEngine.Debug.Log("Download error: " + www.error);
  940. break;
  941. }
  942. OVRPlatformTool.log = string.Format("Downloading.. {0:P1}", www.progress);
  943. SetDirtyOnGUIChange();
  944. yield return new WaitForSeconds(1f);
  945. }
  946. if (www.isDone)
  947. {
  948. System.IO.File.WriteAllBytes(dataPath, www.bytes);
  949. OVRPlatformTool.log = "Completed Provisioning Oculus Platform Util\n";
  950. SetDirtyOnGUIChange();
  951. }
  952. }
  953. #endif
  954. }
  955. private static void DrawAssetConfigList(Rect rect)
  956. {
  957. DrawAssetConfigHeader(rect);
  958. DrawAssetConfigBackground(rect);
  959. DrawAssetConfigElement(rect);
  960. }
  961. private static void DrawAssetConfigElement(Rect rect)
  962. {
  963. Rect elementRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING + ASSET_CONFIG_BACKGROUND_PADDING / 2,
  964. rect.width, SINGLE_LINE_SPACING);
  965. if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
  966. {
  967. for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
  968. {
  969. AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
  970. GUIContent fieldLabel;
  971. config.SetFoldoutState(EditorGUI.Foldout(elementRect, config.GetFoldoutState(), config.name, boldFoldoutStyle));
  972. if (config.GetFoldoutState())
  973. {
  974. Rect attributeRect = new Rect(elementRect.x + INDENT_SPACING, elementRect.y + SINGLE_LINE_SPACING,
  975. elementRect.width - INDENT_SPACING - 3f, SINGLE_LINE_SPACING);
  976. // Extra asset config params are disabled for now until CLI supports them
  977. #if !DISABLE_EXTRA_ASSET_CONFIG
  978. fieldLabel = new GUIContent("Required Asset [?]", "Whether or not this asset file is required for the app to run.");
  979. config.required = EditorGUI.Toggle(attributeRect, fieldLabel, config.required);
  980. attributeRect.y += SINGLE_LINE_SPACING;
  981. fieldLabel = new GUIContent("Asset Type [?]", "The asset file type.");
  982. config.type = (AssetConfig.AssetType)EditorGUI.EnumPopup(attributeRect, fieldLabel, config.type);
  983. attributeRect.y += SINGLE_LINE_SPACING;
  984. #endif
  985. fieldLabel = new GUIContent("Asset SKU [?]", "The Oculus store SKU for this asset file.");
  986. config.sku = EditorGUI.TextField(attributeRect, fieldLabel, config.sku);
  987. elementRect.y = attributeRect.y;
  988. }
  989. elementRect.y += SINGLE_LINE_SPACING;
  990. }
  991. }
  992. else
  993. {
  994. EditorGUI.LabelField(elementRect, "No asset files found. Choose a valid assets directory.");
  995. }
  996. }
  997. private static float GetAssetConfigElementHeight()
  998. {
  999. float totalHeight = 0f;
  1000. if (OVRPlatformToolSettings.AssetConfigs.Count > 0)
  1001. {
  1002. for (int i = 0; i < OVRPlatformToolSettings.AssetConfigs.Count; i++)
  1003. {
  1004. AssetConfig config = OVRPlatformToolSettings.AssetConfigs[i];
  1005. #if !DISABLE_EXTRA_ASSET_CONFIG
  1006. totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 4 : SINGLE_LINE_SPACING;
  1007. #else
  1008. totalHeight += config.GetFoldoutState() ? SINGLE_LINE_SPACING * 2 : SINGLE_LINE_SPACING;
  1009. #endif
  1010. }
  1011. }
  1012. else
  1013. {
  1014. totalHeight += SINGLE_LINE_SPACING;
  1015. }
  1016. return totalHeight + ASSET_CONFIG_BACKGROUND_PADDING;
  1017. }
  1018. private static void DrawAssetConfigHeader(Rect rect)
  1019. {
  1020. Rect headerRect = new Rect(rect.x, rect.y, rect.width, SINGLE_LINE_SPACING);
  1021. EditorGUI.DrawRect(headerRect, EditorGUIUtility.isProSkin ? new Color(0.37f, 0.37f, 0.37f) : new Color(0.55f, 0.55f, 0.55f));
  1022. EditorGUI.LabelField(rect, "Asset File Configuration");
  1023. }
  1024. private static void DrawAssetConfigBackground(Rect rect)
  1025. {
  1026. Rect backgroundRect = new Rect(rect.x, rect.y + SINGLE_LINE_SPACING, rect.width, GetAssetConfigElementHeight());
  1027. EditorGUI.DrawRect(backgroundRect, EditorGUIUtility.isProSkin ? new Color(0.3f, 0.3f, 0.3f) : new Color(0.63f, 0.63f, 0.63f));
  1028. }
  1029. class GUIHelper
  1030. {
  1031. public delegate void Worker();
  1032. static void InOut(Worker begin, Worker body, Worker end)
  1033. {
  1034. try
  1035. {
  1036. begin();
  1037. body();
  1038. }
  1039. finally
  1040. {
  1041. end();
  1042. }
  1043. }
  1044. public static void HInset(int pixels, Worker worker)
  1045. {
  1046. InOut(
  1047. () => {
  1048. GUILayout.BeginHorizontal();
  1049. GUILayout.Space(pixels);
  1050. GUILayout.BeginVertical();
  1051. },
  1052. worker,
  1053. () => {
  1054. GUILayout.EndVertical();
  1055. GUILayout.EndHorizontal();
  1056. }
  1057. );
  1058. }
  1059. public delegate T ControlWorker<T>();
  1060. public static T MakeControlWithLabel<T>(GUIContent label, ControlWorker<T> worker)
  1061. {
  1062. EditorGUILayout.BeginHorizontal();
  1063. EditorGUILayout.LabelField(label);
  1064. var result = worker();
  1065. EditorGUILayout.EndHorizontal();
  1066. return result;
  1067. }
  1068. }
  1069. public class EditorCoroutine
  1070. {
  1071. public static EditorCoroutine Start(IEnumerator routine)
  1072. {
  1073. EditorCoroutine coroutine = new EditorCoroutine(routine);
  1074. coroutine.Start();
  1075. return coroutine;
  1076. }
  1077. readonly IEnumerator routine;
  1078. bool completed;
  1079. EditorCoroutine(IEnumerator _routine)
  1080. {
  1081. routine = _routine;
  1082. completed = false;
  1083. }
  1084. void Start()
  1085. {
  1086. EditorApplication.update += Update;
  1087. }
  1088. public void Stop()
  1089. {
  1090. EditorApplication.update -= Update;
  1091. completed = true;
  1092. }
  1093. public bool GetCompleted()
  1094. {
  1095. return completed;
  1096. }
  1097. void Update()
  1098. {
  1099. if (!routine.MoveNext())
  1100. {
  1101. Stop();
  1102. }
  1103. }
  1104. }
  1105. }
  1106. }