Seven is the number.
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.

319 lines
12 KiB

4 years ago
  1. using UnityEditor;
  2. using UnityEngine.Networking;
  3. using System.Text.RegularExpressions;
  4. #if UNITY_EDITOR
  5. using System;
  6. using UnityEngine;
  7. using System.Collections.Generic;
  8. namespace Bolt.Utils
  9. {
  10. /// <summary>
  11. /// Creates a instance of the Account Service to register Photon Cloud accounts.
  12. /// </summary>
  13. public class AccountService
  14. {
  15. private const string ServiceUrl = "https://partner.photonengine.com/api/Unity/User/RegisterEx";
  16. private readonly Dictionary<string, string> requestHeaders = new Dictionary<string, string>
  17. {
  18. {"Content-Type", "application/json"},
  19. {"x-functions-key", "VQ920wVUieLHT9c3v1ZCbytaLXpXbktUztKb3iYLCdiRKjUagcl6eg=="}
  20. };
  21. /// <summary>
  22. /// Attempts to create a Photon Cloud Account asynchronously.
  23. /// Once your callback is called, check ReturnCode, Message and AppId to get the result of this attempt.
  24. /// </summary>
  25. /// <param name="email">Email of the account.</param>
  26. /// <param name="serviceTypes">Defines which type of Photon-service is being requested.</param>
  27. /// <param name="callback">Called when the result is available.</param>
  28. public bool RegisterByEmail(string email, string serviceTypes, Action<AccountServiceResponse> callback = null,
  29. Action<string> errorCallback = null)
  30. {
  31. if (IsValidEmail(email) == false)
  32. {
  33. Debug.LogErrorFormat("Email \"{0}\" is not valid", email);
  34. return false;
  35. }
  36. if (string.IsNullOrEmpty(serviceTypes))
  37. {
  38. Debug.LogError("serviceTypes string is null or empty");
  39. return false;
  40. }
  41. AccountServiceRequest req = new AccountServiceRequest();
  42. req.Email = email;
  43. req.ServiceTypes = serviceTypes;
  44. return this.RegisterByEmail(req, callback, errorCallback);
  45. }
  46. public bool RegisterByEmail(string email, List<ServiceTypes> serviceTypes,
  47. Action<AccountServiceResponse> callback = null, Action<string> errorCallback = null)
  48. {
  49. if (serviceTypes == null || serviceTypes.Count == 0)
  50. {
  51. Debug.LogError("serviceTypes list is null or empty");
  52. return false;
  53. }
  54. return this.RegisterByEmail(email, GetServiceTypesFromList(serviceTypes), callback, errorCallback);
  55. }
  56. public bool RegisterByEmail(AccountServiceRequest request, Action<AccountServiceResponse> callback = null,
  57. Action<string> errorCallback = null)
  58. {
  59. if (request == null)
  60. {
  61. Debug.LogError("Registration request is null");
  62. return false;
  63. }
  64. StartCoroutine(
  65. HttpPost(GetUrlWithQueryStringEscaped(request),
  66. requestHeaders,
  67. null,
  68. s =>
  69. {
  70. if (string.IsNullOrEmpty(s))
  71. {
  72. if (errorCallback != null)
  73. {
  74. errorCallback(
  75. "Server's response was empty. Please register through account website during this service interruption.");
  76. }
  77. }
  78. else
  79. {
  80. AccountServiceResponse ase = this.ParseResult(s);
  81. if (ase == null)
  82. {
  83. if (errorCallback != null)
  84. {
  85. errorCallback(
  86. "Error parsing registration response. Please try registering from account website");
  87. }
  88. }
  89. else if (callback != null)
  90. {
  91. callback(ase);
  92. }
  93. }
  94. },
  95. e =>
  96. {
  97. if (errorCallback != null)
  98. {
  99. errorCallback(e);
  100. }
  101. })
  102. );
  103. return true;
  104. }
  105. private static string GetUrlWithQueryStringEscaped(AccountServiceRequest request)
  106. {
  107. string email = UnityEngine.Networking.UnityWebRequest.EscapeURL(request.Email);
  108. string st = UnityEngine.Networking.UnityWebRequest.EscapeURL(request.ServiceTypes);
  109. return string.Format("{0}?email={1}&st={2}", ServiceUrl, email, st);
  110. }
  111. /// <summary>
  112. /// Reads the Json response and applies it to local properties.
  113. /// </summary>
  114. /// <param name="result"></param>
  115. private AccountServiceResponse ParseResult(string result)
  116. {
  117. try
  118. {
  119. AccountServiceResponse res = JsonUtility.FromJson<AccountServiceResponse>(result);
  120. // Unity's JsonUtility does not support deserializing Dictionary, we manually parse it, dirty & ugly af, better then using a 3rd party lib
  121. if (res.ReturnCode == AccountServiceReturnCodes.Success)
  122. {
  123. string[] parts = result.Split(new[] {"\"ApplicationIds\":{"},
  124. StringSplitOptions.RemoveEmptyEntries);
  125. parts = parts[1].Split('}');
  126. string applicationIds = parts[0];
  127. if (!string.IsNullOrEmpty(applicationIds))
  128. {
  129. parts = applicationIds.Split(new[] {',', '"', ':'}, StringSplitOptions.RemoveEmptyEntries);
  130. res.ApplicationIds = new Dictionary<string, string>(parts.Length / 2);
  131. for (int i = 0; i < parts.Length; i = i + 2)
  132. {
  133. res.ApplicationIds.Add(parts[i], parts[i + 1]);
  134. }
  135. }
  136. else
  137. {
  138. Debug.LogError(
  139. "The server did not return any AppId, ApplicationIds was empty in the response.");
  140. return null;
  141. }
  142. }
  143. return res;
  144. }
  145. catch (Exception ex) // probably JSON parsing exception, check if returned string is valid JSON
  146. {
  147. Debug.LogException(ex);
  148. return null;
  149. }
  150. }
  151. private static string GetServiceTypesFromList(List<ServiceTypes> appTypes)
  152. {
  153. if (appTypes != null)
  154. {
  155. string serviceTypes = string.Empty;
  156. if (appTypes.Count > 0)
  157. {
  158. serviceTypes = ((int) appTypes[0]).ToString();
  159. for (int i = 1; i < appTypes.Count; i++)
  160. {
  161. int appType = (int) appTypes[i];
  162. serviceTypes = string.Format("{0},{1}", serviceTypes, appType);
  163. }
  164. }
  165. return serviceTypes;
  166. }
  167. return null;
  168. }
  169. // RFC2822 compliant matching 99.9% of all email addresses in actual use today
  170. // according to http://www.regular-expressions.info/email.html [22.02.2012]
  171. private static Regex reg = new Regex(
  172. "^((?>[a-zA-Z\\d!#$%&'*+\\-/=?^_{|}~]+\\x20*|\"((?=[\\x01-\\x7f])[^\"\\]|\\[\\x01-\\x7f])*\"\\x20*)*(?<angle><))?((?!\\.)(?>\\.?[a-zA-Z\\d!#$%&'*+\\-/=?^_{|}~]+)+|\"((?=[\\x01-\\x7f])[^\"\\]|\\[\\x01-\\x7f])*\")@(((?!-)[a-zA-Z\\d\\-]+(?<!-)\\.)+[a-zA-Z]{2,}|\\[(((?(?<!\\[)\\.)(25[0-5]|2[0-4]\\d|[01]?\\d?\\d)){4}|[a-zA-Z\\d\\-]*[a-zA-Z\\d]:((?=[\\x01-\\x7f])[^\\\\[\\]]|\\[\\x01-\\x7f])+)\\])(?(angle)>)$",
  173. RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
  174. public static bool IsValidEmail(string mailAddress)
  175. {
  176. if (string.IsNullOrEmpty(mailAddress))
  177. {
  178. return false;
  179. }
  180. var result = reg.Match(mailAddress);
  181. return result.Success;
  182. }
  183. //https://forum.unity.com/threads/using-unitywebrequest-in-editor-tools.397466/#post-4485181
  184. private static void StartCoroutine(System.Collections.IEnumerator update)
  185. {
  186. EditorApplication.CallbackFunction closureCallback = null;
  187. closureCallback = () =>
  188. {
  189. try
  190. {
  191. if (update.MoveNext() == false)
  192. {
  193. EditorApplication.update -= closureCallback;
  194. }
  195. }
  196. catch (Exception ex)
  197. {
  198. Debug.LogException(ex);
  199. EditorApplication.update -= closureCallback;
  200. }
  201. };
  202. EditorApplication.update += closureCallback;
  203. }
  204. private static System.Collections.IEnumerator HttpPost(string url, Dictionary<string, string> headers,
  205. byte[] payload, Action<string> successCallback, Action<string> errorCallback)
  206. {
  207. using (UnityWebRequest w = new UnityWebRequest(url, "POST"))
  208. {
  209. if (payload != null)
  210. {
  211. w.uploadHandler = new UploadHandlerRaw(payload);
  212. }
  213. w.downloadHandler = new DownloadHandlerBuffer();
  214. if (headers != null)
  215. {
  216. foreach (var header in headers)
  217. {
  218. w.SetRequestHeader(header.Key, header.Value);
  219. }
  220. }
  221. #if UNITY_2017_2_OR_NEWER
  222. yield return w.SendWebRequest();
  223. #else
  224. yield return w.Send();
  225. #endif
  226. while (w.isDone == false)
  227. yield return null;
  228. #if UNITY_2017_1_OR_NEWER
  229. if (w.isNetworkError || w.isHttpError)
  230. #else
  231. if (w.isError)
  232. #endif
  233. {
  234. if (errorCallback != null)
  235. {
  236. errorCallback(w.error);
  237. }
  238. }
  239. else
  240. {
  241. if (successCallback != null)
  242. {
  243. successCallback(w.downloadHandler.text);
  244. }
  245. }
  246. }
  247. }
  248. }
  249. [Serializable]
  250. public class AccountServiceResponse
  251. {
  252. public int ReturnCode;
  253. public string Message;
  254. public Dictionary<string, string>
  255. ApplicationIds; // Unity's JsonUtility does not support deserializing Dictionary
  256. }
  257. [Serializable]
  258. public class AccountServiceRequest
  259. {
  260. public string Email;
  261. public string ServiceTypes;
  262. }
  263. public class AccountServiceReturnCodes
  264. {
  265. public static int Success = 0;
  266. public static int EmailAlreadyRegistered = 8;
  267. public static int InvalidParameters = 12;
  268. public static readonly Dictionary<int, string> ReturnCodes = new Dictionary<int, string>()
  269. {
  270. {Success, "Success"},
  271. {EmailAlreadyRegistered, "Email Already Registered"},
  272. {InvalidParameters, "Invalid Parameters"}
  273. };
  274. }
  275. public enum ServiceTypes
  276. {
  277. Realtime = 0,
  278. Turnbased = 1,
  279. Chat = 2,
  280. Voice = 3,
  281. TrueSync = 4,
  282. Pun = 5,
  283. Thunder = 6,
  284. Bolt = 20
  285. }
  286. }
  287. #endif