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";
}
}