using CSharpUtil;
using GameFramework.Network;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityGameFramework.Runtime;
///
/// 异步版本HTTP请求.
/// author: gwang
/// version:
/// 2.0.0 队列逻辑修改, 不再用任务等待. 2022.9.7
/// 1.0.0 task等待异步版. 2022.8.15
///
public class HttpHelper : MonoSingleton
{
HttpClient m_client = null;
// 发送队列
private ConcurrentQueue mPacketQueue = new ConcurrentQueue();
///
/// 接收队列
///
private ConcurrentDictionary mRecvQueue = new ConcurrentDictionary();
private volatile int mRecvPackSN = 0;
private volatile int mDN = -1;
private string m_url = Config_URL.Server_URL; // 服务器地址
private HttpHelper()
{
#if UNITY_EDITOR
bool useProxy = false;
if (useProxy)
{
var msghandler = new HttpClientHandler();
msghandler.UseProxy = true;
msghandler.Proxy = new WebProxy("127.0.0.1", 8888);
m_client = new HttpClient(msghandler);
}
else
{
#endif
m_client = new HttpClient();
#if UNITY_EDITOR
}
#endif
m_client.MaxResponseContentBufferSize = 256000000; // 最大返回的字节数
m_client.Timeout = TimeSpan.FromMilliseconds(2500); // 超时时间2.5秒
}
// 回调方法
public delegate void OnNetBack(object eventObj);
private string Get(string url)
{
try
{
var responseString = m_client.GetStringAsync(url);
return responseString.Result;
}
catch (Exception ex)
{
return null;
}
}
private string Post(string url, string strJson) => PostAsync(url, strJson).Result; // post同步请求方法
private async Task PostAsync(string url, string strJson) // post异步请求方法
{
try
{
byte[] data = CompressUtil.Deflate(GlobalConfig.Encoding.GetBytes(strJson)); // 自定义编码(deflate+utf8)
HttpContent content = new ByteArrayContent(data);
HttpResponseMessage res = await m_client.PostAsync(url, content); // 由HttpClient发出异步Post请求
if (res.StatusCode == HttpStatusCode.OK)
{
var bytes = res.Content.ReadAsByteArrayAsync().Result;
string decode = CompressUtil.InFlate(bytes, GlobalConfig.Encoding); // 自定义解码(defalte+utf8)
return decode;
}
else
{
if (res.StatusCode == HttpStatusCode.RequestTimeout) // 请求超时
{
LogHelper.LogWarning("HTTP Request time out!");
}
var resp = new RespVo() { err = (int)HttpStatusCode.RequestTimeout, tag = new JObject() };
resp.tag["errmsg"] = "请求超时.请检查网络环境!";
return JsonConvert.SerializeObject(resp);
}
}
catch (Exception ex)
{
Debug.LogException(ex);
var resp = new RespVo() { err = (int)HttpStatusCode.SeeOther, tag = new JObject() };
resp.tag["errmsg"] = "网络失败! 请检查网络环境!";// + ex.Message;
return JsonConvert.SerializeObject(resp);
}
}
///
/// 异步发送请求
///
/// 回调函数参数类型
/// 请求参数
/// 回调函数
public async void SendMsg(ReqVo param, Action callback)
{
await Task.Run(async () =>
{
var netPacket = NetPacket.CreateNew(param, callback);
var ctx = JsonConvert.SerializeObject(netPacket.request);
if (GlobalConfig.GAME_COMM_DEBUG)
{
var msg = ctx.Length > 1024 ? $"{ctx[..150]}...{ctx[^30..]}" : ctx;
LogHelper.Log($"[SEND:]{msg}");
}
string res = await this.PostAsync(m_url, ctx);
netPacket.tsResp = DateTimeOffset.Now;
if (res == null)
{
netPacket.response = new RespVo() { err = ErrCode.net_other };
}
else
{
if (GlobalConfig.GAME_COMM_DEBUG)
{
string format = res;
var msg = format.Length > 1024 ? format[..150] + $"...({format.Length / 1024:.0kb})..." + format[^30..] : format; // 约简日志长度
var sp = (netPacket.tsResp - netPacket.tsRequest).TotalMilliseconds; // 计算往返耗时
LogHelper.Log($"[RECV:]({sp:.00}ms){msg}");
}
RespVo data = JsonConvert.DeserializeObject(@res); // 返回的json数据解析成结构体
netPacket.response = data;
}
if (!mRecvQueue.TryAdd(netPacket.request.SN, netPacket))
{
LogHelper.LogWarning($"收到重复响应包!{netPacket.request.SN}");
}
//LogHelper.Log($"放入{netPacket.request.SN}");
});
}
private void Update()
{
while (mRecvQueue.TryRemove(mRecvPackSN, out var netPacket))
{
RaiseResult(netPacket);
mRecvPackSN++;
//LogHelper.Log($"放出 SN: {netPacket.request.SN}");
// 处理失败, 再处理
}
}
///
/// 发送返回结果事件
///
///
///
private void RaiseResult(NetPacket packet)
{
if (packet.errNum == 0)
{
var resp = packet.response;
if (resp.err == ErrCode.succeed)
{
if (!(packet.request.cmd == CmdCode.cmd_user_loginuserinfo || packet.request.cmd == CmdCode.cmd_user_registerNewRole)
&& resp.result.TryGetValue("store", out var store))
{
if (mDN <= resp.DN)
{
mDN = resp.DN;
UserProxy.Instance.player.InitFromStore((JObject)store);
}
else
{
LogHelper.LogWarning("DN版本低, 丢弃数据.");
}
}
packet.handler?.Invoke(); // 先执行结果回调
EventComponent eventCmpt = GameEntry.GetComponent();
//if (null != eventCmpt)
//{
resp.events.ForEach(ev =>
{
switch (ev.name)
{
case Enum_EventType.TaskCardFinished: // 任务卡完成dwddd
eventCmpt?.FireNow(this, new TaskCardEventFinish(ev.arg1, ev.arg2));
break;
case Enum_EventType.TaskCardActived: // 任务卡激活
eventCmpt?.FireNow(this, new TaskCardEventAtive(ev.arg1, ev.arg2));
break;
case Enum_EventType.TaskCardReward: // 任务卡领取奖励
eventCmpt?.FireNow(this, new TaskCardEventReward(ev.arg1, ev.arg2));
break;
case Enum_EventType.MissionStepComplete: // 任务卡-任务步骤完成
eventCmpt?.FireNow(this, new TaskEventStepFinish(ev.arg1, ev.arg2));
break;
case Enum_EventType.MissionStepProcess: // 任务卡 - 进度更新
eventCmpt?.FireNow(this, new TaskEventStepProcess(ev.arg1, ev.arg2));
break;
case Enum_EventType.AddItem: // 获得道具
eventCmpt?.FireNow(this, new User_AddItemEvent(ev.arg1, ev.arg2));
break;
case Enum_EventType.RemoveItem: // 移除道具
break;
case Enum_EventType.AddTaskItem: // 添加任务卡
eventCmpt?.FireNow(this, new TaskCardEventAdd(ev.arg1, ev?.arg2 ?? null));
break;
case Enum_EventType.RemoveTaskItem: // 移除任务卡
break;
case Enum_EventType.StartPlot: // 开启剧情对话
eventCmpt?.FireNow(this, new StartPlotScene(Convert.ToInt32(ev.arg1), Convert.ToInt32(ev.arg2)));
break;
case Enum_EventType.NpcDialog: // 开启NPC对话
eventCmpt?.FireNow(this, new StartPlotNPC(Convert.ToInt32(ev.arg1), Convert.ToInt32(ev.arg2)));
break;
case Enum_EventType.UnlockBuild:
UserProxy.Instance.player.PrivateState.unlockedBuild.Add(Convert.ToInt32(ev.arg1)); // 同步已解锁建筑id
BuildManager.UnlockBuild(Convert.ToInt32(ev.arg1));
eventCmpt?.FireNow(this, new BuildUnlockEvent(ev.arg1, ev.arg2));
break;
case Enum_EventType.UnlockMap:
eventCmpt?.FireNow(this, new MapUnlockEvent(ev.arg1, ev.arg2));
break;
case Enum_EventType.UserLvlUP:
eventCmpt?.FireNow(this, new UserLvlUpEvent(ev.arg1, ev.arg2));
break;
case Enum_EventType.HeroLvlUp:
eventCmpt?.FireNow(this, new HeroLvlUpEvent(ev.arg1, ev.arg2));
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.HeroTupo:
eventCmpt?.FireNow(this, new HeroTupoEvent(ev.arg1, ev.arg2));
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.WeaponLvUp:
eventCmpt?.FireNow(this, new WuqiLvlUpEvent());
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.WeaponTuPo:
eventCmpt?.FireNow(this, new WuqiTupoEvent());
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.YanLingLvUp:
eventCmpt?.FireNow(this, new YanLingLvlUpEvent());
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.YanLingTuPo:
eventCmpt?.FireNow(this, new YanLingTupoEvent());
eventCmpt?.FireNow(this, new HeroHPMPRefreshEvent(ev.name));
break;
case Enum_EventType.PaySuccess:
eventCmpt?.FireNow(this, new PaySuccessEvent());
break;
}
});
//}
}
else
{
var errNo = resp.err;
var msg = (resp.tag ?? new JObject()).TryGetValue("errmsg", out var msgJ) ? msgJ.ToString() : "";
if (errNo == 3510) // 糊了一个补丁, 任务卡未找到是因为重复发送领取奖励导致的,但是流程上不好处理,-gwang 2021年3月4日17:22:55
{
}
else if (errNo == 1021)
{
UI_CueDialog.Instance().Open("消息超时,或者本地设备时间与现实时间差距较大!", "错误", E_DialogType.OneButton, () =>
{
LogHelper.Log("Exiting");
Application.Quit();
});
}
if (GameConfigData.IsReady)
{ // 错误处理模块
if (GameConfigData.Ins.errmsg.ContainsKey(resp.err))
{
var errInfo = GameConfigData.Ins.errmsg[resp.err];
if (null != errInfo)
{
msg = string.IsNullOrEmpty(msg) ? errInfo.msg : msg;
if (errInfo.type == 1) // 错误类型为重启
{
UI_CueDialog.Instance().Open(msg, "错误", E_DialogType.OneButton, () =>
{
LogHelper.Log("Exiting");
Application.Quit();
});
}
else if (errInfo.type == 0) // 错误类型为警告
{
UI_TipsWindow.InitFloatWaringDialog("警告:" + msg);
}
}
}
}
else // 错误信息表尚未初始化
{
LogHelper.Log($"Err[{errNo}]:{msg}.");
if (msg.Length > 0)
{
UI_CueDialog.Instance().Open(msg, "错误", E_DialogType.OneButton, () =>
{
LogHelper.Log("Exiting");
Application.Quit();
});
}
// UI_TipsWindow.InitFloatWaringDialog(errInfo.type == 1 ? "错误:" : "警告:"+ msg);
}
}
}
else
{
UI_CueDialog.Instance().Open("连接不到游戏服务器, 请检查网络.", "网络故障", E_DialogType.OneButton, ExitGame);
throw new UnityException("网络模块故障, 需要应用上层逻辑处理或重置网络.");
}
}
///
/// 退出游戏
///
private void ExitGame()
{
Application.Quit();
}
private class NetPacket
{
// 包体编号
public int id = 0;
// 请求内容
public ReqVo request = null;
// 返回回调
public Action handler = null;
// 返回内容
public RespVo response = null;
// 发送时间
public DateTimeOffset tsRequest = DateTimeOffset.MinValue;
// 返回时间
public DateTimeOffset tsResp = DateTimeOffset.MinValue;
// 设定的超时时间(秒)
public int tsTimeout = 0;
// 错误编号
public int errNum = 0;
// 错误信息
public string errMsg = string.Empty;
public static NetPacket CreateNew(ReqVo req, Action callback)
{
var obj = new NetPacket();
obj.tsTimeout = GlobalConfig.Net_Connect_TimeOut_sec;
obj.id = objId++;
obj.request = req;
obj.handler = () =>
{
if (null != obj.response)
{
if (obj.response is T t) // RespVo(某些消息需要直接拿Resp做处理)
{
callback?.Invoke(t);
}
else
{
callback?.Invoke(obj.response.result.ToObject());
}
}
};
obj.tsRequest = DateTimeOffset.Now;
return obj;
}
private static volatile int objId = 0;
private NetPacket()
{
}
}
}