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.

1178 lines
54 KiB

  1. // SDK Manager|Utilities|90010
  2. namespace VRTK
  3. {
  4. using UnityEngine;
  5. #if UNITY_2017_2_OR_NEWER
  6. using UnityEngine.XR;
  7. #else
  8. using XRSettings = UnityEngine.VR.VRSettings;
  9. using XRDevice = UnityEngine.VR.VRDevice;
  10. #endif
  11. #if UNITY_EDITOR
  12. using UnityEditor;
  13. using UnityEditor.Callbacks;
  14. using UnityEditorInternal.VR;
  15. #endif
  16. using System;
  17. using System.Collections;
  18. using System.Collections.Generic;
  19. using System.Collections.ObjectModel;
  20. using System.Linq;
  21. using System.Reflection;
  22. /// <summary>
  23. /// The SDK Manager script provides configuration of supported SDKs and manages a list of VRTK_SDKSetups to use.
  24. /// </summary>
  25. public sealed class VRTK_SDKManager : MonoBehaviour
  26. {
  27. /// <summary>
  28. /// A helper class that simply holds references to both the SDK_ScriptingDefineSymbolPredicateAttribute and the method info of the method the attribute is defined on.
  29. /// </summary>
  30. public sealed class ScriptingDefineSymbolPredicateInfo
  31. {
  32. /// <summary>
  33. /// The predicate attribute.
  34. /// </summary>
  35. public readonly SDK_ScriptingDefineSymbolPredicateAttribute attribute;
  36. /// <summary>
  37. /// The method info of the method the attribute is defined on.
  38. /// </summary>
  39. public readonly MethodInfo methodInfo;
  40. /// <summary>
  41. /// Event Payload. Constructs a new instance with the specified predicate attribute and associated method info.
  42. /// </summary>
  43. /// <param name="attribute">The predicate attribute.</param>
  44. /// <param name="methodInfo">The method info of the method the attribute is defined on.</param>
  45. public ScriptingDefineSymbolPredicateInfo(SDK_ScriptingDefineSymbolPredicateAttribute attribute, MethodInfo methodInfo)
  46. {
  47. this.attribute = attribute;
  48. this.methodInfo = methodInfo;
  49. }
  50. }
  51. /// <summary>
  52. /// Event Payload
  53. /// </summary>
  54. /// <param name="previousSetup">The previous loaded Setup. `null` if no previous Setup was loaded.</param>
  55. /// <param name="currentSetup">The current loaded Setup. `null` if no Setup is loaded anymore. See `errorMessage` to check whether this is `null` because of an error.</param>
  56. /// <param name="errorMessage">Explains why loading a list of Setups wasn't successful if `currentSetup` is `null` and an error occurred. `null` if no error occurred.</param>
  57. public struct LoadedSetupChangeEventArgs
  58. {
  59. public readonly VRTK_SDKSetup previousSetup;
  60. public readonly VRTK_SDKSetup currentSetup;
  61. public readonly string errorMessage;
  62. public LoadedSetupChangeEventArgs(VRTK_SDKSetup previousSetup, VRTK_SDKSetup currentSetup, string errorMessage)
  63. {
  64. this.previousSetup = previousSetup;
  65. this.currentSetup = currentSetup;
  66. this.errorMessage = errorMessage;
  67. }
  68. }
  69. /// <summary>
  70. /// Event Payload
  71. /// </summary>
  72. /// <param name="sender">this object</param>
  73. /// <param name="e"><see cref="LoadedSetupChangeEventArgs"/></param>
  74. public delegate void LoadedSetupChangeEventHandler(VRTK_SDKManager sender, LoadedSetupChangeEventArgs e);
  75. /// <summary>
  76. /// All found scripting define symbol predicate attributes with associated method info.
  77. /// </summary>
  78. public static ReadOnlyCollection<ScriptingDefineSymbolPredicateInfo> AvailableScriptingDefineSymbolPredicateInfos { get; private set; }
  79. /// <summary>
  80. /// Specifies the fallback SDK types for every base SDK type.
  81. /// </summary>
  82. public static readonly Dictionary<Type, Type> SDKFallbackTypesByBaseType = new Dictionary<Type, Type>
  83. {
  84. { typeof(SDK_BaseSystem), typeof(SDK_FallbackSystem) },
  85. { typeof(SDK_BaseBoundaries), typeof(SDK_FallbackBoundaries) },
  86. { typeof(SDK_BaseHeadset), typeof(SDK_FallbackHeadset) },
  87. { typeof(SDK_BaseController), typeof(SDK_FallbackController) }
  88. };
  89. /// <summary>
  90. /// All available system SDK infos.
  91. /// </summary>
  92. public static ReadOnlyCollection<VRTK_SDKInfo> AvailableSystemSDKInfos { get; private set; }
  93. /// <summary>
  94. /// All available boundaries SDK infos.
  95. /// </summary>
  96. public static ReadOnlyCollection<VRTK_SDKInfo> AvailableBoundariesSDKInfos { get; private set; }
  97. /// <summary>
  98. /// All available headset SDK infos.
  99. /// </summary>
  100. public static ReadOnlyCollection<VRTK_SDKInfo> AvailableHeadsetSDKInfos { get; private set; }
  101. /// <summary>
  102. /// All available controller SDK infos.
  103. /// </summary>
  104. public static ReadOnlyCollection<VRTK_SDKInfo> AvailableControllerSDKInfos { get; private set; }
  105. /// <summary>
  106. /// All installed system SDK infos. This is a subset of `AvailableSystemSDKInfos`.
  107. /// It contains only those available SDK infos for which an SDK_ScriptingDefineSymbolPredicateAttribute exists that
  108. /// uses the same symbol and whose associated method returns true.
  109. /// </summary>
  110. public static ReadOnlyCollection<VRTK_SDKInfo> InstalledSystemSDKInfos { get; private set; }
  111. /// <summary>
  112. /// All installed boundaries SDK infos. This is a subset of `AvailableBoundariesSDKInfos`.
  113. /// It contains only those available SDK infos for which an SDK_ScriptingDefineSymbolPredicateAttribute exists
  114. /// that uses the same symbol and whose associated method returns true.
  115. /// </summary>
  116. public static ReadOnlyCollection<VRTK_SDKInfo> InstalledBoundariesSDKInfos { get; private set; }
  117. /// <summary>
  118. /// All installed headset SDK infos. This is a subset of `AvailableHeadsetSDKInfos`.
  119. /// It contains only those available SDK infos for which an SDK_ScriptingDefineSymbolPredicateAttribute exists
  120. /// that uses the same symbol and whose associated method returns true.
  121. /// </summary>
  122. public static ReadOnlyCollection<VRTK_SDKInfo> InstalledHeadsetSDKInfos { get; private set; }
  123. /// <summary>
  124. /// All installed controller SDK infos. This is a subset of `AvailableControllerSDKInfos`.
  125. /// It contains only those available SDK infos for which an SDK_ScriptingDefineSymbolPredicateAttribute exists
  126. /// that uses the same symbol and whose associated method returns true.
  127. /// </summary>
  128. public static ReadOnlyCollection<VRTK_SDKInfo> InstalledControllerSDKInfos { get; private set; }
  129. /// <summary>
  130. /// The singleton instance to access the SDK Manager variables from.
  131. /// </summary>
  132. public static VRTK_SDKManager instance
  133. {
  134. get
  135. {
  136. if (_instance == null)
  137. {
  138. VRTK_SDKManager sdkManager = VRTK_SharedMethods.FindEvenInactiveComponent<VRTK_SDKManager>(true);
  139. if (sdkManager != null)
  140. {
  141. sdkManager.CreateInstance();
  142. }
  143. }
  144. return _instance;
  145. }
  146. }
  147. /// <summary>
  148. /// A collection of behaviours to toggle on loaded setup change.
  149. /// </summary>
  150. public static HashSet<Behaviour> delayedToggleBehaviours = new HashSet<Behaviour>();
  151. /// <summary>
  152. /// The ValidInstance method returns whether the SDK Manager isntance is valid (i.e. it's not null).
  153. /// </summary>
  154. /// <returns>Returns `true` if the SDK Manager instance is valid or returns `false` if it is null.</returns>
  155. public static bool ValidInstance()
  156. {
  157. return (instance != null);
  158. }
  159. /// <summary>
  160. /// The AttemptAddBehaviourToToggleOnLoadedSetupChange method will attempt to add the given behaviour to the loaded setup change toggle if the SDK Manager instance exists. If it doesn't exist then it adds it to the `delayedToggleBehaviours` HashSet to be manually added later with the `ProcessDelayedToggleBehaviours` method.
  161. /// </summary>
  162. /// <param name="givenBehaviour">The behaviour to add.</param>
  163. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  164. public static bool AttemptAddBehaviourToToggleOnLoadedSetupChange(Behaviour givenBehaviour)
  165. {
  166. if (ValidInstance())
  167. {
  168. instance.AddBehaviourToToggleOnLoadedSetupChange(givenBehaviour);
  169. return true;
  170. }
  171. delayedToggleBehaviours.Add(givenBehaviour);
  172. return false;
  173. }
  174. /// <summary>
  175. /// The AttemptRemoveBehaviourToToggleOnLoadedSetupChange method will attempt to remove the given behaviour from the loaded setup change toggle if the SDK Manager instance exists.
  176. /// </summary>
  177. /// <param name="givenBehaviour">The behaviour to remove.</param>
  178. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  179. public static bool AttemptRemoveBehaviourToToggleOnLoadedSetupChange(Behaviour givenBehaviour)
  180. {
  181. if (ValidInstance())
  182. {
  183. instance.RemoveBehaviourToToggleOnLoadedSetupChange(givenBehaviour);
  184. delayedToggleBehaviours.Remove(givenBehaviour);
  185. return true;
  186. }
  187. return false;
  188. }
  189. /// <summary>
  190. /// The ProcessDelayedToggleBehaviours method will attempt to addd the behaviours in the `delayedToggleBehaviours` HashSet to the loaded setup change toggle.
  191. /// </summary>
  192. public static void ProcessDelayedToggleBehaviours()
  193. {
  194. if (ValidInstance())
  195. {
  196. foreach (Behaviour currentBehaviour in new HashSet<Behaviour>(delayedToggleBehaviours))
  197. {
  198. instance.AddBehaviourToToggleOnLoadedSetupChange(currentBehaviour);
  199. }
  200. delayedToggleBehaviours.Clear();
  201. }
  202. }
  203. /// <summary>
  204. /// The SubscribeLoadedSetupChanged method attempts to register the given callback with the `LoadedSetupChanged` event.
  205. /// </summary>
  206. /// <param name="callback">The callback to register.</param>
  207. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  208. public static bool SubscribeLoadedSetupChanged(LoadedSetupChangeEventHandler callback)
  209. {
  210. if (ValidInstance())
  211. {
  212. instance.LoadedSetupChanged += callback;
  213. return true;
  214. }
  215. return false;
  216. }
  217. /// <summary>
  218. /// The UnsubscribeLoadedSetupChanged method attempts to unregister the given callback from the `LoadedSetupChanged` event.
  219. /// </summary>
  220. /// <param name="callback">The callback to unregister.</param>
  221. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  222. public static bool UnsubscribeLoadedSetupChanged(LoadedSetupChangeEventHandler callback)
  223. {
  224. if (ValidInstance())
  225. {
  226. instance.LoadedSetupChanged -= callback;
  227. return true;
  228. }
  229. return false;
  230. }
  231. /// <summary>
  232. /// The GetLoadedSDKSetup method returns the current loaded SDK Setup for the SDK Manager instance.
  233. /// </summary>
  234. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  235. public static VRTK_SDKSetup GetLoadedSDKSetup()
  236. {
  237. if (ValidInstance())
  238. {
  239. return instance.loadedSetup;
  240. }
  241. return null;
  242. }
  243. /// <summary>
  244. /// The GetAllSDKSetups method returns all valid SDK Setups attached to the SDK Manager instance.
  245. /// </summary>
  246. /// <returns>An SDKSetup array of all valid SDK Setups for the current SDK Manager instance. If no SDK Manager instance is found then an empty array is returned.</returns>
  247. public static VRTK_SDKSetup[] GetAllSDKSetups()
  248. {
  249. if (ValidInstance())
  250. {
  251. return instance.setups;
  252. }
  253. return new VRTK_SDKSetup[0];
  254. }
  255. /// <summary>
  256. /// The AttemptTryLoadSDKSetup method attempts to load a valid VRTK_SDKSetup from a list if the SDK Manager instance is valid.
  257. /// </summary>
  258. /// <param name="startIndex">The index of the VRTK_SDKSetup to start the loading with.</param>
  259. /// <param name="tryToReinitialize">Whether or not to retry initializing and using the currently set but unusable VR Device.</param>
  260. /// <param name="sdkSetups">The list to try to load a VRTK_SDKSetup from.</param>
  261. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  262. public static bool AttemptTryLoadSDKSetup(int startIndex, bool tryToReinitialize, params VRTK_SDKSetup[] sdkSetups)
  263. {
  264. if (ValidInstance())
  265. {
  266. instance.TryLoadSDKSetup(startIndex, tryToReinitialize, sdkSetups);
  267. return true;
  268. }
  269. return false;
  270. }
  271. /// <summary>
  272. /// The AttemptUnloadSDKSetup method tries to load a valid VRTK_SDKSetup from setups if the SDK Manager instance is valid.
  273. /// </summary>
  274. /// <param name="tryUseLastLoadedSetup">Attempt to use the last loaded setup if it's available.</param>
  275. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  276. public static bool AttemptTryLoadSDKSetupFromList(bool tryUseLastLoadedSetup = true)
  277. {
  278. if (ValidInstance())
  279. {
  280. instance.TryLoadSDKSetupFromList(tryUseLastLoadedSetup);
  281. return true;
  282. }
  283. return false;
  284. }
  285. /// <summary>
  286. /// The AttemptUnloadSDKSetup method attempts to unload the currently loaded VRTK_SDKSetup, if there is one and if the SDK Manager instance is valid.
  287. /// </summary>
  288. /// <param name="disableVR">Whether to disable VR altogether after unloading the SDK Setup.</param>
  289. /// <returns>Returns `true` if the SDK Manager instance was valid.</returns>
  290. public static bool AttemptUnloadSDKSetup(bool disableVR = false)
  291. {
  292. if (ValidInstance())
  293. {
  294. instance.UnloadSDKSetup(disableVR);
  295. return true;
  296. }
  297. return false;
  298. }
  299. private static VRTK_SDKManager _instance;
  300. [Tooltip("Determines whether the scripting define symbols required by the installed SDKs are automatically added to and removed from the player settings.")]
  301. public bool autoManageScriptDefines = true;
  302. /// <summary>
  303. /// The active (i.e. to be added to the PlayerSettings) scripting define symbol predicate attributes that have no associated SDK classes.
  304. /// </summary>
  305. public List<SDK_ScriptingDefineSymbolPredicateAttribute> activeScriptingDefineSymbolsWithoutSDKClasses = new List<SDK_ScriptingDefineSymbolPredicateAttribute>();
  306. [Tooltip("A reference to the GameObject that contains any scripts that apply to the Left Hand Controller.")]
  307. public GameObject scriptAliasLeftController;
  308. [Tooltip("A reference to the GameObject that contains any scripts that apply to the Right Hand Controller.")]
  309. public GameObject scriptAliasRightController;
  310. [Tooltip("Determines whether the VR settings of the Player Settings are automatically adjusted to allow for all the used SDKs in the SDK Setups list below.")]
  311. public bool autoManageVRSettings = true;
  312. [Tooltip("Determines whether the SDK Setups list below is used whenever the SDK Manager is enabled. The first loadable Setup is then loaded.")]
  313. public bool autoLoadSetup = true;
  314. [Tooltip("The list of SDK Setups to choose from.")]
  315. public VRTK_SDKSetup[] setups = new VRTK_SDKSetup[0];
  316. #if UNITY_EDITOR
  317. [Tooltip("The list of Build Target Groups to exclude.")]
  318. public BuildTargetGroup[] excludeTargetGroups = new BuildTargetGroup[] {
  319. #if UNITY_2017_1_OR_NEWER
  320. BuildTargetGroup.Switch,
  321. BuildTargetGroup.Facebook
  322. #endif
  323. };
  324. #endif
  325. [Header("Obsolete Settings")]
  326. [Obsolete("`VRTK_SDKManager.persistOnLoad` has been deprecated and will be removed in a future version of VRTK. See https://github.com/thestonefox/VRTK/issues/1316 for details.")]
  327. [ObsoleteInspector]
  328. public bool persistOnLoad;
  329. /// <summary>
  330. /// The loaded SDK Setup. `null` if no setup is currently loaded.
  331. /// </summary>
  332. public VRTK_SDKSetup loadedSetup
  333. {
  334. get
  335. {
  336. if (_loadedSetup == null && setups.Length == 1 && setups[0].isValid && setups[0].isActiveAndEnabled)
  337. {
  338. _loadedSetup = setups[0];
  339. }
  340. return _loadedSetup;
  341. }
  342. private set { _loadedSetup = value; }
  343. }
  344. private VRTK_SDKSetup _loadedSetup;
  345. private static HashSet<VRTK_SDKInfo> _previouslyUsedSetupInfos = new HashSet<VRTK_SDKInfo>();
  346. /// <summary>
  347. /// All behaviours that need toggling whenever `loadedSetup` changes.
  348. /// </summary>
  349. public ReadOnlyCollection<Behaviour> behavioursToToggleOnLoadedSetupChange { get; private set; }
  350. private List<Behaviour> _behavioursToToggleOnLoadedSetupChange = new List<Behaviour>();
  351. private Dictionary<Behaviour, bool> _behavioursInitialState = new Dictionary<Behaviour, bool>();
  352. private Coroutine checkLeftControllerReadyRoutine = null;
  353. private Coroutine checkRightControllerReadyRoutine = null;
  354. private float checkControllerReadyDelay = 1f;
  355. private int checkControllerValidTimer = 50;
  356. #if UNITY_EDITOR
  357. private BuildTargetGroup[] targetGroupsToExclude;
  358. #endif
  359. /// <summary>
  360. /// The event invoked whenever the loaded SDK Setup changes.
  361. /// </summary>
  362. public event LoadedSetupChangeEventHandler LoadedSetupChanged;
  363. #if UNITY_EDITOR
  364. /// <summary>
  365. /// The ManageScriptingDefineSymbols method manages (i.e. adds and removes) the scripting define symbols of the PlayerSettings for the currently set SDK infos.
  366. /// This method is only available in the editor, so usage of the method needs to be surrounded by `#if UNITY_EDITOR` and `#endif` when used
  367. /// in a type that is also compiled for a standalone build.
  368. /// </summary>
  369. /// <param name="ignoreAutoManageScriptDefines">Whether to ignore `autoManageScriptDefines` while deciding to manage.</param>
  370. /// <param name="ignoreIsActiveAndEnabled">Whether to ignore `Behaviour.isActiveAndEnabled` while deciding to manage.</param>
  371. /// <returns>Whether the PlayerSettings' scripting define symbols were changed.</returns>
  372. public bool ManageScriptingDefineSymbols(bool ignoreAutoManageScriptDefines, bool ignoreIsActiveAndEnabled)
  373. {
  374. if (!((ignoreAutoManageScriptDefines || autoManageScriptDefines) && (ignoreIsActiveAndEnabled || isActiveAndEnabled)))
  375. {
  376. return false;
  377. }
  378. //get valid BuildTargetGroups
  379. BuildTargetGroup[] targetGroups = VRTK_SharedMethods.GetValidBuildTargetGroups();
  380. Dictionary<BuildTargetGroup, HashSet<string>> newSymbolsByTargetGroup = new Dictionary<BuildTargetGroup, HashSet<string>>(targetGroups.Length);
  381. //get current non-removable scripting define symbols
  382. foreach (BuildTargetGroup targetGroup in targetGroups)
  383. {
  384. IEnumerable<string> nonSDKSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup)
  385. .Split(';')
  386. .Where(symbol => !symbol.StartsWith(SDK_ScriptingDefineSymbolPredicateAttribute.RemovableSymbolPrefix, StringComparison.Ordinal));
  387. newSymbolsByTargetGroup[targetGroup] = new HashSet<string>(nonSDKSymbols);
  388. }
  389. VRTK_SDKInfo[] availableSDKInfos = AvailableSystemSDKInfos
  390. .Concat(AvailableBoundariesSDKInfos)
  391. .Concat(AvailableHeadsetSDKInfos)
  392. .Concat(AvailableControllerSDKInfos)
  393. .ToArray();
  394. HashSet<SDK_DescriptionAttribute> descriptions = new HashSet<SDK_DescriptionAttribute>(
  395. availableSDKInfos.Select(info => info.description)
  396. .Where(description => !description.describesFallbackSDK)
  397. );
  398. HashSet<string> activeSymbols = new HashSet<string>(activeScriptingDefineSymbolsWithoutSDKClasses.Select(attribute => attribute.symbol));
  399. //get scripting define symbols and check whether the predicates allow us to add the symbols
  400. foreach (ScriptingDefineSymbolPredicateInfo predicateInfo in AvailableScriptingDefineSymbolPredicateInfos)
  401. {
  402. SDK_ScriptingDefineSymbolPredicateAttribute predicateAttribute = predicateInfo.attribute;
  403. string symbol = predicateAttribute.symbol;
  404. if (!activeSymbols.Contains(symbol)
  405. && !descriptions.Any(description => description.symbol == symbol
  406. && description.buildTargetGroup == predicateAttribute.buildTargetGroup))
  407. {
  408. continue;
  409. }
  410. MethodInfo methodInfo = predicateInfo.methodInfo;
  411. if (!(bool)methodInfo.Invoke(null, null))
  412. {
  413. continue;
  414. }
  415. //add symbols from all predicate attributes on the method since multiple ones are allowed
  416. SDK_ScriptingDefineSymbolPredicateAttribute[] allAttributes = (SDK_ScriptingDefineSymbolPredicateAttribute[])methodInfo.GetCustomAttributes(typeof(SDK_ScriptingDefineSymbolPredicateAttribute), false);
  417. foreach (SDK_ScriptingDefineSymbolPredicateAttribute attribute in allAttributes)
  418. {
  419. BuildTargetGroup buildTargetGroup = attribute.buildTargetGroup;
  420. HashSet<string> newSymbols;
  421. if (!newSymbolsByTargetGroup.TryGetValue(buildTargetGroup, out newSymbols))
  422. {
  423. newSymbols = new HashSet<string>();
  424. newSymbolsByTargetGroup[buildTargetGroup] = newSymbols;
  425. }
  426. newSymbols.Add(attribute.symbol);
  427. }
  428. }
  429. bool changedSymbols = false;
  430. //apply new set of scripting define symbols
  431. foreach (KeyValuePair<BuildTargetGroup, HashSet<string>> keyValuePair in newSymbolsByTargetGroup)
  432. {
  433. BuildTargetGroup targetGroup = keyValuePair.Key;
  434. string[] currentSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(targetGroup)
  435. .Split(';')
  436. .Distinct()
  437. .OrderBy(symbol => symbol, StringComparer.Ordinal)
  438. .ToArray();
  439. string[] newSymbols = keyValuePair.Value.OrderBy(symbol => symbol, StringComparer.Ordinal).ToArray();
  440. if (currentSymbols.SequenceEqual(newSymbols))
  441. {
  442. continue;
  443. }
  444. PlayerSettings.SetScriptingDefineSymbolsForGroup(targetGroup, string.Join(";", newSymbols));
  445. string[] removedSymbols = currentSymbols.Except(newSymbols).ToArray();
  446. if (removedSymbols.Length > 0)
  447. {
  448. VRTK_Logger.Info(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.SCRIPTING_DEFINE_SYMBOLS_REMOVED, targetGroup, string.Join(", ", removedSymbols)));
  449. }
  450. string[] addedSymbols = newSymbols.Except(currentSymbols).ToArray();
  451. if (addedSymbols.Length > 0)
  452. {
  453. VRTK_Logger.Info(VRTK_Logger.GetCommonMessage(VRTK_Logger.CommonMessageKeys.SCRIPTING_DEFINE_SYMBOLS_ADDED, targetGroup, string.Join(", ", addedSymbols)));
  454. }
  455. if (!changedSymbols)
  456. {
  457. changedSymbols = removedSymbols.Length > 0 || addedSymbols.Length > 0;
  458. }
  459. }
  460. return changedSymbols;
  461. }
  462. /// <summary>
  463. /// The ManageVRSettings method manages (i.e. adds and removes) the VR SDKs of the PlayerSettings for the currently set SDK infos.
  464. /// This method is only available in the editor, so usage of the method needs to be surrounded by `#if UNITY_EDITOR` and `#endif` when used
  465. /// in a type that is also compiled for a standalone build.
  466. /// </summary>
  467. /// <param name="force">Whether to ignore `autoManageVRSettings` while deciding to manage.</param>
  468. public void ManageVRSettings(bool force)
  469. {
  470. if (EditorApplication.isPlayingOrWillChangePlaymode || !(force || autoManageVRSettings))
  471. {
  472. return;
  473. }
  474. Dictionary<BuildTargetGroup, string[]> deviceNamesByTargetGroup = setups
  475. .Where(setup => setup != null && setup.isValid)
  476. .SelectMany(setup => new[]
  477. {
  478. setup.systemSDKInfo, setup.boundariesSDKInfo, setup.headsetSDKInfo, setup.controllerSDKInfo
  479. })
  480. .GroupBy(info => info.description.buildTargetGroup)
  481. .ToDictionary(grouping => grouping.Key,
  482. grouping => grouping.Select(info => info.description.vrDeviceName)
  483. .Distinct()
  484. .ToArray());
  485. foreach (BuildTargetGroup targetGroup in VRTK_SharedMethods.GetValidBuildTargetGroups())
  486. {
  487. if (targetGroupsToExclude.Contains(targetGroup))
  488. {
  489. continue;
  490. }
  491. string[] deviceNames;
  492. deviceNamesByTargetGroup.TryGetValue(targetGroup, out deviceNames);
  493. int setupCount = deviceNames == null ? 0 : deviceNames.Length;
  494. bool vrEnabled = deviceNames != null && deviceNames.Length > 0;
  495. if (deviceNames != null)
  496. {
  497. deviceNames = deviceNames.Except(new[] { "None" }).ToArray();
  498. }
  499. #if UNITY_5_5_OR_NEWER
  500. VREditor.SetVREnabledOnTargetGroup(targetGroup, vrEnabled);
  501. #else
  502. VREditor.SetVREnabled(targetGroup, vrEnabled);
  503. #endif
  504. string[] devices;
  505. if (vrEnabled)
  506. {
  507. devices = setupCount > 1
  508. ? new[] { "None" }.Concat(deviceNames).ToArray()
  509. : deviceNames;
  510. }
  511. else
  512. {
  513. devices = new string[0];
  514. }
  515. #if UNITY_5_5_OR_NEWER
  516. VREditor.SetVREnabledDevicesOnTargetGroup(
  517. #else
  518. VREditor.SetVREnabledDevices(
  519. #endif
  520. targetGroup,
  521. devices
  522. );
  523. }
  524. }
  525. #endif
  526. /// <summary>
  527. /// The AddBehaviourToToggleOnLoadedSetupChange method adds a behaviour to the list of behaviours to toggle when `loadedSetup` changes.
  528. /// </summary>
  529. /// <param name="behaviour">The behaviour to add.</param>
  530. public void AddBehaviourToToggleOnLoadedSetupChange(Behaviour behaviour)
  531. {
  532. if (!_behavioursToToggleOnLoadedSetupChange.Contains(behaviour))
  533. {
  534. _behavioursToToggleOnLoadedSetupChange.Add(behaviour);
  535. _behavioursInitialState.Add(behaviour, behaviour.enabled);
  536. }
  537. if (loadedSetup == null && behaviour.enabled)
  538. {
  539. behaviour.enabled = false;
  540. }
  541. }
  542. /// <summary>
  543. /// The RemoveBehaviourToToggleOnLoadedSetupChange method removes a behaviour of the list of behaviours to toggle when `loadedSetup` changes.
  544. /// </summary>
  545. /// <param name="behaviour">The behaviour to remove.</param>
  546. public void RemoveBehaviourToToggleOnLoadedSetupChange(Behaviour behaviour)
  547. {
  548. _behavioursToToggleOnLoadedSetupChange.Remove(behaviour);
  549. }
  550. /// <summary>
  551. /// The TryLoadSDKSetupFromList method tries to load a valid VRTK_SDKSetup from setups.
  552. /// </summary>
  553. /// <param name="tryUseLastLoadedSetup">Attempt to use the last loaded setup if it's available.</param>
  554. public void TryLoadSDKSetupFromList(bool tryUseLastLoadedSetup = true)
  555. {
  556. int index = 0;
  557. if (tryUseLastLoadedSetup && _previouslyUsedSetupInfos.Count > 0)
  558. {
  559. index = Array.FindIndex(
  560. setups,
  561. setup => _previouslyUsedSetupInfos.SetEquals(
  562. new[]
  563. {
  564. setup.systemSDKInfo,
  565. setup.boundariesSDKInfo,
  566. setup.headsetSDKInfo,
  567. setup.controllerSDKInfo
  568. })
  569. );
  570. }
  571. else if (XRSettings.enabled)
  572. {
  573. // Use the SDK Setup for the current VR Device if it's working already
  574. // (may be due to command line argument '-vrmode')
  575. index = Array.FindIndex(
  576. setups,
  577. setup => setup.usedVRDeviceNames.Contains(XRSettings.loadedDeviceName)
  578. );
  579. }
  580. else
  581. {
  582. // If '-vrmode none' was used try to load the respective SDK Setup
  583. string[] commandLineArgs = VRTK_SharedMethods.GetCommandLineArguements();
  584. int commandLineArgIndex = Array.IndexOf(commandLineArgs, "-vrmode", 1);
  585. if (XRSettings.loadedDeviceName == "None"
  586. || (commandLineArgIndex != -1
  587. && commandLineArgIndex + 1 < commandLineArgs.Length
  588. && commandLineArgs[commandLineArgIndex + 1].ToLowerInvariant() == "none"))
  589. {
  590. index = Array.FindIndex(
  591. setups,
  592. setup => setup.usedVRDeviceNames.All(vrDeviceName => vrDeviceName == "None")
  593. );
  594. }
  595. }
  596. index = index == -1 ? 0 : index;
  597. TryLoadSDKSetup(index, false, setups.ToArray());
  598. }
  599. /// <summary>
  600. /// The TryLoadSDKSetup method tries to load a valid VRTK_SDKSetup from a list.
  601. /// </summary>
  602. /// <remarks>
  603. /// The first loadable VRTK_SDKSetup in the list will be loaded. Will fall back to disable VR if none of the provided Setups is useable.
  604. /// </remarks>
  605. /// <param name="startIndex">The index of the VRTK_SDKSetup to start the loading with.</param>
  606. /// <param name="tryToReinitialize">Whether or not to retry initializing and using the currently set but unusable VR Device.</param>
  607. /// <param name="sdkSetups">The list to try to load a VRTK_SDKSetup from.</param>
  608. public void TryLoadSDKSetup(int startIndex, bool tryToReinitialize, params VRTK_SDKSetup[] sdkSetups)
  609. {
  610. if (sdkSetups.Length == 0)
  611. {
  612. return;
  613. }
  614. if (startIndex < 0 || startIndex >= sdkSetups.Length)
  615. {
  616. VRTK_Logger.Fatal(new ArgumentOutOfRangeException("startIndex"));
  617. return;
  618. }
  619. sdkSetups = sdkSetups.ToList()
  620. .GetRange(startIndex, sdkSetups.Length - startIndex)
  621. .ToArray();
  622. foreach (VRTK_SDKSetup invalidSetup in sdkSetups.Where(setup => !setup.isValid))
  623. {
  624. string setupErrorDescriptions = string.Join("\n- ", invalidSetup.GetSimplifiedErrorDescriptions());
  625. if (!string.IsNullOrEmpty(setupErrorDescriptions))
  626. {
  627. setupErrorDescriptions = "- " + setupErrorDescriptions;
  628. VRTK_Logger.Warn(string.Format("Ignoring SDK Setup '{0}' because there are some errors with it:\n{1}", invalidSetup.name, setupErrorDescriptions));
  629. }
  630. }
  631. sdkSetups = sdkSetups.Where(setup => setup.isValid).ToArray();
  632. VRTK_SDKSetup previousLoadedSetup = loadedSetup;
  633. ToggleBehaviours(false);
  634. loadedSetup = null;
  635. if (previousLoadedSetup != null)
  636. {
  637. previousLoadedSetup.OnUnloaded(this);
  638. }
  639. string loadedDeviceName = string.IsNullOrEmpty(XRSettings.loadedDeviceName) ? "None" : XRSettings.loadedDeviceName;
  640. bool isDeviceAlreadyLoaded = sdkSetups[0].usedVRDeviceNames.Contains(loadedDeviceName);
  641. if (!isDeviceAlreadyLoaded)
  642. {
  643. if (!tryToReinitialize && !XRSettings.enabled && loadedDeviceName != "None")
  644. {
  645. sdkSetups = sdkSetups.Where(setup => !setup.usedVRDeviceNames.Contains(loadedDeviceName))
  646. .ToArray();
  647. }
  648. VRTK_SDKSetup[] missingVRDeviceSetups = sdkSetups
  649. .Where(setup => setup.usedVRDeviceNames.Except(XRSettings.supportedDevices.Concat(new[] { "None" })).Any())
  650. .ToArray();
  651. foreach (VRTK_SDKSetup missingVRDeviceSetup in missingVRDeviceSetups)
  652. {
  653. string missingVRDevicesText = string.Join(
  654. ", ",
  655. missingVRDeviceSetup.usedVRDeviceNames
  656. .Except(XRSettings.supportedDevices)
  657. .ToArray()
  658. );
  659. VRTK_Logger.Warn(string.Format("Ignoring SDK Setup '{0}' because the following VR device names are missing from the PlayerSettings:\n{1}",
  660. missingVRDeviceSetup.name,
  661. missingVRDevicesText));
  662. }
  663. sdkSetups = sdkSetups.Except(missingVRDeviceSetups).ToArray();
  664. string[] vrDeviceNames = sdkSetups
  665. .SelectMany(setup => setup.usedVRDeviceNames)
  666. .Distinct()
  667. .Concat(new[] { "None" }) // Add "None" to the end to fall back to
  668. .ToArray();
  669. XRSettings.LoadDeviceByName(vrDeviceNames);
  670. }
  671. StartCoroutine(FinishSDKSetupLoading(sdkSetups, previousLoadedSetup));
  672. }
  673. #if UNITY_EDITOR
  674. /// <summary>
  675. /// The SetLoadedSDKSetupToPopulateObjectReferences method sets a given VRTK_SDKSetup as the loaded SDK Setup to be able to use it when populating object references in the SDK Setup.
  676. /// </summary>
  677. /// <remarks>
  678. /// This method should only be called when not playing as it's only for populating the object references.
  679. /// This method is only available in the editor, so usage of the method needs to be surrounded by `#if UNITY_EDITOR` and `#endif` when used
  680. /// in a type that is also compiled for a standalone build.
  681. /// </remarks>
  682. /// <param name="setup">The SDK Setup to set as the loaded SDK.</param>
  683. public void SetLoadedSDKSetupToPopulateObjectReferences(VRTK_SDKSetup setup)
  684. {
  685. if (EditorApplication.isPlaying)
  686. {
  687. VRTK_Logger.Fatal("The method SetLoadedSDKSetupToPopulateObjectReferences should not be used when the application is playing.");
  688. return;
  689. }
  690. loadedSetup = setup;
  691. }
  692. #endif
  693. /// <summary>
  694. /// The UnloadSDKSetup method unloads the currently loaded VRTK_SDKSetup, if there is one.
  695. /// </summary>
  696. /// <param name="disableVR">Whether to disable VR altogether after unloading the SDK Setup.</param>
  697. public void UnloadSDKSetup(bool disableVR = false)
  698. {
  699. if (loadedSetup != null)
  700. {
  701. ToggleBehaviours(false);
  702. }
  703. VRTK_SDKSetup previousLoadedSetup = loadedSetup;
  704. loadedSetup = null;
  705. if (previousLoadedSetup != null)
  706. {
  707. previousLoadedSetup.OnUnloaded(this);
  708. }
  709. if (disableVR)
  710. {
  711. XRSettings.LoadDeviceByName("None");
  712. XRSettings.enabled = false;
  713. }
  714. if (previousLoadedSetup != null)
  715. {
  716. OnLoadedSetupChanged(new LoadedSetupChangeEventArgs(previousLoadedSetup, null, null));
  717. }
  718. _previouslyUsedSetupInfos.Clear();
  719. if (previousLoadedSetup != null)
  720. {
  721. _previouslyUsedSetupInfos.UnionWith(
  722. new[]
  723. {
  724. previousLoadedSetup.systemSDKInfo,
  725. previousLoadedSetup.boundariesSDKInfo,
  726. previousLoadedSetup.headsetSDKInfo,
  727. previousLoadedSetup.controllerSDKInfo
  728. }
  729. );
  730. }
  731. }
  732. static VRTK_SDKManager()
  733. {
  734. PopulateAvailableScriptingDefineSymbolPredicateInfos();
  735. PopulateAvailableAndInstalledSDKInfos();
  736. #if UNITY_EDITOR
  737. //call AutoManageScriptingDefineSymbolsAndManageVRSettings when the currently active scene changes
  738. #if UNITY_2018_1_OR_NEWER
  739. EditorApplication.hierarchyChanged += AutoManageScriptingDefineSymbolsAndManageVRSettings;
  740. #else
  741. EditorApplication.hierarchyWindowChanged += AutoManageScriptingDefineSymbolsAndManageVRSettings;
  742. #endif
  743. #endif
  744. }
  745. private void OnEnable()
  746. {
  747. behavioursToToggleOnLoadedSetupChange = _behavioursToToggleOnLoadedSetupChange.AsReadOnly();
  748. CreateInstance();
  749. if (loadedSetup == null && autoLoadSetup)
  750. {
  751. TryLoadSDKSetupFromList();
  752. }
  753. }
  754. private void OnDisable()
  755. {
  756. if (checkLeftControllerReadyRoutine != null)
  757. {
  758. StopCoroutine(checkLeftControllerReadyRoutine);
  759. }
  760. if (checkRightControllerReadyRoutine != null)
  761. {
  762. StopCoroutine(checkRightControllerReadyRoutine);
  763. }
  764. #pragma warning disable 618
  765. if (_instance == this && !persistOnLoad)
  766. #pragma warning restore 618
  767. {
  768. UnloadSDKSetup();
  769. }
  770. }
  771. private void CreateInstance()
  772. {
  773. if (_instance == null)
  774. {
  775. _instance = this;
  776. VRTK_SDK_Bridge.InvalidateCaches();
  777. #pragma warning disable 618
  778. if (persistOnLoad && Application.isPlaying)
  779. #pragma warning restore 618
  780. {
  781. DontDestroyOnLoad(gameObject);
  782. }
  783. }
  784. else if (_instance != this)
  785. {
  786. Destroy(gameObject);
  787. }
  788. }
  789. private void OnLoadedSetupChanged(LoadedSetupChangeEventArgs e)
  790. {
  791. LoadedSetupChangeEventHandler handler = LoadedSetupChanged;
  792. if (handler != null)
  793. {
  794. handler(this, e);
  795. }
  796. }
  797. private IEnumerator FinishSDKSetupLoading(VRTK_SDKSetup[] sdkSetups, VRTK_SDKSetup previousLoadedSetup)
  798. {
  799. yield return null;
  800. string loadedDeviceName = string.IsNullOrEmpty(XRSettings.loadedDeviceName) ? "None" : XRSettings.loadedDeviceName;
  801. loadedSetup = sdkSetups.FirstOrDefault(setup => setup.usedVRDeviceNames.Contains(loadedDeviceName));
  802. if (loadedSetup == null)
  803. {
  804. // The loaded VR Device doesn't match any SDK Setup
  805. UnloadSDKSetup();
  806. const string errorMessage = "No SDK Setup from the provided list could be loaded.";
  807. OnLoadedSetupChanged(new LoadedSetupChangeEventArgs(previousLoadedSetup, null, errorMessage));
  808. VRTK_Logger.Error(errorMessage);
  809. yield break;
  810. }
  811. if (loadedSetup.usedVRDeviceNames.Except(new[] { "None" }).Any())
  812. {
  813. // The loaded VR Device is actually a VR Device
  814. XRSettings.enabled = true;
  815. if (!XRDevice.isPresent)
  816. {
  817. // Despite being loaded, the loaded VR Device isn't working correctly
  818. int nextSetupIndex = Array.IndexOf(sdkSetups, loadedSetup) + 1;
  819. string errorMessage = "An SDK Setup from the provided list could be loaded, but the device is not in working order.";
  820. ToggleBehaviours(false);
  821. loadedSetup = null;
  822. if (nextSetupIndex < sdkSetups.Length && sdkSetups.Length - nextSetupIndex > 0)
  823. {
  824. // Let's try loading the remaining SDK Setups
  825. errorMessage += " Now retrying with the remaining SDK Setups from the provided list...";
  826. VRTK_Logger.Warn(errorMessage);
  827. OnLoadedSetupChanged(new LoadedSetupChangeEventArgs(previousLoadedSetup, null, errorMessage));
  828. TryLoadSDKSetup(nextSetupIndex, false, sdkSetups);
  829. yield break;
  830. }
  831. // There are no other SDK Setups
  832. UnloadSDKSetup();
  833. errorMessage += " There are no other Setups in the provided list to try.";
  834. OnLoadedSetupChanged(new LoadedSetupChangeEventArgs(previousLoadedSetup, null, errorMessage));
  835. VRTK_Logger.Error(errorMessage);
  836. yield break;
  837. }
  838. }
  839. // A VR Device was correctly loaded, is working and matches an SDK Setup
  840. loadedSetup.OnLoaded(this);
  841. ToggleBehaviours(true);
  842. CheckControllersReady();
  843. OnLoadedSetupChanged(new LoadedSetupChangeEventArgs(previousLoadedSetup, loadedSetup, null));
  844. }
  845. private void CheckControllersReady()
  846. {
  847. if (checkLeftControllerReadyRoutine != null)
  848. {
  849. StopCoroutine(checkLeftControllerReadyRoutine);
  850. }
  851. checkLeftControllerReadyRoutine = StartCoroutine(CheckLeftControllerReady());
  852. if (checkRightControllerReadyRoutine != null)
  853. {
  854. StopCoroutine(checkRightControllerReadyRoutine);
  855. }
  856. checkRightControllerReadyRoutine = StartCoroutine(CheckRightControllerReady());
  857. }
  858. private IEnumerator CheckLeftControllerReady()
  859. {
  860. WaitForSeconds delayInstruction = new WaitForSeconds(checkControllerReadyDelay);
  861. int maxCheckTime = checkControllerValidTimer;
  862. while (true)
  863. {
  864. if (loadedSetup != null && loadedSetup.actualLeftController != null && loadedSetup.actualLeftController.activeInHierarchy && (loadedSetup.controllerSDK.GetCurrentControllerType() != SDK_BaseController.ControllerType.Undefined || maxCheckTime < 0))
  865. {
  866. break;
  867. }
  868. maxCheckTime--;
  869. yield return delayInstruction;
  870. }
  871. loadedSetup.controllerSDK.OnControllerReady(SDK_BaseController.ControllerHand.Left);
  872. }
  873. private IEnumerator CheckRightControllerReady()
  874. {
  875. WaitForSeconds delayInstruction = new WaitForSeconds(checkControllerReadyDelay);
  876. int maxCheckTime = checkControllerValidTimer;
  877. while (true)
  878. {
  879. if (loadedSetup != null && loadedSetup.actualRightController != null && loadedSetup.actualRightController.activeInHierarchy && (loadedSetup.controllerSDK.GetCurrentControllerType() != SDK_BaseController.ControllerType.Undefined || maxCheckTime < 0))
  880. {
  881. break;
  882. }
  883. maxCheckTime--;
  884. yield return delayInstruction;
  885. }
  886. loadedSetup.controllerSDK.OnControllerReady(SDK_BaseController.ControllerHand.Right);
  887. }
  888. private void ToggleBehaviours(bool state)
  889. {
  890. List<Behaviour> listCopy = _behavioursToToggleOnLoadedSetupChange.ToList();
  891. if (!state)
  892. {
  893. listCopy.Reverse();
  894. }
  895. for (int index = 0; index < listCopy.Count; index++)
  896. {
  897. Behaviour behaviour = listCopy[index];
  898. if (behaviour == null)
  899. {
  900. VRTK_Logger.Error(string.Format("A behaviour to toggle has been destroyed. Have you forgot the corresponding call `VRTK_SDKManager.instance.RemoveBehaviourToToggleOnLoadedSetupChange(this)` in the `OnDestroy` method of `{0}`?", behaviour.GetType()));
  901. _behavioursToToggleOnLoadedSetupChange.RemoveAt(state ? index : _behavioursToToggleOnLoadedSetupChange.Count - 1 - index);
  902. continue;
  903. }
  904. behaviour.enabled = (state && _behavioursInitialState.ContainsKey(behaviour) ? _behavioursInitialState[behaviour] : state);
  905. }
  906. }
  907. /// <summary>
  908. /// Populates `AvailableScriptingDefineSymbolPredicateInfos` with all the available SDK_ScriptingDefineSymbolPredicateAttributes and associated method infos.
  909. /// </summary>
  910. private static void PopulateAvailableScriptingDefineSymbolPredicateInfos()
  911. {
  912. List<ScriptingDefineSymbolPredicateInfo> predicateInfos = new List<ScriptingDefineSymbolPredicateInfo>();
  913. foreach (Type type in VRTK_SharedMethods.GetTypesOfType(typeof(VRTK_SDKManager)))
  914. {
  915. for (int index = 0; index < type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Length; index++)
  916. {
  917. MethodInfo methodInfo = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)[index];
  918. SDK_ScriptingDefineSymbolPredicateAttribute[] predicateAttributes = (SDK_ScriptingDefineSymbolPredicateAttribute[])methodInfo.GetCustomAttributes(typeof(SDK_ScriptingDefineSymbolPredicateAttribute), false);
  919. if (predicateAttributes.Length == 0)
  920. {
  921. continue;
  922. }
  923. if (methodInfo.ReturnType != typeof(bool) || methodInfo.GetParameters().Length != 0)
  924. {
  925. VRTK_Logger.Fatal(new InvalidOperationException(string.Format("The method '{0}' on '{1}' has '{2}' specified but its signature is wrong. The method must take no arguments and return bool.",
  926. methodInfo.Name,
  927. type,
  928. typeof(SDK_ScriptingDefineSymbolPredicateAttribute))));
  929. return;
  930. }
  931. predicateInfos.AddRange(predicateAttributes.Select(predicateAttribute => new ScriptingDefineSymbolPredicateInfo(predicateAttribute, methodInfo)));
  932. }
  933. }
  934. predicateInfos.Sort((x, y) => string.Compare(x.attribute.symbol, y.attribute.symbol, StringComparison.Ordinal));
  935. AvailableScriptingDefineSymbolPredicateInfos = predicateInfos.AsReadOnly();
  936. }
  937. /// <summary>
  938. /// Populates the various lists of available and installed SDK infos.
  939. /// </summary>
  940. private static void PopulateAvailableAndInstalledSDKInfos()
  941. {
  942. List<string> symbolsOfInstalledSDKs = AvailableScriptingDefineSymbolPredicateInfos
  943. .Where(predicateInfo => (bool)predicateInfo.methodInfo.Invoke(null, null))
  944. .Select(predicateInfo => predicateInfo.attribute.symbol)
  945. .ToList();
  946. List<VRTK_SDKInfo> availableSystemSDKInfos = new List<VRTK_SDKInfo>();
  947. List<VRTK_SDKInfo> availableBoundariesSDKInfos = new List<VRTK_SDKInfo>();
  948. List<VRTK_SDKInfo> availableHeadsetSDKInfos = new List<VRTK_SDKInfo>();
  949. List<VRTK_SDKInfo> availableControllerSDKInfos = new List<VRTK_SDKInfo>();
  950. List<VRTK_SDKInfo> installedSystemSDKInfos = new List<VRTK_SDKInfo>();
  951. List<VRTK_SDKInfo> installedBoundariesSDKInfos = new List<VRTK_SDKInfo>();
  952. List<VRTK_SDKInfo> installedHeadsetSDKInfos = new List<VRTK_SDKInfo>();
  953. List<VRTK_SDKInfo> installedControllerSDKInfos = new List<VRTK_SDKInfo>();
  954. PopulateAvailableAndInstalledSDKInfos<SDK_BaseSystem, SDK_FallbackSystem>(availableSystemSDKInfos, installedSystemSDKInfos, symbolsOfInstalledSDKs);
  955. PopulateAvailableAndInstalledSDKInfos<SDK_BaseBoundaries, SDK_FallbackBoundaries>(availableBoundariesSDKInfos, installedBoundariesSDKInfos, symbolsOfInstalledSDKs);
  956. PopulateAvailableAndInstalledSDKInfos<SDK_BaseHeadset, SDK_FallbackHeadset>(availableHeadsetSDKInfos, installedHeadsetSDKInfos, symbolsOfInstalledSDKs);
  957. PopulateAvailableAndInstalledSDKInfos<SDK_BaseController, SDK_FallbackController>(availableControllerSDKInfos, installedControllerSDKInfos, symbolsOfInstalledSDKs);
  958. AvailableSystemSDKInfos = availableSystemSDKInfos.AsReadOnly();
  959. AvailableBoundariesSDKInfos = availableBoundariesSDKInfos.AsReadOnly();
  960. AvailableHeadsetSDKInfos = availableHeadsetSDKInfos.AsReadOnly();
  961. AvailableControllerSDKInfos = availableControllerSDKInfos.AsReadOnly();
  962. InstalledSystemSDKInfos = installedSystemSDKInfos.AsReadOnly();
  963. InstalledBoundariesSDKInfos = installedBoundariesSDKInfos.AsReadOnly();
  964. InstalledHeadsetSDKInfos = installedHeadsetSDKInfos.AsReadOnly();
  965. InstalledControllerSDKInfos = installedControllerSDKInfos.AsReadOnly();
  966. }
  967. /// <summary>
  968. /// Populates the lists of available and installed SDK infos for a specific SDK base type.
  969. /// </summary>
  970. /// <typeparam name="BaseType">The SDK base type of which to populate the lists for. Must be a subclass of `SDK_Base`.</typeparam>
  971. /// <typeparam name="FallbackType">The SDK type to fall back on if problems occur. Must be a subclass of `BaseType`.</typeparam>
  972. /// <param name="availableSDKInfos">The list of available SDK infos to populate.</param>
  973. /// <param name="installedSDKInfos">The list of installed SDK infos to populate.</param>
  974. /// <param name="symbolsOfInstalledSDKs">The list of symbols of all the installed SDKs.</param>
  975. private static void PopulateAvailableAndInstalledSDKInfos<BaseType, FallbackType>(List<VRTK_SDKInfo> availableSDKInfos,
  976. List<VRTK_SDKInfo> installedSDKInfos,
  977. ICollection<string> symbolsOfInstalledSDKs)
  978. where BaseType : SDK_Base where FallbackType : BaseType
  979. {
  980. Type baseType = typeof(BaseType);
  981. Type fallbackType = SDKFallbackTypesByBaseType[baseType];
  982. availableSDKInfos.AddRange(VRTK_SDKInfo.Create<BaseType, FallbackType, FallbackType>());
  983. availableSDKInfos.AddRange(VRTK_SharedMethods.GetExportedTypesOfType(baseType)
  984. .Where(type => VRTK_SharedMethods.IsTypeSubclassOf(type, baseType) && type != fallbackType && !VRTK_SharedMethods.IsTypeAbstract(type))
  985. .SelectMany(VRTK_SDKInfo.Create<BaseType, FallbackType>));
  986. availableSDKInfos.Sort((x, y) => x.description.describesFallbackSDK
  987. ? -1 //the fallback SDK should always be the first SDK in the list
  988. : string.Compare(x.description.prettyName, y.description.prettyName, StringComparison.Ordinal));
  989. installedSDKInfos.AddRange(availableSDKInfos.Where(info =>
  990. {
  991. string symbol = info.description.symbol;
  992. return string.IsNullOrEmpty(symbol) || symbolsOfInstalledSDKs.Contains(symbol);
  993. }));
  994. }
  995. #if UNITY_EDITOR
  996. /// <summary>
  997. /// Calls `ManageScriptingDefineSymbols` and `ManageVRSettings` (both without forcing) at the appropriate times when in the editor.
  998. /// </summary>
  999. [DidReloadScripts(1)]
  1000. private static void AutoManageScriptingDefineSymbolsAndManageVRSettings()
  1001. {
  1002. if (EditorApplication.isPlayingOrWillChangePlaymode)
  1003. {
  1004. return;
  1005. }
  1006. RemoveLegacyScriptingDefineSymbols();
  1007. if (instance != null && !instance.ManageScriptingDefineSymbols(false, false))
  1008. {
  1009. instance.targetGroupsToExclude = instance.excludeTargetGroups;
  1010. instance.ManageVRSettings(false);
  1011. }
  1012. }
  1013. /// <summary>
  1014. /// Removes scripting define symbols used by previous VRTK versions.
  1015. /// </summary>
  1016. private static void RemoveLegacyScriptingDefineSymbols()
  1017. {
  1018. string[] currentSymbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone)
  1019. .Split(';')
  1020. .Distinct()
  1021. .OrderBy(symbol => symbol, StringComparer.Ordinal)
  1022. .ToArray();
  1023. string[] newSymbols = currentSymbols.Where(symbol => !symbol.StartsWith("VRTK_SDK_", StringComparison.Ordinal)).ToArray();
  1024. if (currentSymbols.SequenceEqual(newSymbols))
  1025. {
  1026. return;
  1027. }
  1028. PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, string.Join(";", newSymbols));
  1029. string[] removedSymbols = currentSymbols.Except(newSymbols).ToArray();
  1030. if (removedSymbols.Length > 0)
  1031. {
  1032. VRTK_Logger.Info(string.Format("Legacy (i.e. used by previous VRTK versions only) Scripting Define Symbols removed from [Project Settings->Player] for {0}: {1}",
  1033. BuildTargetGroup.Standalone,
  1034. string.Join(", ", removedSymbols)));
  1035. }
  1036. }
  1037. #endif
  1038. }
  1039. }