AstarUpdateChecker.cs 10 KB

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