PageRenderTime 45ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Assets/GameAnalytics/Plugins/Framework/Scripts/GA_Queue.cs

https://bitbucket.org/AgentCodeMonkey/gameframework-unity-project
C# | 372 lines | 234 code | 48 blank | 90 comment | 52 complexity | 5db7b8c48059163c69bed0c3e5076907 MD5 | raw file
  1. /// <summary>
  2. /// This class handles the submit queue. Any data which should be sent to the Game Analytics servers should be added to the queue
  3. /// using the AddItem method. All messages in the queue will be sent at every TIMER interval.
  4. /// </summary>
  5. using UnityEngine;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System;
  9. public static class GA_Queue
  10. {
  11. /// <summary>
  12. /// The maximum length of the submit queue. When messages are not submitted due to an error they are put back
  13. /// into the queue and another attempt at submitting them is made during the next submit interval. When the queue
  14. /// reaches the maximum length the oldest messages will be dropped.
  15. /// </summary>
  16. public static int MAXQUEUESIZE = 800;
  17. /// <summary>
  18. /// If true the game will automatically quit after data has been submitted to the server
  19. /// </summary>
  20. public static bool QUITONSUBMIT = false;
  21. #region private values
  22. /// <summary>
  23. /// A list containing all the messages which should be submitted to the GA server on the next submit
  24. /// </summary>
  25. private static List<GA_Submit.Item> _queue = new List<GA_Submit.Item>();
  26. /// <summary>
  27. /// A temporary list containing any new messages being recorded during a submit.
  28. /// These messages will be put back into the queue for the next submit
  29. /// </summary>
  30. private static List<GA_Submit.Item> _tempQueue = new List<GA_Submit.Item>();
  31. /// <summary>
  32. /// A list containing all the failed messages from a submit. These messages will be put
  33. /// back into the submit queue when another message is submitted succesfully
  34. /// </summary>
  35. private static List<GA_Submit.Item> _errorQueue = new List<GA_Submit.Item>();
  36. /// <summary>
  37. /// While true we are submitting messages to the GA server and new messages should therefore be put into the temporary queue
  38. /// </summary>
  39. private static bool _submittingData = false;
  40. /// <summary>
  41. /// The number of messages we have submitted to the GA server so far during any queue submit
  42. /// </summary>
  43. private static int _submitCount = 0;
  44. /// <summary>
  45. /// set to true when the game cannot be found, to prevent us from sending messages constantly with no hope of success
  46. /// </summary>
  47. private static bool _endsubmit = false;
  48. #endregion
  49. #region public methods
  50. /// <summary>
  51. /// Add a new message to the submit queue. If we are in the middle of a queue submit we add the message to the temporary queue instead
  52. /// </summary>
  53. /// <param name="parameters">
  54. /// The message is a dictionary of parameters <see cref="Dictionary<System.String, System.Object>"/>
  55. /// </param>
  56. /// <param name="type">
  57. /// The GA service to send the message to (see GA_Submit) <see cref="GA_Submit.CategoryType"/>
  58. /// </param>
  59. /// <param name="stack">
  60. /// If true any identical messages in the queue will be merged/stacked as a single message, to save server load
  61. /// </param>
  62. public static void AddItem(Dictionary<string, object> parameters, GA_Submit.CategoryType type, bool stack)
  63. {
  64. //No reason to add any more items if we have stopped submitting data or we are not supposed to submit in the first place
  65. if (_endsubmit || (Application.isEditor && !GA.Settings.RunInEditorPlayMode))
  66. {
  67. return;
  68. }
  69. GA_Submit.Item item = new GA_Submit.Item
  70. {
  71. Type = type,
  72. Parameters = parameters,
  73. AddTime = Time.time
  74. };
  75. if (_submittingData)
  76. {
  77. if (stack && type == GA_Submit.CategoryType.GA_Log)
  78. {
  79. StackQueue(_tempQueue, item);
  80. }
  81. else
  82. {
  83. _tempQueue.Add(item);
  84. }
  85. }
  86. else
  87. {
  88. if (stack && type == GA_Submit.CategoryType.GA_Log)
  89. {
  90. StackQueue(_queue, item);
  91. }
  92. else
  93. {
  94. _queue.Add(item);
  95. }
  96. }
  97. }
  98. /// <summary>
  99. /// At every timer interval we submit the next batch of messages to the GA server
  100. /// </summary>
  101. /// <returns>
  102. /// A <see cref="IEnumerator"/>
  103. /// </returns>
  104. public static IEnumerator SubmitQueue()
  105. {
  106. while (!_endsubmit)
  107. {
  108. while (GA.Settings.CustomUserID && GA.API.GenericInfo.UserID == string.Empty)
  109. {
  110. GA.LogWarning("GameAnalytics: User ID not set, not sending data. Waiting 10 sec. to try again.");
  111. yield return new WaitForSeconds(10f);
  112. }
  113. //If we're still submitting data then wait half a second and try again
  114. while (_submittingData)
  115. {
  116. yield return new WaitForSeconds(0.5f);
  117. }
  118. //If we have internet connection then add any archived data to the submit queue
  119. if (GA.Settings.ArchiveData && GA.Settings.InternetConnectivity)
  120. {
  121. List<GA_Submit.Item> archivedItems = GA.API.Archive.GetArchivedData();
  122. if (archivedItems != null && archivedItems.Count > 0)
  123. {
  124. foreach (GA_Submit.Item item in archivedItems)
  125. {
  126. GA_Queue.AddItem(item.Parameters, item.Type, false);
  127. }
  128. if (GA.Settings.DebugMode)
  129. {
  130. GA.Log("GA: Network connection detected. Adding archived data to next submit queue.");
  131. }
  132. }
  133. }
  134. ForceSubmit();
  135. //Wait for the next timer interval before we try to submit again
  136. yield return new WaitForSeconds(GA.Settings.SubmitInterval);
  137. }
  138. }
  139. /// <summary>
  140. /// Submit any queued up data immediately. Useful when you want to control when data is submitted.
  141. /// </summary>
  142. public static void ForceSubmit()
  143. {
  144. //If we have something to submit and we have not stopped submitting completely then we start submitting data
  145. if (_queue.Count > 0 && !_submittingData && !_endsubmit)
  146. {
  147. _submittingData = true;
  148. GA.Log("GameAnalytics: Queue submit started");
  149. GA.API.Submit.SubmitQueue(_queue, Submitted, SubmitError);
  150. }
  151. }
  152. #endregion
  153. #region private methods
  154. /// <summary>
  155. /// If we already have the same message item in the queue we simply increase the message count instead of adding a duplicate.
  156. /// </summary>
  157. /// <param name='queue'>
  158. /// Queue.
  159. /// </param>
  160. /// <param name='item'>
  161. /// Item.
  162. /// </param>
  163. private static void StackQueue(List<GA_Submit.Item> queue, GA_Submit.Item item)
  164. {
  165. bool stacked = false;
  166. for (int i = 0; i < queue.Count; i++)
  167. {
  168. if (!stacked && queue[i].Type == GA_Submit.CategoryType.GA_Log && item.Type == GA_Submit.CategoryType.GA_Log &&
  169. queue[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.EventID]) &&
  170. item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.EventID]) &&
  171. queue[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.EventID]].Equals(item.Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.EventID]]) &&
  172. queue[i].Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Message]) &&
  173. item.Parameters.ContainsKey(GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Message]) &&
  174. queue[i].Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Message]].Equals(item.Parameters[GA_ServerFieldTypes.Fields[GA_ServerFieldTypes.FieldType.Message]]))
  175. {
  176. stacked = true;
  177. queue[i] = new GA_Submit.Item { AddTime = queue[i].AddTime, Parameters = queue[i].Parameters, Type = queue[i].Type, Count = Mathf.Max(2, queue[i].Count + 1) };
  178. }
  179. }
  180. if (!stacked)
  181. {
  182. queue.Add(item);
  183. }
  184. }
  185. /// <summary>
  186. /// This is called once for every successful message submitted. We count the number of messages submitted and when
  187. /// we are done submitting everything in the queue we put everything in the temporary queue back into the queue
  188. /// </summary>
  189. private static void Submitted(List<GA_Submit.Item> items, bool success)
  190. {
  191. _submitCount += items.Count;
  192. // Update GA_Status
  193. if (success)
  194. {
  195. GA.Settings.TotalMessagesSubmitted += items.Count;
  196. foreach (GA_Submit.Item it in items)
  197. {
  198. switch (it.Type)
  199. {
  200. case GA_Submit.CategoryType.GA_Event:
  201. GA.Settings.DesignMessagesSubmitted++;
  202. break;
  203. case GA_Submit.CategoryType.GA_Log:
  204. GA.Settings.QualityMessagesSubmitted++;
  205. break;
  206. case GA_Submit.CategoryType.GA_Purchase:
  207. GA.Settings.BusinessMessagesSubmitted++;
  208. break;
  209. case GA_Submit.CategoryType.GA_User:
  210. GA.Settings.UserMessagesSubmitted++;
  211. break;
  212. }
  213. }
  214. }
  215. /* When all items have either been submitted successfully or errors have been stored in the error queue,
  216. * we can return to collecting new events normally */
  217. if (_submitCount >= _queue.Count)
  218. {
  219. if (GA.Settings.DebugMode)
  220. {
  221. GA.Log("GA: Queue submit over");
  222. }
  223. //If we were told to quit after this submit then quit
  224. if (QUITONSUBMIT)
  225. {
  226. Application.Quit();
  227. }
  228. //Put the items collected while we were submitting in to the queue and clear the temporary queue
  229. _queue = _tempQueue;
  230. _tempQueue = new List<GA_Submit.Item>();
  231. //Do not attempt to re-submit errored messages until a message has been submitted successfully
  232. if (success)
  233. {
  234. _queue.AddRange(_errorQueue);
  235. _errorQueue = new List<GA_Submit.Item>();
  236. }
  237. //Reset counters and go back to normal event collection
  238. _submitCount = 0;
  239. _submittingData = false;
  240. }
  241. }
  242. /// <summary>
  243. /// This is called once for every failed message (an error occurs during submit). We put the message into the temporary queue - this way
  244. /// it will be put back into the queue at the end of the queue submit and on the next queue submit we attempt to submit the message again
  245. /// </summary>
  246. /// <param name="item">
  247. /// The failed message <see cref="GA_Submit.Item"/>
  248. /// </param>
  249. private static void SubmitError(List<GA_Submit.Item> items)
  250. {
  251. //If items are null we should stop submitting data after this timer interval because the game cannot be found
  252. if (items == null)
  253. {
  254. GA.Log("GA: Ending all data submission after this timer interval");
  255. _endsubmit = true;
  256. return;
  257. }
  258. else
  259. {
  260. // Update GA_Status
  261. GA.Settings.TotalMessagesFailed += items.Count;
  262. foreach (GA_Submit.Item it in items)
  263. {
  264. switch (it.Type)
  265. {
  266. case GA_Submit.CategoryType.GA_Event:
  267. GA.Settings.DesignMessagesFailed++;
  268. break;
  269. case GA_Submit.CategoryType.GA_Log:
  270. GA.Settings.QualityMessagesFailed++;
  271. break;
  272. case GA_Submit.CategoryType.GA_Purchase:
  273. GA.Settings.BusinessMessagesFailed++;
  274. break;
  275. case GA_Submit.CategoryType.GA_User:
  276. GA.Settings.UserMessagesFailed++;
  277. break;
  278. }
  279. }
  280. // Check to see if we have access to a network connection - if this is the cause of the error future submits will take it into account
  281. GA.RunCoroutine(GA.Settings.CheckInternetConnectivity(false));
  282. }
  283. _errorQueue.AddRange(items);
  284. /* If the number of error messages exceeds the limit we sort the errors so game design events
  285. * are the first to be discarded. Then we remove the access error messages */
  286. if (_errorQueue.Count > MAXQUEUESIZE)
  287. {
  288. _errorQueue.Sort(new ItemComparer());
  289. _errorQueue.RemoveRange(MAXQUEUESIZE, _errorQueue.Count - MAXQUEUESIZE);
  290. }
  291. Submitted(items, false);
  292. }
  293. #endregion
  294. }
  295. class ItemComparer : IComparer<GA_Submit.Item>
  296. {
  297. public int Compare(GA_Submit.Item item1, GA_Submit.Item item2)
  298. {
  299. if (item1.Type != GA_Submit.CategoryType.GA_Event &&
  300. item2.Type == GA_Submit.CategoryType.GA_Event)
  301. {
  302. return 1;
  303. }
  304. else if (item2.Type != GA_Submit.CategoryType.GA_Event &&
  305. item1.Type == GA_Submit.CategoryType.GA_Event)
  306. {
  307. return -1;
  308. }
  309. float n1 = item1.AddTime;
  310. float n2 = item2.AddTime;
  311. if (n1 < n2)
  312. {
  313. return 1;
  314. }
  315. else if (n1 == n2)
  316. {
  317. return 0;
  318. }
  319. else
  320. {
  321. return -1;
  322. }
  323. }
  324. }