OVRPluginUpdater.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /************************************************************************************
  2. Copyright : Copyright 2017 Oculus VR, LLC. All Rights reserved.
  3. Licensed under the Oculus VR Rift SDK License Version 3.4.1 (the "License");
  4. you may not use the Oculus VR Rift SDK except in compliance with the License,
  5. which is provided at the time of installation or download, or which
  6. otherwise accompanies this software in either electronic or hard copy form.
  7. You may obtain a copy of the License at
  8. https://developer.oculus.com/licenses/sdk-3.4.1
  9. Unless required by applicable law or agreed to in writing, the Oculus VR SDK
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ************************************************************************************/
  15. using UnityEngine;
  16. using UnityEditor;
  17. using UnityEditor.Callbacks;
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Text.RegularExpressions;
  22. using System.IO;
  23. using System.Diagnostics;
  24. [InitializeOnLoad]
  25. class OVRPluginUpdater
  26. {
  27. class PluginPackage
  28. {
  29. public string RootPath;
  30. public System.Version Version;
  31. public Dictionary<BuildTarget, string> Plugins = new Dictionary<BuildTarget, string>();
  32. public bool IsBundledPluginPackage()
  33. {
  34. return (RootPath == GetBundledPluginRootPath());
  35. }
  36. public bool IsEnabled()
  37. {
  38. // TODO: Check each individual platform rather than using the Win64 DLL status for the overall package status.
  39. string win64PluginPath = "";
  40. if (Plugins.TryGetValue(BuildTarget.StandaloneWindows64, out win64PluginPath))
  41. {
  42. return File.Exists(win64PluginPath);
  43. }
  44. return false;
  45. }
  46. }
  47. static OVRPluginUpdater()
  48. {
  49. EditorApplication.delayCall += OnDelayCall;
  50. }
  51. static void OnDelayCall()
  52. {
  53. if (ShouldAttemptPluginUpdate())
  54. {
  55. AttemptPluginUpdate(true);
  56. }
  57. }
  58. private static PluginPackage GetPluginPackage(string rootPath)
  59. {
  60. return new PluginPackage()
  61. {
  62. RootPath = rootPath,
  63. Version = GetPluginVersion(rootPath),
  64. Plugins = new Dictionary<BuildTarget, string>()
  65. {
  66. { BuildTarget.Android, rootPath + GetPluginBuildTargetSubPath(BuildTarget.Android) },
  67. #if UNITY_2017_3_OR_NEWER
  68. { BuildTarget.StandaloneOSX, rootPath + GetPluginBuildTargetSubPath(BuildTarget.StandaloneOSX) },
  69. #else
  70. { BuildTarget.StandaloneOSXUniversal, rootPath + GetPluginBuildTargetSubPath(BuildTarget.StandaloneOSXUniversal) },
  71. #endif
  72. { BuildTarget.StandaloneWindows, rootPath + GetPluginBuildTargetSubPath(BuildTarget.StandaloneWindows) },
  73. { BuildTarget.StandaloneWindows64, rootPath + GetPluginBuildTargetSubPath(BuildTarget.StandaloneWindows64) },
  74. }
  75. };
  76. }
  77. private static PluginPackage GetBundledPluginPackage()
  78. {
  79. return GetPluginPackage(GetBundledPluginRootPath());
  80. }
  81. private static List<PluginPackage> GetAllUtilitiesPluginPackages()
  82. {
  83. string pluginRootPath = GetUtilitiesPluginRootPath();
  84. List<PluginPackage> packages = new List<PluginPackage>();
  85. if (Directory.Exists(pluginRootPath))
  86. {
  87. var dirs = Directory.GetDirectories(pluginRootPath);
  88. foreach(string dir in dirs)
  89. {
  90. packages.Add(GetPluginPackage(dir));
  91. }
  92. }
  93. return packages;
  94. }
  95. private static string GetCurrentProjectPath()
  96. {
  97. return Directory.GetParent(Application.dataPath).FullName;
  98. }
  99. private static string GetUtilitiesPluginRootPath()
  100. {
  101. return GetUtilitiesRootPath() + @"/Plugins";
  102. }
  103. private static string GetUtilitiesRootPath()
  104. {
  105. var so = ScriptableObject.CreateInstance(typeof(OVRPluginUpdaterStub));
  106. var script = MonoScript.FromScriptableObject(so);
  107. string assetPath = AssetDatabase.GetAssetPath(script);
  108. string editorDir = Directory.GetParent(assetPath).FullName;
  109. string ovrDir = Directory.GetParent(editorDir).FullName;
  110. return ovrDir;
  111. }
  112. private static string GetBundledPluginRootPath()
  113. {
  114. string basePath = EditorApplication.applicationContentsPath;
  115. string pluginPath = @"/UnityExtensions/Unity/VR";
  116. return basePath + pluginPath;
  117. }
  118. private static string GetPluginBuildTargetSubPath(BuildTarget target)
  119. {
  120. string path = string.Empty;
  121. switch (target)
  122. {
  123. case BuildTarget.Android:
  124. path = @"/Android/OVRPlugin.aar";
  125. break;
  126. #if UNITY_2017_3_OR_NEWER
  127. case BuildTarget.StandaloneOSX:
  128. #else
  129. case BuildTarget.StandaloneOSXUniversal:
  130. #endif
  131. path = @"/OSXUniversal/OVRPlugin.bundle";
  132. break;
  133. case BuildTarget.StandaloneWindows:
  134. path = @"/Win/OVRPlugin.dll";
  135. break;
  136. case BuildTarget.StandaloneWindows64:
  137. path = @"/Win64/OVRPlugin.dll";
  138. break;
  139. default:
  140. throw new ArgumentException("Attempted GetPluginBuildTargetSubPath() for unsupported BuildTarget: " + target);
  141. }
  142. return path;
  143. }
  144. private static string GetDisabledPluginSuffix()
  145. {
  146. return @".disabled";
  147. }
  148. private static System.Version GetPluginVersion(string path)
  149. {
  150. System.Version invalidVersion = new System.Version("0.0.0");
  151. System.Version pluginVersion = invalidVersion;
  152. try
  153. {
  154. pluginVersion = new System.Version(Path.GetFileName(path));
  155. }
  156. catch
  157. {
  158. pluginVersion = invalidVersion;
  159. }
  160. if (pluginVersion == invalidVersion)
  161. {
  162. //Unable to determine version from path, fallback to Win64 DLL meta data
  163. path += GetPluginBuildTargetSubPath(BuildTarget.StandaloneWindows64);
  164. if (!File.Exists(path))
  165. {
  166. path += GetDisabledPluginSuffix();
  167. if (!File.Exists(path))
  168. {
  169. return invalidVersion;
  170. }
  171. }
  172. FileVersionInfo pluginVersionInfo = FileVersionInfo.GetVersionInfo(path);
  173. if (pluginVersionInfo == null || pluginVersionInfo.ProductVersion == null || pluginVersionInfo.ProductVersion == "")
  174. {
  175. return invalidVersion;
  176. }
  177. pluginVersion = new System.Version(pluginVersionInfo.ProductVersion);
  178. }
  179. return pluginVersion;
  180. }
  181. private static bool ShouldAttemptPluginUpdate()
  182. {
  183. return autoUpdateEnabled && !restartPending && !Application.isPlaying;
  184. }
  185. private static void DisableAllUtilitiesPluginPackages()
  186. {
  187. List<PluginPackage> allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
  188. foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
  189. {
  190. foreach(string path in pluginPkg.Plugins.Values)
  191. {
  192. if ((Directory.Exists(path)) || (File.Exists(path)))
  193. {
  194. string basePath = GetCurrentProjectPath();
  195. string relPath = path.Substring(basePath.Length + 1);
  196. AssetDatabase.MoveAsset(relPath, relPath + GetDisabledPluginSuffix());
  197. AssetDatabase.ImportAsset(relPath + GetDisabledPluginSuffix(), ImportAssetOptions.ForceUpdate);
  198. }
  199. }
  200. }
  201. AssetDatabase.Refresh();
  202. AssetDatabase.SaveAssets();
  203. }
  204. private static void EnablePluginPackage(PluginPackage pluginPkg)
  205. {
  206. foreach(var kvp in pluginPkg.Plugins)
  207. {
  208. BuildTarget platform = kvp.Key;
  209. string path = kvp.Value;
  210. if ((Directory.Exists(path + GetDisabledPluginSuffix())) || (File.Exists(path + GetDisabledPluginSuffix())))
  211. {
  212. string basePath = GetCurrentProjectPath();
  213. string relPath = path.Substring(basePath.Length + 1);
  214. AssetDatabase.MoveAsset(relPath + GetDisabledPluginSuffix(), relPath);
  215. AssetDatabase.ImportAsset(relPath, ImportAssetOptions.ForceUpdate);
  216. PluginImporter pi = PluginImporter.GetAtPath(relPath) as PluginImporter;
  217. if (pi == null)
  218. {
  219. continue;
  220. }
  221. pi.SetCompatibleWithAnyPlatform(false);
  222. switch (platform)
  223. {
  224. case BuildTarget.Android:
  225. pi.SetCompatibleWithPlatform(BuildTarget.Android, true);
  226. pi.SetPlatformData(BuildTarget.Android, "CPU", "ARMv7");
  227. #if UNITY_2017_3_OR_NEWER
  228. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false);
  229. #else
  230. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXUniversal, false);
  231. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel, false);
  232. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel64, false);
  233. #endif
  234. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false);
  235. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false);
  236. pi.SetCompatibleWithEditor(false);
  237. break;
  238. #if UNITY_2017_3_OR_NEWER
  239. case BuildTarget.StandaloneOSX:
  240. #else
  241. case BuildTarget.StandaloneOSXUniversal:
  242. #endif
  243. pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
  244. pi.SetPlatformData(BuildTarget.Android, "CPU", "ARMv7");
  245. #if UNITY_2017_3_OR_NEWER
  246. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, true);
  247. #else
  248. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXUniversal, true);
  249. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel, true);
  250. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel64, true);
  251. #endif
  252. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false);
  253. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false);
  254. pi.SetCompatibleWithEditor(true);
  255. pi.SetEditorData("CPU", "AnyCPU");
  256. pi.SetEditorData("OS", "OSX");
  257. pi.SetPlatformData("Editor", "CPU", "AnyCPU");
  258. pi.SetPlatformData("Editor", "OS", "OSX");
  259. break;
  260. case BuildTarget.StandaloneWindows:
  261. pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
  262. pi.SetPlatformData(BuildTarget.Android, "CPU", "ARMv7");
  263. #if UNITY_2017_3_OR_NEWER
  264. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false);
  265. #else
  266. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXUniversal, false);
  267. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel, false);
  268. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel64, false);
  269. #endif
  270. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, true);
  271. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false);
  272. pi.SetCompatibleWithEditor(true);
  273. pi.SetEditorData("CPU", "X86");
  274. pi.SetEditorData("OS", "Windows");
  275. pi.SetPlatformData("Editor", "CPU", "X86");
  276. pi.SetPlatformData("Editor", "OS", "Windows");
  277. break;
  278. case BuildTarget.StandaloneWindows64:
  279. pi.SetCompatibleWithPlatform(BuildTarget.Android, false);
  280. pi.SetPlatformData(BuildTarget.Android, "CPU", "ARMv7");
  281. #if UNITY_2017_3_OR_NEWER
  282. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSX, false);
  283. #else
  284. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXUniversal, false);
  285. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel, false);
  286. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneOSXIntel64, false);
  287. #endif
  288. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false);
  289. pi.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, true);
  290. pi.SetCompatibleWithEditor(true);
  291. pi.SetEditorData("CPU", "X86_64");
  292. pi.SetEditorData("OS", "Windows");
  293. pi.SetPlatformData("Editor", "CPU", "X86_64");
  294. pi.SetPlatformData("Editor", "OS", "Windows");
  295. break;
  296. default:
  297. throw new ArgumentException("Attempted EnablePluginPackage() for unsupported BuildTarget: " + platform);
  298. }
  299. AssetDatabase.ImportAsset(relPath, ImportAssetOptions.ForceUpdate);
  300. }
  301. }
  302. AssetDatabase.Refresh();
  303. AssetDatabase.SaveAssets();
  304. }
  305. private static bool restartPending = false;
  306. private static readonly string autoUpdateEnabledKey = "Oculus_Utilities_OVRPluginUpdater_AutoUpdate_" + OVRManager.utilitiesVersion;
  307. private static bool autoUpdateEnabled
  308. {
  309. get {
  310. return PlayerPrefs.GetInt(autoUpdateEnabledKey, 1) == 1;
  311. }
  312. set {
  313. PlayerPrefs.SetInt(autoUpdateEnabledKey, value ? 1 : 0);
  314. }
  315. }
  316. [MenuItem("Tools/Oculus/Disable OVR Utilities Plugin")]
  317. private static void AttemptPluginDisable()
  318. {
  319. PluginPackage bundledPluginPkg = GetBundledPluginPackage();
  320. List<PluginPackage> allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
  321. PluginPackage enabledUtilsPluginPkg = null;
  322. foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
  323. {
  324. if (pluginPkg.IsEnabled())
  325. {
  326. if ((enabledUtilsPluginPkg == null) || (pluginPkg.Version > enabledUtilsPluginPkg.Version))
  327. {
  328. enabledUtilsPluginPkg = pluginPkg;
  329. }
  330. }
  331. }
  332. if (enabledUtilsPluginPkg == null)
  333. {
  334. if (EditorUtility.DisplayDialog("Disable Oculus Utilities Plugin", "The OVRPlugin included with Oculus Utilities is already disabled. The OVRPlugin bundled with the Unity Editor will continue to be used.\n\nBundled version: " + bundledPluginPkg.Version, "Ok", ""))
  335. {
  336. return;
  337. }
  338. }
  339. else
  340. {
  341. if (EditorUtility.DisplayDialog("Disable Oculus Utilities Plugin", "Do you want to disable the OVRPlugin included with Oculus Utilities and revert to the OVRPlugin bundled with the Unity Editor?\n\nCurrent version: " + enabledUtilsPluginPkg.Version + "\nBundled version: " + bundledPluginPkg.Version, "Yes", "No"))
  342. {
  343. DisableAllUtilitiesPluginPackages();
  344. if (EditorUtility.DisplayDialog("Restart Unity", "OVRPlugin has been updated to " + bundledPluginPkg.Version + ".\n\nPlease restart the Unity Editor to complete the update process. You may need to manually relaunch Unity if you are using Unity 5.6 and higher.", "Restart", "Not Now"))
  345. {
  346. RestartUnityEditor();
  347. }
  348. }
  349. }
  350. }
  351. [MenuItem("Tools/Oculus/Update OVR Utilities Plugin")]
  352. private static void RunPluginUpdate()
  353. {
  354. AttemptPluginUpdate(false);
  355. }
  356. private static void AttemptPluginUpdate(bool triggeredByAutoUpdate)
  357. {
  358. autoUpdateEnabled = true;
  359. PluginPackage bundledPluginPkg = GetBundledPluginPackage();
  360. List<PluginPackage> allUtilsPluginPkgs = GetAllUtilitiesPluginPackages();
  361. PluginPackage enabledUtilsPluginPkg = null;
  362. PluginPackage newestUtilsPluginPkg = null;
  363. foreach(PluginPackage pluginPkg in allUtilsPluginPkgs)
  364. {
  365. if ((newestUtilsPluginPkg == null) || (pluginPkg.Version > newestUtilsPluginPkg.Version))
  366. {
  367. newestUtilsPluginPkg = pluginPkg;
  368. }
  369. if (pluginPkg.IsEnabled())
  370. {
  371. if ((enabledUtilsPluginPkg == null) || (pluginPkg.Version > enabledUtilsPluginPkg.Version))
  372. {
  373. enabledUtilsPluginPkg = pluginPkg;
  374. }
  375. }
  376. }
  377. PluginPackage targetPluginPkg = null;
  378. if ((newestUtilsPluginPkg != null) && (newestUtilsPluginPkg.Version > bundledPluginPkg.Version))
  379. {
  380. if ((enabledUtilsPluginPkg == null) || (enabledUtilsPluginPkg.Version != newestUtilsPluginPkg.Version))
  381. {
  382. targetPluginPkg = newestUtilsPluginPkg;
  383. }
  384. }
  385. else if ((enabledUtilsPluginPkg != null) && (enabledUtilsPluginPkg.Version < bundledPluginPkg.Version))
  386. {
  387. targetPluginPkg = bundledPluginPkg;
  388. }
  389. System.Version currentVersion = (enabledUtilsPluginPkg != null) ? enabledUtilsPluginPkg.Version : bundledPluginPkg.Version;
  390. if (targetPluginPkg == null)
  391. {
  392. if (!triggeredByAutoUpdate)
  393. {
  394. EditorUtility.DisplayDialog("Update Oculus Utilities Plugin", "OVRPlugin is already up to date.\n\nCurrent version: " + currentVersion + "\nBundled version: " + bundledPluginPkg.Version, "Ok", "");
  395. }
  396. return; // No update necessary.
  397. }
  398. System.Version targetVersion = targetPluginPkg.Version;
  399. int dialogResult = EditorUtility.DisplayDialogComplex("Update Oculus Utilities Plugin", "Oculus Utilities has detected that a newer OVRPlugin is available. Using the newest version is recommended. Do you want to enable it?\n\nCurrent version: " + currentVersion + "\nAvailable version: " + targetVersion, "Yes", "No, Don't Ask Again", "No");
  400. bool userAcceptsUpdate = false;
  401. switch (dialogResult)
  402. {
  403. case 0: // "Yes"
  404. userAcceptsUpdate = true;
  405. break;
  406. case 1: // "No, Don't Ask Again"
  407. autoUpdateEnabled = false;
  408. EditorUtility.DisplayDialog("Oculus Utilities OVRPlugin", "To manually update in the future, use the following menu option:\n\n[Tools -> Oculus -> Update OVR Utilities Plugin]", "Ok", "");
  409. return;
  410. case 2: // "No"
  411. return;
  412. }
  413. if (userAcceptsUpdate)
  414. {
  415. DisableAllUtilitiesPluginPackages();
  416. if (!targetPluginPkg.IsBundledPluginPackage())
  417. {
  418. EnablePluginPackage(targetPluginPkg);
  419. }
  420. if (EditorUtility.DisplayDialog("Restart Unity", "OVRPlugin has been updated to " + targetPluginPkg.Version + ".\n\nPlease restart the Unity Editor to complete the update process. You may need to manually relaunch Unity if you are using Unity 5.6 and higher.", "Restart", "Not Now"))
  421. {
  422. RestartUnityEditor();
  423. }
  424. }
  425. }
  426. private static void RestartUnityEditor()
  427. {
  428. restartPending = true;
  429. EditorApplication.OpenProject(GetCurrentProjectPath());
  430. }
  431. }