TapClientStandalone.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. using UnityEngine;
  2. using System.Threading.Tasks;
  3. using TapSDK.Core.Internal.Utils;
  4. using TapSDK.Core.Standalone.Internal;
  5. using TapSDK.UI;
  6. using System;
  7. using System.Runtime.InteropServices;
  8. using TapSDK.Core.Standalone.Internal.Openlog;
  9. using System.Threading;
  10. using TapSDK.Core.Internal.Log;
  11. namespace TapSDK.Core.Standalone
  12. {
  13. #if UNITY_STANDALONE_WIN
  14. public class TapClientStandalone
  15. {
  16. // 是否是渠道服游戏包
  17. private static bool isChannelPackage = false;
  18. // -1 未执行 0 失败 1 成功
  19. private static int lastIsLaunchedFromTapTapPCResult = -1;
  20. private static bool isRuningIsLaunchedFromTapTapPC = false;
  21. // 当为渠道游戏包时,与启动器的初始化校验结果
  22. private static TapInitResult tapInitResult;
  23. // <summary>
  24. // 校验游戏是否通过启动器唤起,建立与启动器通讯
  25. //</summary>
  26. public static async Task<bool> IsLaunchedFromTapTapPC()
  27. {
  28. // 正在执行中
  29. if (isRuningIsLaunchedFromTapTapPC)
  30. {
  31. UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 正在执行,请勿重复调用", UIManager.GeneralToastLevel.Error);
  32. TapLog.Error("IsLaunchedFromTapTapPC 正在执行,请勿重复调用");
  33. return false;
  34. }
  35. // 多次执行时返回上一次结果
  36. if (lastIsLaunchedFromTapTapPCResult != -1)
  37. {
  38. TapLog.Log("IsLaunchedFromTapTapPC duplicate invoke return " + lastIsLaunchedFromTapTapPCResult);
  39. return lastIsLaunchedFromTapTapPCResult > 0;
  40. }
  41. isChannelPackage = true;
  42. TapTapSdkOptions coreOptions = TapCoreStandalone.coreOptions;
  43. if (coreOptions == null)
  44. {
  45. UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 调用必须在初始化之后", UIManager.GeneralToastLevel.Error);
  46. TapLog.Error("IsLaunchedFromTapTapPC 调用必须在初始化之后");
  47. return false;
  48. }
  49. string clientId = coreOptions.clientId;
  50. string pubKey = coreOptions.clientPublicKey;
  51. if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(pubKey))
  52. {
  53. UIManager.Instance.OpenToast("clientId 及 TapPubKey 参数都不能为空, clientId =" + clientId + ", TapPubKey = " + pubKey, UIManager.GeneralToastLevel.Error);
  54. TapLog.Error("clientId 或 TapPubKey 无效, clientId = " + clientId + ", TapPubKey = " + pubKey);
  55. return false;
  56. }
  57. isRuningIsLaunchedFromTapTapPC = true;
  58. string sessionId = Guid.NewGuid().ToString();
  59. TapCoreTracker.Instance.TrackStart(TapCoreTracker.METHOD_LAUNCHER, sessionId);
  60. try
  61. {
  62. TapInitResult result = await RunClientBridgeMethod(clientId, pubKey);
  63. TapLog.Log("check startupWithClientBridge finished thread = " + Thread.CurrentThread.ManagedThreadId);
  64. isRuningIsLaunchedFromTapTapPC = false;
  65. if (result.needQuitGame)
  66. {
  67. lastIsLaunchedFromTapTapPCResult = 0;
  68. TapCoreTracker.Instance.TrackSuccess(TapCoreTracker.METHOD_LAUNCHER, sessionId, TapCoreTracker.SUCCESS_TYPE_RESTART);
  69. TapLog.Log("IsLaunchedFromTapTapPC Quit game");
  70. Application.Quit();
  71. return false;
  72. }
  73. else
  74. {
  75. if (result.result == (int)TapSDKInitResult.OK)
  76. {
  77. string currentClientId;
  78. bool isFetchClientIdSuccess = TapClientBridge.GetClientId(out currentClientId);
  79. TapLog.Log("IsLaunchedFromTapTapPC get clientId = " + currentClientId);
  80. if (isFetchClientIdSuccess && !string.IsNullOrEmpty(currentClientId) && currentClientId != clientId)
  81. {
  82. UIManager.Instance.OpenToast("SDK 中配置的 clientId = " + clientId + "与 Tap 启动器中" + currentClientId + "不一致", UIManager.GeneralToastLevel.Error);
  83. TapLog.Error("SDK 中配置的 clientId = " + clientId + "与 Tap 启动器中" + currentClientId + "不一致");
  84. TapCoreTracker.Instance.TrackFailure(TapCoreTracker.METHOD_LAUNCHER, sessionId, -1, "SDK 中配置的 clientId = " + clientId + "与 Tap 启动器中" + currentClientId + "不一致");
  85. lastIsLaunchedFromTapTapPCResult = 0;
  86. return false;
  87. }
  88. string openId;
  89. bool fetchOpenIdSuccess = TapClientBridge.GetTapUserOpenId(out openId);
  90. if (fetchOpenIdSuccess)
  91. {
  92. TapLog.Log("IsLaunchedFromTapTapPC get openId = " + openId);
  93. EventManager.TriggerEvent(EventManager.IsLaunchedFromTapTapPCFinished, openId);
  94. }
  95. else
  96. {
  97. TapLog.Log("IsLaunchedFromTapTapPC get openId failed");
  98. }
  99. lastIsLaunchedFromTapTapPCResult = 1;
  100. TapClientBridgePoll.StartUp();
  101. TapCoreTracker.Instance.TrackSuccess(TapCoreTracker.METHOD_LAUNCHER, sessionId, TapCoreTracker.SUCCESS_TYPE_INIT);
  102. TapLog.Log("IsLaunchedFromTapTapPC check success");
  103. return true;
  104. }
  105. else
  106. {
  107. TapCoreTracker.Instance.TrackFailure(TapCoreTracker.METHOD_LAUNCHER, sessionId, (int)result.result, result.errorMsg ?? "");
  108. lastIsLaunchedFromTapTapPCResult = 0;
  109. TapLog.Log("IsLaunchedFromTapTapPC show TapClient tip Pannel " + result.result + " , error = " + result.errorMsg);
  110. string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel";
  111. if (Resources.Load<GameObject>(tipPannelPath) != null)
  112. {
  113. var pannel = UIManager.Instance.OpenUI<TapClientConnectTipController>(tipPannelPath);
  114. pannel.Show(result.result);
  115. }
  116. return false;
  117. }
  118. }
  119. }
  120. catch (Exception e)
  121. {
  122. lastIsLaunchedFromTapTapPCResult = 0;
  123. TapCoreTracker.Instance.TrackFailure(TapCoreTracker.METHOD_LAUNCHER, sessionId, (int)TapSDKInitResult.Unknown, e.Message ?? "");
  124. TapLog.Log("IsLaunchedFromTapTapPC check exception = " + e.Message + " \n" + e.StackTrace);
  125. string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel";
  126. if (Resources.Load<GameObject>(tipPannelPath) != null)
  127. {
  128. var pannel = UIManager.Instance.OpenUI<TapClientConnectTipController>(tipPannelPath);
  129. pannel.Show((int)TapSDKInitResult.Unknown);
  130. }
  131. return false;
  132. }
  133. }
  134. private static async Task<TapInitResult> RunClientBridgeMethod(string clientId, string pubKey)
  135. {
  136. TaskCompletionSource<TapInitResult> task = new TaskCompletionSource<TapInitResult>();
  137. try
  138. {
  139. await Task.Run(() =>
  140. {
  141. TapLog.Log( "check startupWithClientBridge start thread = " + Thread.CurrentThread.ManagedThreadId);
  142. bool needQuitGame = TapClientBridge.TapSDK_RestartAppIfNecessary(clientId);
  143. TapLog.Log("RunClientBridgeMethodWithTimeout invoke TapSDK_RestartAppIfNecessary result = " + needQuitGame);
  144. TapLog.Log("RunClientBridgeMethodWithTimeout invoke TapSDK_RestartAppIfNecessary finished " );
  145. if (needQuitGame)
  146. {
  147. tapInitResult = new TapInitResult(needQuitGame);
  148. }
  149. else
  150. {
  151. string outputError;
  152. int tapSDKInitResult = TapClientBridge.CheckInitState(out outputError, pubKey);
  153. TapLog.Log("RunClientBridgeMethodWithTimeout invoke CheckInitState result = " + tapSDKInitResult + ", error = " + outputError);
  154. tapInitResult = new TapInitResult(tapSDKInitResult, outputError);
  155. }
  156. task.TrySetResult(tapInitResult);
  157. });
  158. }
  159. catch (Exception ex)
  160. {
  161. TapLog.Log("RunClientBridgeMethodWithTimeout invoke C 方法出错!" + ex.Message);
  162. task.TrySetException(ex);
  163. }
  164. return await task.Task;
  165. }
  166. /// <summary>
  167. /// 是否需要从启动器登录
  168. /// </summary>
  169. public static bool IsNeedLoginByTapClient()
  170. {
  171. return isChannelPackage;
  172. }
  173. public static bool isPassedInLaunchedFromTapTapPCCheck(){
  174. return lastIsLaunchedFromTapTapPCResult > 0;
  175. }
  176. private static Action<bool, string> currentLoginCallback;
  177. /// <summary>
  178. /// 发起登录授权
  179. /// </summary>
  180. public static bool StartLoginWithScopes(string[] scopes, string responseType, string redirectUri,
  181. string codeChallenge, string state, string codeChallengeMethod, string versonCode, string sdkUa, string info, Action<bool, string> callback)
  182. {
  183. if (lastIsLaunchedFromTapTapPCResult == -1)
  184. {
  185. // UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 正在执行,请在完成后调用授权接口", UIManager.GeneralToastLevel.Error);
  186. TapLog.Error(" login must be invoked after IsLaunchedFromTapTapPC success");
  187. throw new Exception("login must be invoked after IsLaunchedFromTapTapPC success");
  188. }
  189. TapLog.Log("LoginWithScopes start login by tapclient thread = " + Thread.CurrentThread.ManagedThreadId);
  190. try
  191. {
  192. TapClientBridge.RegisterCallback(TapEventID.AuthorizeFinished_internal, loginCallbackDelegate);
  193. AuthorizeResult authorizeResult = TapClientBridge.LoginWithScopesInternal(scopes, responseType, redirectUri,
  194. codeChallenge, state, codeChallengeMethod, versonCode, sdkUa, info);
  195. TapLog.Log("LoginWithScopes start result = " + authorizeResult);
  196. if (authorizeResult != AuthorizeResult.OK)
  197. {
  198. TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate);
  199. return false;
  200. }
  201. else
  202. {
  203. currentLoginCallback = callback;
  204. return true;
  205. }
  206. }
  207. catch (Exception ex)
  208. {
  209. TapLog.Log("LoginWithScopes start login by tapclient error = " + ex.Message);
  210. TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate);
  211. return false;
  212. }
  213. }
  214. [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))]
  215. static void loginCallbackDelegate(int id, IntPtr userData)
  216. {
  217. TapLog.Log("LoginWithScopes recevie callback " + id);
  218. if (id == (int)TapEventID.AuthorizeFinished_internal)
  219. {
  220. TapLog.Log("LoginWithScopes callback thread = " + Thread.CurrentThread.ManagedThreadId);
  221. TapClientBridge.AuthorizeFinishedResponse response = Marshal.PtrToStructure<TapClientBridge.AuthorizeFinishedResponse>(userData);
  222. TapLog.Log("LoginWithScopes callback = " + response.is_cancel + " uri = " + response.callback_uri);
  223. if (currentLoginCallback != null)
  224. {
  225. currentLoginCallback(response.is_cancel != 0, response.callback_uri);
  226. TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate);
  227. currentLoginCallback = null;
  228. }
  229. }
  230. }
  231. // DLC 相关功能
  232. private static Action<string, bool> currentDlcDelegate;
  233. private static Action<bool> currentLicenseDelegate;
  234. /// 查询是否购买 DLC , 未调用 isLaunchFromPC 会抛异常
  235. public static bool QueryDLC(string skuId)
  236. {
  237. if (lastIsLaunchedFromTapTapPCResult != 1)
  238. {
  239. throw new Exception("queryDLC must be invoked after IsLaunchedFromTapTapPC success");
  240. }
  241. bool success = TapClientBridge.QueryDLC(skuId);
  242. return success;
  243. }
  244. /// 跳转到 TapTap 客户端 DLC 购买页面 , 未调用 isLaunchFromPC 会抛异常
  245. public static bool ShowStore(string skuId)
  246. {
  247. if (lastIsLaunchedFromTapTapPCResult != 1)
  248. {
  249. throw new Exception("purchaseDLC must be invoked after IsLaunchedFromTapTapPC success");
  250. }
  251. TapLog.Log("purchaseDLC start = " + skuId);
  252. return TapClientBridge.TapDLC_ShowStore(skuId);
  253. }
  254. /// 注册 DLC 购买状态变更回调,包括购买成功和退款
  255. public static void RegisterDLCOwnedCallback(Action<string, bool> dlcDelegate)
  256. {
  257. currentDlcDelegate = dlcDelegate;
  258. TapClientBridge.RegisterCallback(TapEventID.DLCPlayableStatusChanged, DLCCallbackDelegate);
  259. }
  260. /// DLC 回调
  261. [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))]
  262. static void DLCCallbackDelegate(int id, IntPtr userData)
  263. {
  264. TapLog.Log("queryDlC recevie callback " + id);
  265. if (currentDlcDelegate != null)
  266. {
  267. TapClientBridge.DLCPlayableStatusChangedResponse response = Marshal.PtrToStructure<TapClientBridge.DLCPlayableStatusChangedResponse>(userData);
  268. TapLog.Log("queryDlC callback = " + response.dlc_id + " isOwn = " + response.is_playable);
  269. currentDlcDelegate(response.dlc_id, response.is_playable != 0);
  270. }
  271. }
  272. /// 注册 License 购买状态变更回调,包括购买成功和退款
  273. public static void RegisterLicenseCallback(Action<bool> licensecDelegate)
  274. {
  275. currentLicenseDelegate = licensecDelegate;
  276. TapClientBridge.RegisterCallback(TapEventID.GamePlayableStatusChanged, LicenseCallbackDelegate);
  277. }
  278. /// License 回调
  279. [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))]
  280. static void LicenseCallbackDelegate(int id, IntPtr userData)
  281. {
  282. TapLog.Log("License recevie callback " + id);
  283. if (currentLicenseDelegate != null)
  284. {
  285. TapClientBridge.GamePlayableStatusChangedResponse response = Marshal.PtrToStructure<TapClientBridge.GamePlayableStatusChangedResponse>(userData);
  286. TapLog.Log("License callback isOwn changed " + response.is_playable );
  287. currentLicenseDelegate(response.is_playable != 0);
  288. }
  289. }
  290. public static bool HasLicense()
  291. {
  292. if (lastIsLaunchedFromTapTapPCResult != 1)
  293. {
  294. throw new Exception("checkLicense must be invoked after IsLaunchedFromTapTapPC success");
  295. }
  296. return TapClientBridge.HasLicense();
  297. }
  298. // 初始化校验结果
  299. private class TapInitResult
  300. {
  301. internal int result;
  302. internal string errorMsg;
  303. internal bool needQuitGame = false;
  304. public TapInitResult(int result, string errorMsg)
  305. {
  306. this.result = result;
  307. this.errorMsg = errorMsg;
  308. }
  309. public TapInitResult(bool needQuitGame)
  310. {
  311. this.needQuitGame = needQuitGame;
  312. }
  313. }
  314. }
  315. #endif
  316. }