AstarUpdateChecker.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. using UnityEngine;
  2. using UnityEditor;
  3. #if UNITY_2018_1_OR_NEWER
  4. using UnityEngine.Networking;
  5. #endif
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. namespace Pathfinding {
  9. /// <summary>Handles update checking for the A* Pathfinding Project</summary>
  10. [InitializeOnLoad]
  11. public static class AstarUpdateChecker {
  12. #if UNITY_2018_1_OR_NEWER
  13. /// <summary>Used for downloading new version information</summary>
  14. static UnityWebRequest updateCheckDownload;
  15. #else
  16. /// <summary>Used for downloading new version information</summary>
  17. static WWW updateCheckDownload;
  18. #endif
  19. static System.DateTime _lastUpdateCheck;
  20. static bool _lastUpdateCheckRead;
  21. static System.Version _latestVersion;
  22. static System.Version _latestBetaVersion;
  23. /// <summary>Description of the latest update of the A* Pathfinding Project</summary>
  24. static string _latestVersionDescription;
  25. static bool hasParsedServerMessage;
  26. /// <summary>Number of days between update checks</summary>
  27. const double updateCheckRate = 1F;
  28. /// <summary>URL to the version file containing the latest version number.</summary>
  29. const string updateURL = "http://www.arongranberg.com/astar/version.php";
  30. /// <summary>Last time an update check was made</summary>
  31. public static System.DateTime lastUpdateCheck {
  32. get {
  33. try {
  34. // Reading from EditorPrefs is relatively slow, avoid it
  35. if (_lastUpdateCheckRead) return _lastUpdateCheck;
  36. _lastUpdateCheck = System.DateTime.Parse(EditorPrefs.GetString("AstarLastUpdateCheck", "1/1/1971 00:00:01"), System.Globalization.CultureInfo.InvariantCulture);
  37. _lastUpdateCheckRead = true;
  38. }
  39. catch (System.FormatException) {
  40. lastUpdateCheck = System.DateTime.UtcNow;
  41. Debug.LogWarning("Invalid DateTime string encountered when loading from preferences");
  42. }
  43. return _lastUpdateCheck;
  44. }
  45. private set {
  46. _lastUpdateCheck = value;
  47. EditorPrefs.SetString("AstarLastUpdateCheck", _lastUpdateCheck.ToString(System.Globalization.CultureInfo.InvariantCulture));
  48. }
  49. }
  50. /// <summary>Latest version of the A* Pathfinding Project</summary>
  51. public static System.Version latestVersion {
  52. get {
  53. RefreshServerMessage();
  54. return _latestVersion ?? AstarPath.Version;
  55. }
  56. private set {
  57. _latestVersion = value;
  58. }
  59. }
  60. /// <summary>Latest beta version of the A* Pathfinding Project</summary>
  61. public static System.Version latestBetaVersion {
  62. get {
  63. RefreshServerMessage();
  64. return _latestBetaVersion ?? AstarPath.Version;
  65. }
  66. private set {
  67. _latestBetaVersion = value;
  68. }
  69. }
  70. /// <summary>Summary of the latest update</summary>
  71. public static string latestVersionDescription {
  72. get {
  73. RefreshServerMessage();
  74. return _latestVersionDescription ?? "";
  75. }
  76. private set {
  77. _latestVersionDescription = value;
  78. }
  79. }
  80. /// <summary>
  81. /// Holds various URLs and text for the editor.
  82. /// This info can be updated when a check for new versions is done to ensure that there are no invalid links.
  83. /// </summary>
  84. static Dictionary<string, string> astarServerData = new Dictionary<string, string> {
  85. { "URL:modifiers", "http://www.arongranberg.com/astar/docs/modifiers.php" },
  86. { "URL:astarpro", "http://arongranberg.com/unity/a-pathfinding/astarpro/" },
  87. { "URL:documentation", "http://arongranberg.com/astar/docs/" },
  88. { "URL:findoutmore", "http://arongranberg.com/astar" },
  89. { "URL:download", "http://arongranberg.com/unity/a-pathfinding/download" },
  90. { "URL:changelog", "http://arongranberg.com/astar/docs/changelog.php" },
  91. { "URL:tags", "http://arongranberg.com/astar/docs/tags.php" },
  92. { "URL:homepage", "http://arongranberg.com/astar/" }
  93. };
  94. static AstarUpdateChecker() {
  95. // Add a callback so that we can parse the message when it has been downloaded
  96. EditorApplication.update += UpdateCheckLoop;
  97. EditorBase.getDocumentationURL = () => GetURL("documentation");
  98. }
  99. static void RefreshServerMessage () {
  100. if (!hasParsedServerMessage) {
  101. var serverMessage = EditorPrefs.GetString("AstarServerMessage");
  102. if (!string.IsNullOrEmpty(serverMessage)) {
  103. ParseServerMessage(serverMessage);
  104. ShowUpdateWindowIfRelevant();
  105. }
  106. }
  107. }
  108. public static string GetURL (string tag) {
  109. RefreshServerMessage();
  110. string url;
  111. astarServerData.TryGetValue("URL:"+tag, out url);
  112. return url ?? "";
  113. }
  114. /// <summary>Initiate a check for updates now, regardless of when the last check was done</summary>
  115. public static void CheckForUpdatesNow () {
  116. lastUpdateCheck = System.DateTime.UtcNow.AddDays(-5);
  117. // Remove the callback if it already exists
  118. EditorApplication.update -= UpdateCheckLoop;
  119. // Add a callback so that we can parse the message when it has been downloaded
  120. EditorApplication.update += UpdateCheckLoop;
  121. }
  122. /// <summary>
  123. /// Checking for updates...
  124. /// Should be called from EditorApplication.update
  125. /// </summary>
  126. static void UpdateCheckLoop () {
  127. // Go on until the update check has been completed
  128. if (!CheckForUpdates()) {
  129. EditorApplication.update -= UpdateCheckLoop;
  130. }
  131. }
  132. /// <summary>
  133. /// Checks for updates if there was some time since last check.
  134. /// It must be called repeatedly to ensure that the result is processed.
  135. /// Returns: True if an update check is progressing (WWW request)
  136. /// </summary>
  137. static bool CheckForUpdates () {
  138. if (updateCheckDownload != null && updateCheckDownload.isDone) {
  139. if (!string.IsNullOrEmpty(updateCheckDownload.error)) {
  140. Debug.LogWarning("There was an error checking for updates to the A* Pathfinding Project\n" +
  141. "The error might disappear if you switch build target from Webplayer to Standalone because of the webplayer security emulation\nError: " +
  142. updateCheckDownload.error);
  143. updateCheckDownload = null;
  144. return false;
  145. }
  146. #if UNITY_2018_1_OR_NEWER
  147. UpdateCheckCompleted(updateCheckDownload.downloadHandler.text);
  148. updateCheckDownload.Dispose();
  149. #else
  150. UpdateCheckCompleted(updateCheckDownload.text);
  151. #endif
  152. updateCheckDownload = null;
  153. }
  154. // Check if it is time to check for updates
  155. // Check for updates a bit earlier if we are in play mode or have the AstarPath object in the scene
  156. // as then the collected statistics will be a bit more accurate
  157. var offsetMinutes = (Application.isPlaying && Time.time > 60) || AstarPath.active != null ? -20 : 20;
  158. var minutesUntilUpdate = lastUpdateCheck.AddDays(updateCheckRate).AddMinutes(offsetMinutes).Subtract(System.DateTime.UtcNow).TotalMinutes;
  159. if (minutesUntilUpdate < 0) {
  160. DownloadVersionInfo();
  161. }
  162. return updateCheckDownload != null || minutesUntilUpdate < 10;
  163. }
  164. static void DownloadVersionInfo () {
  165. var script = AstarPath.active != null ? AstarPath.active : GameObject.FindObjectOfType(typeof(AstarPath)) as AstarPath;
  166. if (script != null) {
  167. script.ConfigureReferencesInternal();
  168. if ((!Application.isPlaying && (script.data.graphs == null || script.data.graphs.Length == 0)) || script.data.graphs == null) {
  169. script.data.DeserializeGraphs();
  170. }
  171. }
  172. bool mecanim = GameObject.FindObjectOfType(typeof(Animator)) != null;
  173. string query = updateURL+
  174. "?v="+AstarPath.Version+
  175. "&pro=0"+
  176. "&check="+updateCheckRate+"&distr="+AstarPath.Distribution+
  177. "&unitypro="+(Application.HasProLicense() ? "1" : "0")+
  178. "&inscene="+(script != null ? "1" : "0")+
  179. "&targetplatform="+EditorUserBuildSettings.activeBuildTarget+
  180. "&devplatform="+Application.platform+
  181. "&mecanim="+(mecanim ? "1" : "0")+
  182. "&hasNavmesh=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "NavMeshGraph") ? 1 : 0) +
  183. "&hasPoint=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "PointGraph") ? 1 : 0) +
  184. "&hasGrid=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "GridGraph") ? 1 : 0) +
  185. "&hasLayered=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "LayerGridGraph") ? 1 : 0) +
  186. "&hasRecast=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "RecastGraph") ? 1 : 0) +
  187. "&hasGrid=" + (script != null && script.data.graphs.Any(g => g.GetType().Name == "GridGraph") ? 1 : 0) +
  188. "&hasCustom=" + (script != null && script.data.graphs.Any(g => g != null && !g.GetType().FullName.Contains("Pathfinding.")) ? 1 : 0) +
  189. "&graphCount=" + (script != null ? script.data.graphs.Count(g => g != null) : 0) +
  190. "&unityversion="+Application.unityVersion +
  191. "&branch="+AstarPath.Branch;
  192. #if UNITY_2018_1_OR_NEWER
  193. updateCheckDownload = UnityWebRequest.Get(query);
  194. updateCheckDownload.SendWebRequest();
  195. #else
  196. updateCheckDownload = new WWW(query);
  197. #endif
  198. lastUpdateCheck = System.DateTime.UtcNow;
  199. }
  200. /// <summary>Handles the data from the update page</summary>
  201. static void UpdateCheckCompleted (string result) {
  202. EditorPrefs.SetString("AstarServerMessage", result);
  203. ParseServerMessage(result);
  204. ShowUpdateWindowIfRelevant();
  205. }
  206. static void ParseServerMessage (string result) {
  207. if (string.IsNullOrEmpty(result)) {
  208. return;
  209. }
  210. hasParsedServerMessage = true;
  211. #if ASTARDEBUG
  212. Debug.Log("Result from update check:\n"+result);
  213. #endif
  214. string[] splits = result.Split('|');
  215. latestVersionDescription = splits.Length > 1 ? splits[1] : "";
  216. if (splits.Length > 4) {
  217. // First 4 are just compatibility fields
  218. var fields = splits.Skip(4).ToArray();
  219. // Take all pairs of fields
  220. for (int i = 0; i < (fields.Length/2)*2; i += 2) {
  221. string key = fields[i];
  222. string val = fields[i+1];
  223. astarServerData[key] = val;
  224. }
  225. }
  226. try {
  227. latestVersion = new System.Version(astarServerData["VERSION:branch"]);
  228. } catch (System.Exception ex) {
  229. Debug.LogWarning("Could not parse version\n"+ex);
  230. }
  231. try {
  232. latestBetaVersion = new System.Version(astarServerData["VERSION:beta"]);
  233. } catch (System.Exception ex) {
  234. Debug.LogWarning("Could not parse version\n"+ex);
  235. }
  236. }
  237. static void ShowUpdateWindowIfRelevant () {
  238. #if !ASTAR_ATAVISM
  239. try {
  240. System.DateTime remindDate;
  241. var remindVersion = new System.Version(EditorPrefs.GetString("AstarRemindUpdateVersion", "0.0.0.0"));
  242. if (latestVersion == remindVersion && System.DateTime.TryParse(EditorPrefs.GetString("AstarRemindUpdateDate", "1/1/1971 00:00:01"), out remindDate)) {
  243. if (System.DateTime.UtcNow < remindDate) {
  244. // Don't remind yet
  245. return;
  246. }
  247. } else {
  248. EditorPrefs.DeleteKey("AstarRemindUpdateDate");
  249. EditorPrefs.DeleteKey("AstarRemindUpdateVersion");
  250. }
  251. } catch {
  252. Debug.LogError("Invalid AstarRemindUpdateVersion or AstarRemindUpdateDate");
  253. }
  254. var skipVersion = new System.Version(EditorPrefs.GetString("AstarSkipUpToVersion", AstarPath.Version.ToString()));
  255. if (AstarPathEditor.FullyDefinedVersion(latestVersion) != AstarPathEditor.FullyDefinedVersion(skipVersion) && AstarPathEditor.FullyDefinedVersion(latestVersion) > AstarPathEditor.FullyDefinedVersion(AstarPath.Version)) {
  256. EditorPrefs.DeleteKey("AstarSkipUpToVersion");
  257. EditorPrefs.DeleteKey("AstarRemindUpdateDate");
  258. EditorPrefs.DeleteKey("AstarRemindUpdateVersion");
  259. AstarUpdateWindow.Init(latestVersion, latestVersionDescription);
  260. }
  261. #endif
  262. }
  263. }
  264. }