296 lines
9.1 KiB
C#
296 lines
9.1 KiB
C#
using asap.core;
|
||
using cfg;
|
||
using game;
|
||
using GameCore;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
|
||
public class BanServerConfig
|
||
{
|
||
public bool AntiCheatOn { get; set; }
|
||
public Dictionary<int, List<int>> AntiCheatParam { get; set; }
|
||
public List<int> GetParam(int key)
|
||
{
|
||
if (AntiCheatParam != null && AntiCheatParam.ContainsKey(key))
|
||
{
|
||
return AntiCheatParam[key];
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
public class AntiCheatRequest
|
||
{
|
||
public string playerID { get; set; }
|
||
public int banType { get; set; }
|
||
public string deviceId { get; set; }
|
||
public bool gm { get; set; }
|
||
}
|
||
public class AntiCheatResponse
|
||
{
|
||
public string playerID { get; set; }
|
||
public int banTypeCode { get; set; } // 封禁类型编码(如101)
|
||
public bool isBanned { get; set; }
|
||
}
|
||
|
||
|
||
public class AntiCheatManager
|
||
{
|
||
[Inject]
|
||
public IUserService userService { get; set; }
|
||
[Inject]
|
||
public ICustomServerMgr customServerMgr { get; set; }
|
||
bool IsAnti = false;
|
||
string DeviceId
|
||
{
|
||
get
|
||
{
|
||
#if UNITY_IOS
|
||
return UnityEngine.iOS.Device.advertisingIdentifier;
|
||
#elif UNITY_ANDROID
|
||
return SystemInfo.deviceUniqueIdentifier;
|
||
#endif
|
||
}
|
||
}
|
||
#region 行为检测核心方法
|
||
// 检测登录/钓鱼行为(101)检查是否已经被封
|
||
// 获取服务器表配置
|
||
public async Task CheckLoginPhishingBehavior()
|
||
{
|
||
if (tbServerConfig == null)
|
||
{
|
||
try
|
||
{
|
||
var json = await customServerMgr.GetStringAsync("anti-cheat/GetConfigRequest");
|
||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||
{
|
||
tbServerConfig = Newtonsoft.Json.JsonConvert.DeserializeObject<BanServerConfig>(json);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
Debug.LogWarning("[AntiCheat] Failed to fetch server config.");
|
||
}
|
||
}
|
||
if (IsAnti)
|
||
{
|
||
return;
|
||
}
|
||
if (tbServerConfig == null)
|
||
{
|
||
return;
|
||
}
|
||
bool AntiCheatOn = tbServerConfig.AntiCheatOn;
|
||
if (!AntiCheatOn)
|
||
{
|
||
return;
|
||
}
|
||
|
||
int buildCount = GetDailyBuildCount();
|
||
int vip = GContext.container.Resolve<PlayerData>().vip;
|
||
int maxLevel = GContext.container.Resolve<Tables>().TbMapData.DataList[^1].LevelRequired;
|
||
int level = GContext.container.Resolve<PlayerData>().lv;
|
||
if (level > maxLevel)
|
||
{
|
||
//建造等级超出已解锁地图的最大建造等级-303
|
||
TriggerBanEvent(303);
|
||
}
|
||
else
|
||
{
|
||
//0付费,vip等级>=5 -101
|
||
float payAmount = GContext.container.Resolve<PlayerShopData>().GetBuyData();
|
||
var Param = tbServerConfig.GetParam(101);
|
||
if (Param != null && Param.Count == 2
|
||
&& payAmount <= Param[0]
|
||
&& vip >= Param[1])
|
||
{
|
||
TriggerBanEvent(101);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检测道具获取行为(201/202/203)
|
||
public void CheckItemChangeBehavior(int id, int count)
|
||
{
|
||
if (tbServerConfig == null)
|
||
{
|
||
return;
|
||
}
|
||
bool AntiCheatOn = tbServerConfig.AntiCheatOn;
|
||
if (!AntiCheatOn)
|
||
{
|
||
return;
|
||
}
|
||
float payAmount = GContext.container.Resolve<PlayerShopData>().GetBuyData();
|
||
int createRoleDays = (int)((DateTime.UtcNow.Ticks - userService.CreateTime.Ticks) / TimeSpan.TicksPerDay);
|
||
|
||
//玩家行为 item_change
|
||
if (id == 1001)
|
||
{
|
||
//付费为0,距离创角小于等于7天,单次获取5000及以上鱼钩-201
|
||
var Param = tbServerConfig.GetParam(201);
|
||
if (Param != null && Param.Count == 3
|
||
&& payAmount <= Param[0]
|
||
&& createRoleDays <= Param[1]
|
||
&& count >= Param[2])
|
||
{
|
||
TriggerBanEvent(201);
|
||
return;
|
||
}
|
||
//付费为0,单次获取15000及以上鱼钩-202
|
||
Param = tbServerConfig.GetParam(202);
|
||
if (Param != null && Param.Count == 2
|
||
&& payAmount <= Param[0]
|
||
&& count >= Param[1])
|
||
{
|
||
TriggerBanEvent(202);
|
||
}
|
||
}
|
||
else if (id == 1005)
|
||
{
|
||
//付费为0,单次获取5000及以上钻石 - 203
|
||
var Param = tbServerConfig.GetParam(203);
|
||
if (Param != null && Param.Count == 2
|
||
&& payAmount <= Param[0]
|
||
&& count >= Param[1])
|
||
{
|
||
TriggerBanEvent(203);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检测建造行为(301/302/303/304)
|
||
public void CheckBuildBehavior(int addLevel)
|
||
{
|
||
if (tbServerConfig == null)
|
||
{
|
||
return;
|
||
}
|
||
bool AntiCheatOn = tbServerConfig.AntiCheatOn;
|
||
if (!AntiCheatOn)
|
||
{
|
||
return;
|
||
}
|
||
float payAmount = GContext.container.Resolve<PlayerShopData>().GetBuyData();
|
||
int createRoleDays = (int)((DateTime.UtcNow.Ticks - userService.CreateTime.Ticks) / TimeSpan.TicksPerDay);
|
||
int buildCount = GetDailyBuildCount();
|
||
int level = GContext.container.Resolve<PlayerData>().lv;
|
||
//付费为0,距离创角<10天,建造等级达到10000及以上-302
|
||
var Param = tbServerConfig.GetParam(302);
|
||
if (Param != null && Param.Count == 3
|
||
&& payAmount <= Param[0]
|
||
&& createRoleDays <= Param[1]
|
||
&& level >= Param[2])
|
||
{
|
||
TriggerBanEvent(302);
|
||
return;
|
||
}
|
||
//付费为0,单日build_upgrade1000次及以上-301
|
||
Param = tbServerConfig.GetParam(301);
|
||
if (Param != null && Param.Count == 2
|
||
&& payAmount <= Param[0]
|
||
&& level - buildCount >= Param[1])
|
||
{
|
||
TriggerBanEvent(301);
|
||
return;
|
||
}
|
||
|
||
//前后两次建造building times差值大于1(第二次减第一次大于1)-304
|
||
Param = tbServerConfig.GetParam(304);
|
||
if (Param != null && Param.Count == 1
|
||
&& addLevel > Param[0])
|
||
{
|
||
TriggerBanEvent(304);
|
||
return;
|
||
}
|
||
}
|
||
|
||
int GetDailyBuildCount()
|
||
{
|
||
string key = $"BuildCount_{userService.UserId}";
|
||
string dayBuildData = PlayerPrefs.GetString(key, "");
|
||
var currentDays = (DateTime.UtcNow.Date - new DateTime(2026, 1, 1)).TotalDays;
|
||
if (!string.IsNullOrEmpty(dayBuildData))
|
||
{
|
||
var parts = dayBuildData.Split('_');
|
||
if (parts.Length == 2)
|
||
{
|
||
double savedDays;
|
||
if (double.TryParse(parts[0], out savedDays))
|
||
{
|
||
int savedLevel;
|
||
if (int.TryParse(parts[1], out savedLevel))
|
||
{
|
||
if (savedDays == currentDays)
|
||
{
|
||
//同一天,返回保存的建造次数
|
||
return savedLevel;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
int level = GContext.container.Resolve<PlayerData>().lv;
|
||
PlayerPrefs.SetString(key, $"{currentDays}_{level}");
|
||
//获取单日建造次数逻辑
|
||
return level;
|
||
}
|
||
BanServerConfig tbServerConfig = null;
|
||
void TriggerBanEvent(int banType)
|
||
{
|
||
if (IsAnti || banType == 0)
|
||
{
|
||
return;
|
||
}
|
||
OnBan("anti-cheat/SendClientBan", banType);
|
||
}
|
||
public void CheckBan()
|
||
{
|
||
if (IsAnti)
|
||
{
|
||
return;
|
||
}
|
||
OnBan("anti-cheat/CheckBan", 0);
|
||
}
|
||
async void OnBan(string url, int banType)
|
||
{
|
||
IConfig config = GContext.container.Resolve<IConfig>();
|
||
AntiCheatRequest evt = new AntiCheatRequest
|
||
{
|
||
playerID = userService.UserId,
|
||
banType = banType,
|
||
deviceId = DeviceId,
|
||
gm = config != null && (config.Get("DEBUG", "0") == "1" || config.Get("GM", "0") == "1")
|
||
};
|
||
string json = await customServerMgr.CustomServerPost(url, Newtonsoft.Json.JsonConvert.SerializeObject(evt));
|
||
if (IsAnti)
|
||
{
|
||
return;
|
||
}
|
||
if (!string.IsNullOrEmpty(json) && json != "[]")
|
||
{
|
||
AntiCheatResponse response = Newtonsoft.Json.JsonConvert.DeserializeObject<AntiCheatResponse>(json);
|
||
Debug.Log($"[AntiCheat] PlayerID: {response.playerID} DeviceId: {DeviceId} banned for reason: {response.banTypeCode}");
|
||
|
||
#if !UNITY_EDITOR
|
||
if (response.banTypeCode > 0)
|
||
{
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("Anti_cheating"))
|
||
{
|
||
e.AddContent("type", response.banTypeCode)
|
||
.AddContent("deviceId", DeviceId)
|
||
.AddContent("is_first_ban", !response.isBanned);
|
||
}
|
||
#endif
|
||
IsAnti = true;
|
||
await UIManager.Instance.ShowUI(UITypes.AccountAbnormalityPopupPanel);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
#endregion
|
||
}
|