You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
12 KiB
393 lines
12 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
|
|
using Sog;
|
|
using ProtoCSStruct;
|
|
|
|
namespace World
|
|
{
|
|
public class WaitAckDiamondHolderOneObj
|
|
{
|
|
public WaitAckDiamondHolderOne Data;
|
|
public long LastSendTime;
|
|
}
|
|
|
|
public class WaitAckMutexOrderID
|
|
{
|
|
public string OrderID;
|
|
public long CreateTime;//保护10秒
|
|
}
|
|
/// <summary>
|
|
/// 一定需要等待回应的消息发送器
|
|
/// 邮件之类需要严格处理逻辑的可以使用
|
|
/// </summary>
|
|
public class WaitAckDiamondHolderReqSender : Singleton<WaitAckDiamondHolderReqSender>
|
|
{
|
|
private const int TimeoutDeleteTimeSecond = 24*3600;//默认24小时超时,会强制删除
|
|
|
|
private int m_sendCountThisTick = 0; //频率控制
|
|
|
|
private long m_lastSaveTime = 0;
|
|
private bool m_bDirty;
|
|
|
|
//uid 互斥列表
|
|
private Dictionary<long, WaitAckMutexOrderID> m_mutexMap = new Dictionary<long, WaitAckMutexOrderID>();
|
|
private Dictionary<string, WaitAckDiamondHolderOneObj> m_sendAllPacket = new Dictionary<string, WaitAckDiamondHolderOneObj>();
|
|
|
|
public void Init()
|
|
{
|
|
ReadFromFile();
|
|
}
|
|
|
|
public int CheckMutex(PlayerInfoWorld player)
|
|
{
|
|
if(m_mutexMap.ContainsKey(player.UserID))
|
|
{
|
|
string orderId = m_mutexMap[player.UserID].OrderID;
|
|
|
|
TraceLog.Error("WaitAckDiamondHolderReqSender.CheckMutex uid {0} orderId {1}", player.UserID, orderId);
|
|
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public void AddToMutex(long uid, string orderId)
|
|
{
|
|
if(m_mutexMap.ContainsKey(uid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
WaitAckMutexOrderID mutexOrderID = new WaitAckMutexOrderID();
|
|
mutexOrderID.OrderID = orderId;
|
|
mutexOrderID.CreateTime = WorldServerUtils.GetTimeSecond();
|
|
|
|
m_mutexMap.Add(uid, mutexOrderID);
|
|
}
|
|
|
|
public void RemoveMutexOrderId(long uid, string orderId)
|
|
{
|
|
if (m_mutexMap.ContainsKey(uid))
|
|
{
|
|
string mutexOrderId = m_mutexMap[uid].OrderID;
|
|
if (mutexOrderId == orderId)
|
|
{
|
|
m_mutexMap.Remove(uid);
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.RemoveMutexOrderId orderid {0} uid {1} success"
|
|
, orderId, uid);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void BeginSendAdd(ref SSPayDiamondHolderAddReq req, PlayerInfoWorld player)
|
|
{
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.BeginSendAdd orderId {0} uid {1} add {2}"
|
|
, req.OrderId, req.Uid, req.Add);
|
|
|
|
WaitAckDiamondHolderOneObj obj = new WaitAckDiamondHolderOneObj();
|
|
|
|
obj.Data.Uid = player.UserID;
|
|
obj.Data.RealmId = player.RealmID;
|
|
obj.Data.Account = req.Account;
|
|
obj.Data.Qqpayinfo = req.Qqpayinfo;
|
|
|
|
obj.Data.OrderId = req.OrderId;
|
|
obj.Data.ReqType = (int)SSGameMsgID.PayDiamondHolderAddReq;
|
|
obj.Data.Value = req.Add;
|
|
obj.Data.Mutex = req.Mutex;
|
|
obj.Data.ReasonId = req.ReasonId;
|
|
string orderId = req.OrderId.GetString();
|
|
m_sendAllPacket.Add(orderId, obj);
|
|
if(req.Mutex)
|
|
{
|
|
AddToMutex(player.UserID, orderId);
|
|
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.BeginSendAdd orderId {0} add mutex uid {1}"
|
|
, orderId, player.UserID);
|
|
}
|
|
|
|
SendReq(obj);
|
|
|
|
m_bDirty = true;
|
|
}
|
|
|
|
public void OnReceiveSuccess(long uid, string orderId)
|
|
{
|
|
if(m_sendAllPacket.ContainsKey(orderId))
|
|
{
|
|
RemoveByOrderId(orderId);
|
|
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.OnReceiveSuccess orderId {0} , removed"
|
|
, orderId);
|
|
}
|
|
else
|
|
{
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.OnReceiveSuccess no orderId {0}", orderId);
|
|
}
|
|
|
|
//有可能orderId不在m_sendAllPacket里,只在m_mutexMap里,这是有可能的
|
|
RemoveMutexOrderId(uid, orderId);
|
|
}
|
|
|
|
public void Tick(long nowSecond)
|
|
{
|
|
TickSender(nowSecond);
|
|
|
|
TickMutex(nowSecond);
|
|
|
|
TickSave(nowSecond);
|
|
}
|
|
|
|
private void TickSender(long nowSecond)
|
|
{
|
|
m_sendCountThisTick = 0;
|
|
|
|
List<string> deleteList = null;
|
|
foreach (var pair in m_sendAllPacket)
|
|
{
|
|
var one = pair.Value;
|
|
//timeout delete
|
|
if (nowSecond - one.Data.CreateTime > TimeoutDeleteTimeSecond)
|
|
{
|
|
if (deleteList == null)
|
|
{
|
|
deleteList = new List<string>();
|
|
}
|
|
|
|
deleteList.Add(one.Data.OrderId.GetString());
|
|
|
|
//需要记录bill日志,表示超时失败
|
|
TraceLog.Error("WaitAckDiamondHolderReqSender.TickSender timeout, Deleted, uid {0} orderId {1} type {2} value {3}"
|
|
, one.Data.Uid
|
|
, one.Data.OrderId
|
|
, one.Data.ReqType
|
|
, one.Data.Value);
|
|
|
|
continue;
|
|
}
|
|
|
|
//动态计算重试时间
|
|
//前5分钟每分钟尝试一次,如果还失败,每5分钟尝试一次
|
|
int resenderTimeOut = 60;
|
|
if(one.Data.Mutex)
|
|
{
|
|
resenderTimeOut = 30; //互斥的快点处理
|
|
}
|
|
if (nowSecond - one.Data.CreateTime > 300)
|
|
{
|
|
resenderTimeOut = 300;
|
|
}
|
|
|
|
//失败了重新尝试发送
|
|
if (nowSecond - one.LastSendTime > resenderTimeOut)
|
|
{
|
|
one.LastSendTime = nowSecond;
|
|
|
|
//发送
|
|
SendReq(one);
|
|
|
|
TraceLog.Error("WaitAckDiamondHolderReqSender orderid {0} MsgID {1} uid {2} timeout ,resend it"
|
|
, one.Data.OrderId, one.Data.ReqType, one.Data.Uid);
|
|
|
|
m_sendCountThisTick++;
|
|
|
|
//控制1秒最多100个请求,防止雪崩
|
|
if (m_sendCountThisTick > 100)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (deleteList != null)
|
|
{
|
|
foreach (var key in deleteList)
|
|
{
|
|
RemoveByOrderId(key);
|
|
}
|
|
|
|
m_bDirty = true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private void TickMutex(long nowSecond)
|
|
{
|
|
List<long> deleteList = null;
|
|
foreach(var pair in m_mutexMap)
|
|
{
|
|
if(nowSecond - pair.Value.CreateTime >= 30)
|
|
{
|
|
if(deleteList == null)
|
|
{
|
|
deleteList = new List<long>();
|
|
}
|
|
|
|
deleteList.Add(pair.Key);
|
|
}
|
|
}
|
|
|
|
if (deleteList != null)
|
|
{
|
|
foreach (var key in deleteList)
|
|
{
|
|
m_mutexMap.Remove(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RemoveByOrderId(string orderId)
|
|
{
|
|
WaitAckDiamondHolderOneObj obj;
|
|
if (m_sendAllPacket.TryGetValue(orderId, out obj))
|
|
{
|
|
m_sendAllPacket.Remove(orderId);
|
|
if(obj.Data.Mutex)
|
|
{
|
|
RemoveMutexOrderId(obj.Data.Uid, orderId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SendReq(WaitAckDiamondHolderOneObj one)
|
|
{
|
|
if(one.Data.ReqType == (int)SSGameMsgID.PayDiamondHolderAddReq)
|
|
{
|
|
SSPayDiamondHolderAddReq req = new SSPayDiamondHolderAddReq();
|
|
req.RealmId = one.Data.RealmId;
|
|
req.OrderId = one.Data.OrderId;
|
|
req.Uid = one.Data.Uid;
|
|
req.Account = one.Data.Account;
|
|
req.Qqpayinfo = one.Data.Qqpayinfo;
|
|
req.Add = one.Data.Value;
|
|
req.Mutex = one.Data.Mutex;
|
|
req.ReasonId = one.Data.ReasonId;
|
|
|
|
//先实现托管给数据库,没问题了再实现托管给米大师
|
|
uint dbServerID = DBServerIDUtils.GetGameDBServerID(req.Uid);
|
|
WorldServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PayDiamondHolderAddReq, ref req, req.Uid);
|
|
}
|
|
}
|
|
|
|
private void TickSave(long nowSecond)
|
|
{
|
|
if(m_bDirty == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//1分钟尝试保存一下
|
|
if (nowSecond - m_lastSaveTime < 60)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_lastSaveTime = nowSecond;
|
|
|
|
SaveToFile();
|
|
|
|
m_bDirty = false;
|
|
}
|
|
|
|
public string GetFileDBName()
|
|
{
|
|
return "../bin/" + "world_waitackdiamondhandler.fdb";
|
|
}
|
|
|
|
public void SaveToFile()
|
|
{
|
|
string filepath = GetFileDBName();
|
|
try
|
|
{
|
|
byte[] dataByte = new byte[1024];
|
|
|
|
FileStream fileStream = File.OpenWrite(filepath);
|
|
//清空文件
|
|
fileStream.SetLength(0);
|
|
|
|
BinaryWriter sw = new BinaryWriter(fileStream);
|
|
|
|
foreach (var onereq in m_sendAllPacket)
|
|
{
|
|
int size = onereq.Value.Data.CalculateSize();
|
|
CodedOutputStream ouput = new CodedOutputStream(dataByte);
|
|
onereq.Value.Data.WriteTo(ouput);
|
|
sw.Write(size);
|
|
sw.Write(dataByte,0,size);
|
|
}
|
|
sw.Flush();
|
|
sw.Dispose();
|
|
fileStream.Dispose();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("WaitAckDiamondHolderReqSender.SaveToFile write file failed! {0}", filepath);
|
|
TraceLog.Exception(ex);
|
|
}
|
|
}
|
|
|
|
private void ReadFromFile()
|
|
{
|
|
string filepath = GetFileDBName();
|
|
if (File.Exists(filepath) == false)
|
|
{
|
|
//这个不能算错误
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.ReadFromFile file not exist {0}", filepath);
|
|
return ;
|
|
}
|
|
|
|
try
|
|
{
|
|
byte[] data = File.ReadAllBytes(filepath);
|
|
if(data.Length < 4)
|
|
{
|
|
//TraceLog.Error("WaitAckDiamondHolderReqSender.ReadFromFile length invalid {0}", filepath);
|
|
return;
|
|
}
|
|
|
|
|
|
int startIndex = 0;
|
|
while(startIndex < data.Length - 10)
|
|
{
|
|
int oneDataLength = BitConverter.ToInt32(data, startIndex);
|
|
//orderid挺长的,其实肯定不止10个长度
|
|
if (oneDataLength < 10)
|
|
{
|
|
break;
|
|
}
|
|
|
|
startIndex += 4;
|
|
|
|
ProtoCSStruct.CodedInputStream input = new ProtoCSStruct.CodedInputStream(data, startIndex, oneDataLength);
|
|
startIndex += oneDataLength;
|
|
|
|
WaitAckDiamondHolderOneObj one = new WaitAckDiamondHolderOneObj();
|
|
one.Data.ReadFrom(input);
|
|
|
|
string orderId = one.Data.OrderId.GetString();
|
|
if(m_sendAllPacket.ContainsKey(orderId) == false)
|
|
{
|
|
m_sendAllPacket.Add(orderId, one);
|
|
}
|
|
}
|
|
|
|
TraceLog.Debug("WaitAckDiamondHolderReqSender.ReadFromFile read WaitAckDiamondHolderOne count {0}", m_sendAllPacket.Count);
|
|
|
|
return ;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Error("WaitAckDiamondHolderReqSender.ReadFromFile parse message failed! {0}", filepath);
|
|
TraceLog.Exception(ex);
|
|
return ;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|