using UnityEngine; using System.Threading.Tasks; using TapTap.Common.Standalone.Internal; using TapTap.UI; using System; using System.Runtime.InteropServices; using System.Threading; using System.Collections.Generic; namespace TapTap.Common.Standalone { #if UNITY_STANDALONE_WIN public class TapClientStandalone { // 是否是渠道服游戏包 private static bool isChannelPackage = false; // -1 未执行 0 失败 1 成功 private static int lastIsLaunchedFromTapTapPCResult = -1; private static bool isRuningIsLaunchedFromTapTapPC = false; // 当为渠道游戏包时,与启动器的初始化校验结果 private static TapInitResult tapInitResult; // // 校验游戏是否通过启动器唤起,建立与启动器通讯 // public static async Task IsLaunchedFromTapTapPC() { // 正在执行中 if (isRuningIsLaunchedFromTapTapPC) { UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 正在执行,请勿重复调用", UIManager.GeneralToastLevel.Error); Log(true, "IsLaunchedFromTapTapPC 正在执行,请勿重复调用"); return false; } // 多次执行时返回上一次结果 if (lastIsLaunchedFromTapTapPCResult != -1) { Log(false, "IsLaunchedFromTapTapPC duplicate invoke return " + lastIsLaunchedFromTapTapPCResult); return lastIsLaunchedFromTapTapPCResult > 0; } isChannelPackage = true; if (TapCommonStandalone.currentTapConfig == null) { UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 调用必须在初始化之后", UIManager.GeneralToastLevel.Error); Log(true, "IsLaunchedFromTapTapPC 调用必须在初始化之后"); return false; } string clientId = TapCommonStandalone.currentTapConfig.ClientID; string pubKey = TapCommonStandalone.currentTapConfig.ClientPublicKey; if (string.IsNullOrEmpty(clientId) || string.IsNullOrEmpty(pubKey)) { UIManager.Instance.OpenToast("clientId 及 TapPubKey 参数都不能为空, clientId =" + clientId + ", TapPubKey = " + pubKey, UIManager.GeneralToastLevel.Error); Log(true, "clientId 或 TapPubKey 无效, clientId = " + clientId + ", TapPubKey = " + pubKey); return false; } isRuningIsLaunchedFromTapTapPC = true; try { TapInitResult result = await RunClientBridgeMethod(clientId, pubKey); Log(false, "check startupWithClientBridge finished thread = " + Thread.CurrentThread.ManagedThreadId); isRuningIsLaunchedFromTapTapPC = false; if (result.needQuitGame) { lastIsLaunchedFromTapTapPCResult = 0; Log(false, "IsLaunchedFromTapTapPC Quit game"); Application.Quit(); return false; } else { if (result.result == (int)TapSDKInitResult.OK) { string currentClientId; bool isFetchClientIdSuccess = TapClientBridge.GetClientId(out currentClientId); Log(false, "IsLaunchedFromTapTapPC get clientId = " + currentClientId, false); if (isFetchClientIdSuccess && !string.IsNullOrEmpty(currentClientId) && currentClientId != clientId) { UIManager.Instance.OpenToast("SDK 中配置的 clientId = " + clientId + "与 Tap 启动器中" + currentClientId + "不一致", UIManager.GeneralToastLevel.Error); Log(true, "SDK 中配置的 clientId = " + clientId + "与 Tap 启动器中" + currentClientId + "不一致"); lastIsLaunchedFromTapTapPCResult = 0; return false; } string openId; bool fetchOpenIdSuccess = TapClientBridge.GetTapUserOpenId(out openId); if (fetchOpenIdSuccess) { Log(false, "IsLaunchedFromTapTapPC get openId = " + openId, false); EventManager.TriggerEvent(EventConst.IsLaunchedFromTapTapPCFinished, openId); } else { Log(false, "IsLaunchedFromTapTapPC get openId failed"); } lastIsLaunchedFromTapTapPCResult = 1; TapCommonBridgePoll.StartUp(); // 如果开发者已经注册了监听客户端运行状态,此时添加对应回调 if ( taptapPCStateChangeListeners != null && taptapPCStateChangeListeners.Count > 0 && !hasRegisterSystemListener ) { TapClientBridge.RegisterCallback(TapEventID.SystemStateChanged, TapTapPCStateDelegate); hasRegisterSystemListener = true; } Log(false, "IsLaunchedFromTapTapPC check success"); return true; } else { lastIsLaunchedFromTapTapPCResult = 0; Log(false, "IsLaunchedFromTapTapPC show TapClient tip Pannel " + result.result + " , error = " + result.errorMsg); string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel"; if (Resources.Load(tipPannelPath) != null) { var pannel = UIManager.Instance.OpenUI(tipPannelPath); pannel.Show(result.result); } return false; } } } catch (Exception e) { lastIsLaunchedFromTapTapPCResult = 0; Log(false, "IsLaunchedFromTapTapPC check exception = " + e.Message + " \n" + e.StackTrace); string tipPannelPath = "Prefabs/TapClient/TapClientConnectTipPanel"; if (Resources.Load(tipPannelPath) != null) { var pannel = UIManager.Instance.OpenUI(tipPannelPath); pannel.Show((int)TapSDKInitResult.Unknown); } return false; } } private static async Task RunClientBridgeMethod(string clientId, string pubKey) { TaskCompletionSource task = new TaskCompletionSource(); try { await Task.Run(() => { Log(false, "check startupWithClientBridge start thread = " + Thread.CurrentThread.ManagedThreadId); bool needQuitGame = TapClientBridge.TapSDK_RestartAppIfNecessary(clientId); Log(false, "RunClientBridgeMethodWithTimeout invoke TapSDK_RestartAppIfNecessary result = " + needQuitGame); if (needQuitGame) { tapInitResult = new TapInitResult(needQuitGame); } else { string outputError; int tapSDKInitResult = TapClientBridge.CheckInitState(out outputError, pubKey); Log(false, "RunClientBridgeMethodWithTimeout invoke CheckInitState result = " + tapSDKInitResult + ", error = " + outputError); tapInitResult = new TapInitResult(tapSDKInitResult, outputError); } task.TrySetResult(tapInitResult); }); } catch (Exception ex) { Log(false, "RunClientBridgeMethodWithTimeout invoke C 方法出错!" + ex.Message); task.TrySetException(ex); } return await task.Task; } /// /// 是否需要从启动器登录 /// public static bool IsNeedLoginByTapClient() { return isChannelPackage; } private static Action currentLoginCallback; /// /// 发起登录授权 /// public static bool StartLoginWithScopes(string[] scopes, string responseType, string redirectUri, string codeChallenge, string state, string codeChallengeMethod, string versonCode, string sdkUa, string info, Action callback) { if (lastIsLaunchedFromTapTapPCResult == -1) { // UIManager.Instance.OpenToast("IsLaunchedFromTapTapPC 正在执行,请在完成后调用授权接口", UIManager.GeneralToastLevel.Error); Log(true, " login must be invoked after IsLaunchedFromTapTapPC success"); throw new Exception("login must be invoked after IsLaunchedFromTapTapPC success"); } Log(false, "LoginWithScopes start login by tapclient thread = " + Thread.CurrentThread.ManagedThreadId); try { TapClientBridge.RegisterCallback(TapEventID.AuthorizeFinished_internal, loginCallbackDelegate); AuthorizeResult authorizeResult = TapClientBridge.LoginWithScopesInternal(scopes, responseType, redirectUri, codeChallenge, state, codeChallengeMethod, versonCode, sdkUa, info); Log(false, "LoginWithScopes start result = " + authorizeResult); if (authorizeResult != AuthorizeResult.OK) { TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate); return false; } else { currentLoginCallback = callback; return true; } } catch (Exception ex) { Log(false, "LoginWithScopes start login by tapclient error = " + ex.Message); TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate); return false; } } [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))] static void loginCallbackDelegate(int id, IntPtr userData) { Log(false, "LoginWithScopes recevie callback " + id); if (id == (int)TapEventID.AuthorizeFinished_internal) { Log(false, "LoginWithScopes callback thread = " + Thread.CurrentThread.ManagedThreadId); TapClientBridge.AuthorizeFinishedResponse_internal response = Marshal.PtrToStructure(userData); Log(false, "LoginWithScopes callback = " + response.is_cancel + " uri = " + response.callback_uri, false); if (currentLoginCallback != null) { currentLoginCallback(response.is_cancel != 0, response.callback_uri); TapClientBridge.UnRegisterCallback(TapEventID.AuthorizeFinished_internal,loginCallbackDelegate); currentLoginCallback = null; } } } // DLC 相关功能 private static Action currentDlcDelegate; private static Action currentLicenseDelegate; /// 查询是否购买 DLC , 未调用 isLaunchFromPC 会抛异常 public static bool QueryDLC(string skuId) { if (lastIsLaunchedFromTapTapPCResult != 1) { throw new Exception("queryDLC must be invoked after IsLaunchedFromTapTapPC success"); } bool success = TapClientBridge.QueryDLC(skuId); return success; } /// 跳转到 TapTap 客户端 DLC 购买页面 , 未调用 isLaunchFromPC 会抛异常 public static bool ShowStore(string skuId) { if (lastIsLaunchedFromTapTapPCResult != 1) { throw new Exception("ShowStore must be invoked after IsLaunchedFromTapTapPC success"); } Log(false, "ShowStore start = " + skuId); return TapClientBridge.TapDLC_ShowStore(skuId); } /// 注册 DLC 购买状态变更回调,包括购买成功和退款 public static void RegisterDLCOwnedCallback(Action dlcDelegate) { currentDlcDelegate = dlcDelegate; TapClientBridge.RegisterCallback(TapEventID.DLCPlayableStatusChanged, DLCCallbackDelegate); } /// DLC 回调 [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))] static void DLCCallbackDelegate(int id, IntPtr userData) { Log(false, "queryDlC recevie callback " + id); if (currentDlcDelegate != null) { TapClientBridge.DLCPlayableStatusChangedResponse response = Marshal.PtrToStructure(userData); Log(false, "queryDlC callback = " + response.dlc_id + " isOwn = " + response.is_playable); currentDlcDelegate(response.dlc_id, response.is_playable != 0); } } /// 注册 License 购买状态变更回调,包括购买成功和退款 public static void RegisterLicenseCallback(Action licensecDelegate) { currentLicenseDelegate = licensecDelegate; TapClientBridge.RegisterCallback(TapEventID.GamePlayableStatusChanged, LicenseCallbackDelegate); } /// License 回调 [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))] static void LicenseCallbackDelegate(int id, IntPtr userData) { Log(false, "License recevie callback " + id); if (currentLicenseDelegate != null) { TapClientBridge.GamePlayableStatusChangedResponse response = Marshal.PtrToStructure(userData); Log(false, "License callback isOwn changed " + response.is_playable ); currentLicenseDelegate(response.is_playable != 0); } } public static bool HasLicense() { if (lastIsLaunchedFromTapTapPCResult != 1) { throw new Exception("ShowStore must be invoked after IsLaunchedFromTapTapPC success"); } return TapClientBridge.HasLicense(); } private static HashSet> taptapPCStateChangeListeners; private static volatile bool hasRegisterSystemListener = false; /// /// 设置 TapPC 客户端状态监听 /// internal static void RegisterTapTapPCStateChangeListener(Action action) { if (taptapPCStateChangeListeners == null) { taptapPCStateChangeListeners = new HashSet>(); } taptapPCStateChangeListeners.Add(action); if (lastIsLaunchedFromTapTapPCResult > 0 && !hasRegisterSystemListener) { TapClientBridge.RegisterCallback(TapEventID.SystemStateChanged,TapTapPCStateDelegate); hasRegisterSystemListener = true; } } internal static void UnRegisterTapTapPCStateChangeListener(Action action) { if (taptapPCStateChangeListeners != null && taptapPCStateChangeListeners.Count > 0) { taptapPCStateChangeListeners.Remove(action); if (taptapPCStateChangeListeners.Count == 0) { TapClientBridge.UnRegisterCallback(TapEventID.SystemStateChanged,TapTapPCStateDelegate); hasRegisterSystemListener = false; } } } [AOT.MonoPInvokeCallback(typeof(TapClientBridge.CallbackDelegate))] static void TapTapPCStateDelegate(int id, IntPtr userData) { if (id == (int)TapEventID.SystemStateChanged) { SystemStateResponse response = Marshal.PtrToStructure( userData ); if (taptapPCStateChangeListeners != null) { foreach (var listener in taptapPCStateChangeListeners) { listener(response.state); } } } } private static void Log(bool isError, string data, bool alwaysShow = true) { if (!string.IsNullOrEmpty(data)) { if (TapLogger.LogDelegate != null) { if (isError) { TapLogger.Error(data); } else { TapLogger.Debug(data); } } else { if (alwaysShow) { if (isError) { UnityEngine.Debug.LogErrorFormat($"[TapSDK] ERROR: {data}"); } else { UnityEngine.Debug.LogFormat($"[TapSDK] INFO: {data}"); } } } } } // 初始化校验结果 private class TapInitResult { internal int result; internal string errorMsg; internal bool needQuitGame = false; public TapInitResult(int result, string errorMsg) { this.result = result; this.errorMsg = errorMsg; } public TapInitResult(bool needQuitGame) { this.needQuitGame = needQuitGame; } } } #endif }