using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using AOT; using NetEase; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NTESHTPSec; using UnityEngine; using UnityEngine.Android; using AntiCheatResult = NetEase.AntiCheatResult; using RequestCmdID = NetEase.RequestCmdID; // using AntiCheatResult = NTESHTPSec.AntiCheatResult; namespace YiDun.Protector { public abstract class YiDunManager { private static YiDunManager _instance; private static readonly object Lock = new object(); public static YiDunManager Instance { get { lock (Lock) { if (_instance == null) { // 创建具体实现类的实例 _instance = CreateConcreteInstance(); if (_instance != null) { Debug.Log($"[YiDunManager] : {_instance.GetType().Name}"); } else { Debug.LogError("[YiDunManager] Failed!"); } } return _instance; } } } private static YiDunManager CreateConcreteInstance() { // 这里可以根据平台或其他条件返回不同的具体实现 #if UNITY_ANDROID && !UNITY_EDITOR return new YiDunAndroidManager(); #elif UNITY_IOS && !UNITY_EDITOR return new YiDunIOSManager(); #else return new DefaultYiDunManager(); #endif } // private string ProductId { get; set; } private string BusinessId { get; set; } private string Token { get; set; } private string Account { get; set; } /// /// 销毁单例实例(主要用于测试) /// public static void DestroyInstance() { lock (Lock) { if (_instance != null) { _instance.Cleanup(); _instance = null; Debug.Log("[YiDunManager] 单例实例已销毁"); } } } public void Cleanup() { } // 标记 // public static string PlatformFlag = ""; public abstract string PlatformFlag { get; } // private HTProtectConfig _config; public delegate void OutAction(out T result); public void Init(string productId, YiDunHtpCallback yiDunHtpCallback) { Debug.Log($"Init-> {PlatformFlag} {productId}"); ProductId = productId; #if UNITY_ANDROID && !UNITY_EDITOR NetSecProtect.init(productId, yiDunHtpCallback, _config); #elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR // NTESRiskSecProtect.init(productId, (code,message,content ) => // { // Debug.Log($"init callback -> {productId} {code} {message} {content}"); // // yiDunHtpCallback.onReceive(code,message); // NTESRiskSecProtect.ioctl(NTESHTPSec.RequestCmdID.Cmd_SetConfigData,message); // }); NTESRiskSecProtect.setServerType(_config.getServerType()); NTESRiskSecProtect.setChannel(_config.getChannel()); NTESRiskSecProtect.InitCallback initCallback = new NTESRiskSecProtect.InitCallback(InitProductIdCallback); NTESRiskSecProtect.init(productId, initCallback); #endif } [MonoPInvokeCallback(typeof(NTESRiskSecProtect.InitCallback))] public static void InitProductIdCallback(int code, string message, string content) { Debug.Log($"init callback -> {code} {message} {content}"); // code返回200说明初始化成功 NTESRiskSecProtect.ioctl(NTESHTPSec.RequestCmdID.Cmd_SetConfigData,message); } // public YiDunManager InitConfig(OutAction act) { act?.Invoke(out _config); #if (UNITY_ANDROID) && !UNITY_EDITOR Debug.Log($"InitConfig() Android"); #elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR Debug.Log($"InitConfig() IOS"); NTESRiskSecProtect.setChannel(_config.getChannel()); #else Debug.Log($"InitConfig() Default"); #endif return this; } // 获取超时时间 3000秒 private int timeOut = 3000; private int _tryTimesForGetToken = 0; private int MaxTimesForGetToken = 3; private bool IsTokenGetProcessed = false; private TaskCompletionSource _taskCompletionForGetToken; public async Task StartGetToken() { // Log("StartGetToken-> "); _tryTimesForGetToken = 0; _taskCompletionForGetToken = new TaskCompletionSource(); await TryGetToken(); return await _taskCompletionForGetToken.Task; } private async Task TryGetToken() { // Log("TryGetToken()-> "); var task = new TaskCompletionSource(); GetTokenAsync(timeOut,BusinessId, new YiDunGetTokenCallback( (code, message) => { if (code == AntiCheatResult.OK) { Debug.Log($"GetToken Succeed In {_tryTimesForGetToken} times"); Token = message; FinishGetToken(); } task.TrySetResult(true); })); await task.Task; if (IsTokenGetProcessed) { return; } if (_tryTimesForGetToken >= MaxTimesForGetToken) { return; } await TryNextGetToken(); } private void FinishGetToken() { Log("FinishGetToken "); IsTokenGetProcessed = true; var bRet = _taskCompletionForGetToken.TrySetResult(true); if (!bRet) { Log("FinishGetToken-> Failed"); } } private async Task TryNextGetToken() { _tryTimesForGetToken++; Log($"TryNextGetToken For {_tryTimesForGetToken} times"); await TryGetToken(); } // public int SetRoleInfo(string businessId, string roleId, string roleName, string roleAccount, string roleServer, int serverId, string gameJson) { BusinessId = businessId; Account = roleAccount; Log($"SetRoleInfo ->{businessId},{roleId} {roleName} {roleAccount} {roleServer} {serverId} {gameJson}"); #if (UNITY_ANDROID ) && !UNITY_EDITOR return NetSecProtect.setRoleInfo(businessId, roleId, roleName, roleAccount, roleServer, serverId, gameJson); #elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR return NTESRiskSecProtect.setRoleInfo(businessId, roleId, roleName, roleAccount, roleServer, serverId, gameJson); #endif return 0; } // public void LogOut() { #if (UNITY_ANDROID ) && !UNITY_EDITOR NetSecProtect.logOut(); #elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR NTESRiskSecProtect.logOut(); #endif Debug.Log("LogOut"); } // public void GetTokenAsync(int timeOut, string businessId, YiDunGetTokenCallback callback) { #if (UNITY_ANDROID ) && !UNITY_EDITOR NetSecProtect.getTokenAsync(timeOut, businessId, callback); #elif (UNITY_IOS || UNITY_IPHONE) && !UNITY_EDITOR // NTESRiskSecProtect.getTokenAsync(timeOut,businessId, (token, code, codeStr, businessId) => NTESRiskSecProtect.getTokenAsync(timeOut,businessId,(result) => { callback.onResult(new AntiCheatResult(result.code,result.codeStr,result.token,businessId) ); }); #else callback.onResult(new AntiCheatResult(200,"token","simulator_token",businessId)); #endif Log($"GetTokenAsync-> {timeOut} {businessId}"); } /** * * 拼接组装参与计算的params参数示例 * 不同接口校验参数不同,详见目录下的demo文件: https://github.com/yidun/irisk-openapi-demo/blob/main/irisk-openapi-csharp-demo */ private static Dictionary GetParamsMap(RiskRequest riskRequest) { var parameters = new Dictionary { // { "productId", riskRequest.productId }, { "secretId", riskRequest.secretId }, { "businessId", riskRequest.businessId }, { "timestamp", riskRequest.timestamp }, { "nonce", riskRequest.nonce }, { "version", riskRequest.version }, { "token", riskRequest.token }, { "account", riskRequest.account }, { "ip", riskRequest.ip }, { "sceneData", riskRequest.sceneData } }; return parameters; } // 获取签名 public static string GenSignature(String secretKey, Dictionary parameters) { parameters = parameters.OrderBy(o => o.Key, StringComparer.Ordinal).ToDictionary(o => o.Key, p => p.Value); var builder = new StringBuilder(); foreach (var kv in parameters) { builder.Append(kv.Key).Append(kv.Value); } builder.Append(secretKey); var tmp = builder.ToString(); MD5 md5 = new MD5CryptoServiceProvider(); var result = md5.ComputeHash(Encoding.UTF8.GetBytes(tmp)); builder.Clear(); foreach (var b in result) { builder.Append(b.ToString("x2").ToLower()); } return builder.ToString(); } public YiDunManager BindTo(GameObject gameObject) { #if (UNITY_ANDROID && !UNITY_EDITOR) if (!gameObject.GetComponent()) { gameObject.AddComponent(); } #elif (UNITY_IOS|| UNITY_IPHONE) && !UNITY_EDITOR if (!gameObject.GetComponent()) { gameObject.AddComponent(); } #endif return this; } private string CheckURL = "http://ir-open.dun.163.com/v6/risk/check"; // private string ContentType = "Content-Type:application/json"; private string SecretId = "7992abe1b4c13dddf09b6d5e6614f790"; private string SecretKey = "513123689ad874638be2ab02edd9c826"; public class RiskRequest { // public string productId; public string businessId; public string secretId; public string timestamp; // public readonly string nonce = Guid.NewGuid().ToString("N"); public readonly string version = "603"; public string signature; public string token; public string account; // public string roleId; // public string nickname; // public string phone; // public string email; // public long registerTime; // 注册事件,可不填 public readonly string ip = "unknown"; public string sceneData; } private const int TIME_DELTA = 60 * 2; private DateTime _riskTime = DateTime.MinValue; private bool CheckRiskEnable() { var timeSpan = DateTime.UtcNow - _riskTime; // Log($"CheckRiskEnable() -> {timeSpan.TotalSeconds} > {TIME_DELTA} = {timeSpan.TotalSeconds > TIME_DELTA}"); return timeSpan.TotalSeconds > TIME_DELTA; } private async void DoRiskCheck(string sceneData) { if (!CheckRiskEnable() ) return ; _riskTime = DateTime.UtcNow; Log("DoRiskCheck-> "); using (var client = new HttpClient()) { try { client.Timeout = TimeSpan.FromSeconds(15); var riskRequest = new RiskRequest() { // productId = ProductId, businessId = BusinessId, secretId = SecretId, timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(), token = Token, account = Account, sceneData = sceneData }; // var json = JsonConvert.SerializeObject(riskRequest); riskRequest.signature = GenSignature(SecretKey, GetParamsMap(riskRequest)); var content = JsonConvert.SerializeObject(riskRequest); var stringContent = new StringContent(content, Encoding.UTF8, "application/json"); var response = await client.PostAsync(CheckURL, stringContent); var result = await response.Content.ReadAsStringAsync(); if (result != null) { var ret = JObject.Parse(result); var code = ret.GetValue("code")?.ToObject(); var msg = ret.GetValue("msg")?.ToObject(); if (code == 200) { Debug.Log($"SUCCESS: code={code}, msg={msg}"); } else { Debug.Log($"ERROR: code={code}, msg={msg}"); } } } catch (Exception ex) { // ignored Debug.LogError(ex.Message + "\n" + ex.StackTrace); } finally { Token = string.Empty; } } } // 定时器,定时发送RiskCheck public async void PostRiskCheck(string sceneData = "{ }") { Log("PostRiskCheck()-> "); if (string.IsNullOrEmpty(Token)) { Log("token is Invalid - trying Get"); await StartGetToken(); if (string.IsNullOrEmpty(Token)) return; } DoRiskCheck(sceneData); } private static void Log(string message) { Debug.Log($" YiDunManager => {message} "); } } public class YiDunIOSManager : YiDunManager { public override string PlatformFlag => "iOS"; } // 方便测试 public class DefaultYiDunManager : YiDunManager { public override string PlatformFlag => "defaut"; } public class YiDunAndroidManager : YiDunManager { public override string PlatformFlag => "Android"; } }