NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Framework/BI/BIManager.cs

567 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using PhxhSDK;
using System;
using System.Collections.Generic;
using UnityEngine;
public partial class BIManager : Singlenton<BIManager>, IInitable, IUpdatable
{
// 关卡属性
private BILevelInfo LevelInfo;
private IBIService _biService;
private StorageEvent _storageEvent;
private bool _isInit;
private bool sendPhxh = false;
private Dictionary<string, object> _tempEventDictionary = new(4);
#region 网络状态和发送周期相关
/// <summary>
/// 事件队列
/// </summary>
private Queue<(string, Dictionary<string, object>)> eventQueue = new();
/// <summary>
/// 是否使用队列模式(无网络或设置了发送周期)
/// </summary>
private bool _useQueueMode = false;
/// <summary>
/// 发送周期 默认1秒
/// </summary>
private float _sendInterval = 1.0f;
/// <summary>
/// 网络状态 通过心跳包检测
/// </summary>
private bool _networkAvailable = true;
/// <summary>
/// 计时器
/// </summary>
private float _timer = 0f;
#endregion
#region 基本属性
/// <summary>
/// 设备机型
/// </summary>
private string deviceInfo;
/// <summary>
/// 设备ID
/// </summary>
private string deviceID;
/// <summary>
/// 版本
/// </summary>
private string gameVersion;
/// <summary>
/// 平台
/// </summary>
private string platform;
/// <summary>
/// 渠道
/// </summary>
private string channel;
/// <summary>
/// 剩余存储空间
/// </summary>
private int storageInfo;
#endregion
#region 初始化
public void Init()
{
InitBiService();
#if BI_PHXH
sendPhxh = true;
#endif
LevelInfo = new BILevelInfo();
_storageEvent = StorageMgr.Instance.GetStorage<StorageEvent>("StorageEvent");
_storageEvent ??= new StorageEvent();
_isInit = true;
// 初始化网络状态
RegisterHeartbeatEvents();
//初次启动游戏打点
//TrackEventOnce(BI_GameStart, param: new Dictionary<string, object>() { { "event", "first_open" } });
}
/// <summary>
/// 登陆后初始化信息
/// </summary>
/// <param name="name"></param>
public void BiAfterLoginInit(SDKManager.SdkName name)
{
_biService = SDKManager.Instance.GetSdkHelper(name) as IBIService;
if (_biService == null)
{
DebugUtil.Log("Get BI Service error, SDK helper name is {0}", name);
return;
}
// 设置登陆后才有的基本属性 如用户ID
_biService.SetUser(SDKManager.Instance.ApplicationUserID);
SendQueuedEvents();
}
private void InitBiService()
{
var helperName = SDKManager.SdkName.TapTap;
#if SDK_FIREBASE
helperName = SDKManager.SdkName.FireBase;
_biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
#endif
#if SDK_TAPTAP
helperName = SDKManager.SdkName.TapTap;
_biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
#endif
#if SDK_TALKINGDATA
helperName = SDKManager.SdkName.TalkingData;
_biService = SDKManager.Instance.GetSdkHelper(helperName) as IBIService;
//TD在登录后初始化
#endif
if (_biService == null)
{
DebugUtil.Log("Get BI Service error, SDK helper name is {0}", helperName);
return;
}
// 初始化基本属性
deviceInfo = SystemInfo.deviceModel;
storageInfo = SystemInfo.systemMemorySize;
deviceID = SystemInfo.deviceUniqueIdentifier;
gameVersion = Application.version;
platform = DeviceHelper.GetPlatformString();
channel = PlayerPrefs.GetString("BI_Platform", "test");
// TODO 设置通用属性 例如 服务器
_biService?.SetUserProperty("platform", DeviceHelper.GetPlatformString());
DebugUtil.Log("device platform:{0}", platform);
#if SDK_TALKINGDATA
_biService?.SetUserProperty(TalkingDataProfileParamName.Param1, deviceInfo);
_biService?.SetUserProperty(TalkingDataProfileParamName.Param2, storageInfo.ToString() + "MB");
#endif
}
/// <summary>
/// 注册心跳包相关事件,用于检测网络状态
/// </summary>
private void RegisterHeartbeatEvents()
{
// 注册心跳包响应事件
Framework.EventManager.Instance.Register(Framework.EventManager.EventName.NetHandshakeDone,
(Action<uint>)OnNetworkConnected);
Framework.EventManager.Instance.Register(Framework.EventManager.EventName.NetError,
(Action<uint>)OnNetworkError);
// 初始状态假设网络可用,但使用队列模式
_networkAvailable = true;
_useQueueMode = _sendInterval > 0;
}
/// <summary>
/// 设置用户属性
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void SetUserProperty(string key, string value)
{
if (!_isInit)
return;
_biService?.SetUserProperty(key, value);
}
#endregion
#region 释放
public void Release()
{
_tempEventDictionary = null;
// 在释放时尝试发送剩余的队列事件
if (_isInit && _networkAvailable && eventQueue.Count > 0 && _biService != null)
{
SendQueuedEvents();
}
// 注销事件监听
Framework.EventManager.Instance.Unregister(Framework.EventManager.EventName.NetHandshakeDone,
(Action<uint>)OnNetworkConnected);
Framework.EventManager.Instance.Unregister(Framework.EventManager.EventName.NetError,
(Action<uint>)OnNetworkError);
}
#endregion
#region 周期发送
public void Update(float deltaTime)
{
if (!_isInit)
return;
// 如果队列中有事件并且有网络连接
if (eventQueue.Count > 0 && _networkAvailable)
{
// 如果使用周期发送模式
if (_sendInterval > 0)
{
_timer += Time.deltaTime;
// 达到发送周期
if (_timer >= _sendInterval)
{
_timer = 0;
SendQueuedEvents();
}
}
// 如果不使用周期模式(立即发送)但是之前网络不可用导致有队列
else
{
SendQueuedEvents();
}
}
}
/// <summary>
/// 网络连接成功
/// </summary>
private void OnNetworkConnected(uint netID)
{
_networkAvailable = true;
_useQueueMode = _sendInterval > 0; // 如果有设置发送周期,仍然使用队列模式
// 如果不使用队列模式,立即发送所有队列中的事件
if (!_useQueueMode && eventQueue.Count > 0)
{
SendQueuedEvents();
}
}
/// <summary>
/// 网络连接错误
/// </summary>
private void OnNetworkError(uint netID)
{
_networkAvailable = false;
_useQueueMode = true; // 网络错误时启用队列模式
}
/// <summary>
/// 设置事件发送周期
/// </summary>
/// <param name="interval">发送周期单位秒设为0则立即发送</param>
public void SetSendInterval(float interval)
{
_sendInterval = Mathf.Max(0, interval);
_useQueueMode = !_networkAvailable || _sendInterval > 0;
}
#endregion
#region 发送事件
/// <summary>
/// 发送队列中的所有事件
/// </summary>
private void SendQueuedEvents()
{
if (_biService == null || !_networkAvailable)
return;
while (eventQueue.Count > 0)
{
//将缓存的事件上报
var (eventName, param) = eventQueue.Dequeue();
_biService.ReportEvent(eventName, param);
TrackEventPhxh(eventName, param);
}
}
/// <summary>
/// 用于传输参数自定义命名事件 (普通事件)
/// </summary>
/// <param name="eventName"></param>
/// <param name="param"></param>
public void TrackEvent(string eventName, Dictionary<string, object> param)
{
if (!_isInit)
return;
try
{
// 使用队列模式或者服务未初始化时,暂存事件
if (_useQueueMode || _biService == null)
{
eventQueue.Enqueue((eventName, param));
}
else
{
_biService.ReportEvent(eventName, param);
TrackEventPhxh(eventName, param);
}
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
/// <summary>
/// 上报普通事件 TD有在用 谨慎删除
/// </summary>
public void TrackEvent(string eventName, string param1 = null, string param2 = null, string param3 = null)
{
if (!_isInit)
return;
try
{
_tempEventDictionary.Clear();
var userParam = _tempEventDictionary;
if (!string.IsNullOrEmpty(param1))
{
userParam.Add("param1", param1);
}
if (!string.IsNullOrEmpty(param2))
{
userParam.Add("param2", param2);
}
if (!string.IsNullOrEmpty(param3))
{
userParam.Add("param3", param3);
}
if (_biService != null)
{
_biService.ReportEvent(eventName, userParam);
}
else
{
eventQueue.Enqueue((eventName, userParam));
}
_tempEventDictionary.Clear();
}
catch (Exception e)
{
DebugUtil.LogError("BI Event: {0}, TrackEvent Error: {1}", eventName, e);
}
}
/// <summary>
/// 用于传输参数自定义命名事件 (一次性事件)
/// </summary>
/// <param name="firstEventName"></param>
/// <param name="param"></param>
public void TrackEventOnce(string firstEventName, Dictionary<string, object> param)
{
if (!_isInit)
return;
try
{
if (IsRecorded(firstEventName))
{
DebugUtil.Log("已经上报过 {0} 事件, 不会再次上报", firstEventName);
return;
}
_storageEvent.EventList.Add(firstEventName);
// 使用队列模式或者服务未初始化时,暂存事件
if (_useQueueMode || _biService == null)
{
eventQueue.Enqueue((firstEventName, param));
}
else
{
_biService.ReportEvent(firstEventName, param);
}
StorageMgr.Instance.SyncForce = true;
}
catch (Exception e)
{
DebugUtil.LogError("BI Event: {0}, TrackEventOnce Error: {1}", firstEventName, e);
}
}
/// <summary>
/// 上报关卡内普通事件
/// </summary>
public void TrackEventLevel(string eventName, BILevelInfo levelInfo, Dictionary<string, object> normalParam)
{
if (!_isInit)
return;
try
{
LevelInfo.Clone(levelInfo);
var levelParam = PhxhSDK.Utils.PropertiesNameValue2ObjectDictionary(LevelInfo);
foreach (var param in normalParam)
{
levelParam.Add(param.Key, param.Value);
}
// 使用队列模式或者服务未初始化时,暂存事件
if (_useQueueMode || _biService == null)
{
eventQueue.Enqueue((eventName, levelParam));
}
else
{
_biService.ReportEvent(eventName, levelParam);
TrackEventPhxh(eventName, levelParam);
}
}
catch (Exception e)
{
DebugUtil.LogError("BI Event: {0}, TrackEventLevel Error: {1}", eventName, e);
}
}
/// <summary>
/// 上报关卡内一次性事件
/// </summary>
public void TrackEventLevelOnce(string firstEventName, BILevelInfo levelInfo,
Dictionary<string, object> normalParam)
{
if (!_isInit)
return;
try
{
if (IsRecorded(firstEventName))
{
DebugUtil.Log("已经上报过 {0} 关卡事件,不会再次上报", firstEventName);
return;
}
LevelInfo.Clone(levelInfo);
var levelParam = PhxhSDK.Utils.PropertiesNameValue2ObjectDictionary(LevelInfo);
foreach (var param in normalParam)
{
levelParam.Add(param.Key, param.Value);
}
_storageEvent.EventList.Add(firstEventName);
// 使用队列模式或者服务未初始化时,暂存事件
if (_useQueueMode || _biService == null)
{
eventQueue.Enqueue((firstEventName, levelParam));
}
else
{
_biService.ReportEvent(firstEventName, levelParam);
}
StorageMgr.Instance.SyncForce = true;
}
catch (Exception e)
{
DebugUtil.LogError("BI Event: {0}, TrackEventLevelOnce Error: {1}", firstEventName, e);
}
}
private bool IsRecorded(string eventName)
{
return _storageEvent.EventList.Contains(eventName);
}
#endregion
#region PHXH 服务器打点
private void TrackEventPhxh(string eventName, Dictionary<string, object> param)
{
if (!_isInit || !sendPhxh)
return;
try
{
string
url = PostHeader.PostUrl + "bi/events"; //"http://bi.kedrgame.com/api/v1/events"; //192.168.2.135:17777
string data = JsonConvert.SerializeObject(param);
var sendData = new PhxhBIEvent();
sendData.event_name = eventName;
sendData.event_key = eventName;
if (param != null)
sendData.event_value = data;
sendData.actor_id = SDKManager.Instance.ApplicationUserID;
sendData.model = deviceInfo;
sendData.uuid = deviceID;
sendData.version = gameVersion;
sendData.platform = platform;
sendData.channel = channel;
Dictionary<string, string> headData = PostHeader.PostHeaderInfo(new Dictionary<string, string>
{
{ "event_name", sendData.event_name }, //string
{ "event_key", sendData.event_key },
{ "event_value", sendData.event_value },
{ "tag", sendData.tag },
{ "game_id", sendData.game_id.ToString() },
{ "actor_id", sendData.actor_id }, //string
{ "details", sendData.details }, //json
{ "uuid", sendData.uuid }, //string
{ "model", sendData.model }, //string
{ "version", sendData.version }, //string
{ "platform", sendData.platform }, //string
{ "channel", sendData.channel }, //string
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() },
});
HttpHelper.PostAsync(url, sendData, headData).Forget();
}
catch (Exception e)
{
DebugUtil.LogError("Track event fail! EventName:" + eventName + ",\n error msg: " + e.Message);
}
}
private class PhxhBIEvent
{
public string event_name; // 标记类型
public string event_key;
public string event_value;
public string actor_id;
public string details;
public string version; // 游戏版本
public string tag; // tag: 区分多游戏
public string platform; // 平台安卓和IOS
public string model; // 手机型号
public string uuid; // 设备唯一标识
public string channel; // 渠道
public int game_id;
}
#endregion
}