using System; using System.Collections.Generic; using Sog; using ProtoCSStruct; using System.Linq; using Sog.Service; using Sog.Log; namespace Mail { public static class MailSendWithRule { public static void TickGetMailWithRule(long nowSecond) { //每分钟拉取邮件 var serverdata = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref serverdata.m_mailWithRule; SSGetMailWithRuleReq req = new SSGetMailWithRuleReq(); req.BeginSeq = (int)mailWithRule.OpSvrToMailSeq; req.IsDiscard = false; MailServerUtils.GetPacketSender().SendToWorldServer((int)SSGameMsgID.GetMailWithRuleReq, ref req,0); //拉取废弃邮件 SSGetMailWithRuleReq req2 = new SSGetMailWithRuleReq(); req2.BeginSeq = 0; req2.IsDiscard = true; MailServerUtils.GetPacketSender().SendToWorldServer((int)SSGameMsgID.GetMailWithRuleReq, ref req2, 0); } public static void OnGetMailWithRuleRes(uint remoteAppID, StructPacket packet) { bool isNeedSendMail = false; ref var res = ref packet.GetMessage(); for (int i = 0; i < res.RuleMail.Count; i++) { if(res.RuleMail[i].RuleParam1 > 0) { if(res.IsDiscard) { OnDiscardMailWithRuleReq(remoteAppID, res.RuleMail[i].Mail.Uuid.ToString(), (int)res.RuleMail[i].Mail.MailID); } else { GmSendMailWithRule(remoteAppID, ref res.RuleMail[i]); isNeedSendMail = true; } } } if(isNeedSendMail) { var playertable = MailServerUtils.GetMailServerData().m_playerTable; foreach (var playerOnMail in playertable.Values) { if (playerOnMail.GameServerID == 0) { continue; } if (playerOnMail.IsOnline == false) { continue; } CheckAndSendMailWithRule(playerOnMail); } } } public static void OnDiscardMailWithRuleReq(uint remoteAppID, StructPacket packet) { ref var req = ref packet.GetMessage(); OnDiscardMailWithRuleReq(remoteAppID, req.Uuid.ToString(), req.MailID); } private static void OnDiscardMailWithRuleReq(uint remoteAppID, string uuid, int mailId) { var data = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref data.m_mailWithRule; for (int i = mailWithRule.MailRecord.Count - 1; i >= 0; i--) { ref var oneRecord = ref mailWithRule.MailRecord[i]; if(oneRecord.Mail.Uuid.ToString() == uuid) { mailWithRule.MailRecord.RemoveAt(i); } } data.mailWithRuleNeedSave = true; } public static void GmSendMailWithRule(uint remoteAppID, StructPacket packet) { GmSendMailWithRule(remoteAppID, ref packet.GetMessage()); } private static void GmSendMailWithRule(uint remoteAppID, ref SSSendMailWithRule req) { TraceLog.Debug("MailSendWithRule.GmSendMailWithRule RuleType {0} param {1}" , req.RuleType, req.RuleParam1); if (req.RuleType == (int)SendMailRuleType.All || req.RuleType == (int)SendMailRuleType.Ios || req.RuleType == (int)SendMailRuleType.Android) { GmSendMailWithRuleToAll(ref req, req.RuleParam1, req.RuleType); } else { GmSendMailWithRuleToUidList(req); } var serverdata = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref serverdata.m_mailWithRule; if(req.Seq > mailWithRule.OpSvrToMailSeq) { mailWithRule.OpSvrToMailSeq = req.Seq; //修改seq,下次请求邮件从这个开始 } var res = new SSSendMailWithRuleRes(); res.Ret = 0; res.Mail = req.Mail; res.RuleType = req.RuleType; res.RuleParam1 = req.RuleParam1; res.TargetUid.SetString(req.TargetUid.ToString()); res.Seq = req.Seq; res.StrParam1 = req.StrParam1; MailServerUtils.GetPacketSender().SendToWorldServer((int)SSGameMsgID.SendMailWithRuleRes, ref res, 0); MailServerUtils.GetMailServerData().mailWithRuleNeedSave = true; } public static void GmSendMailWithRuleToUidList(SSSendMailWithRule req) { string[] uidsplit = req.TargetUid.ToString().Split('|'); RepeatedInt_1024 offlineUids = new RepeatedInt_1024(); var serverdata = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref serverdata.m_mailWithRule; for (int i = 0; i < uidsplit.Length; i++) { long uidparse = 0; if (true == long.TryParse(uidsplit[i], out uidparse)) { if (uidparse > 0) { if (offlineUids.Count < offlineUids.GetMaxCount() && offlineUids.Contains((int)uidparse) == false) { offlineUids.Add((int)uidparse); } } } } if(offlineUids.Count > 0) { // 离线玩家添加到离线队列 SendMailWithRuleRecord record = new SendMailWithRuleRecord(); record.Mail = req.Mail; record.EndTime = MailServerUtils.GetTimeSecond() + req.RuleParam1 * 60; record.SendMailRuleType = (int)SendMailRuleType.None; record.VersionSeq = (int)req.Mail.MailID; record.TargetUid.CopyFrom(ref offlineUids); ParseRealm(req.RealmStr.GetString(), req.TimeParamStr.GetString(), ref record); AddRecordMail(ref record); // 多语言内容添加 if (req.LangContent.Count > 0) { AddMailLang(ref req); } } MailServerUtils.GetMailServerData().mailWithRuleNeedSave = true; } public static void AddRecordMail(ref SendMailWithRuleRecord record) { var serverData = MailServerUtils.GetMailServerData(); record.VersionSeq = (int)record.Mail.MailID; if (serverData.m_mailWithRule.MailRecord.GetCount() >= serverData.m_mailWithRule.MailRecord.GetMaxCount()) { serverData.m_mailWithRule.MailRecord.RemoveAt(0); } serverData.m_mailWithRule.MailRecord.Add(ref record); } public static void GmSendMailWithRuleToAll(ref SSSendMailWithRule req, long durationMinute, int type) { DBMail mail = req.Mail; SendMailWithRuleRecord record = new SendMailWithRuleRecord(); record.Mail = mail; record.EndTime = MailServerUtils.GetTimeSecond() + durationMinute * 60; record.SendMailRuleType = type; ParseRealm(req.RealmStr.GetString(), req.TimeParamStr.GetString(), ref record); AddRecordMail(ref record); TraceLog.Debug("MailSendWithRule.GmSendMailWithRuleToAll add record type {0} endTime {1} versionSeq {2}" , record.SendMailRuleType, record.EndTime, record.VersionSeq); // 多语言内容添加 if(req.LangContent.Count > 0) { AddMailLang(ref req); } MailServerUtils.GetMailServerData().mailWithRuleNeedSave = true; } public static void AddMailLang(ref SSSendMailWithRule req) { var serverData = MailServerUtils.GetMailServerData(); if (GetMailLang((int)req.Mail.MailID) == null) { MailLang mailLang = new MailLang(); mailLang.MailId = (int)req.Mail.MailID; mailLang.langContent = new List(); for (int i = 0; i < req.LangContent.Count; i++) { MailLangListLang tempLang = new MailLangListLang(); tempLang.Content = (req.LangContent[i].Content.GetString()); tempLang.Language = (req.LangContent[i].Language.GetString()); tempLang.Title = (req.LangContent[i].Title.GetString()); tempLang.senderName = (req.LangContent[i].SenderName.GetString()); mailLang.langContent.Add(tempLang); } serverData.m_mailLang.langContentList.Add(mailLang); } } public static void TickSendMailWithRule(long nowSecond) { var data = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref data.m_mailWithRule; for (int i = mailWithRule.MailRecord.Count - 1; i >= 0 ; i--) { var record = mailWithRule.MailRecord[i]; var mailId = record.Mail.MailID; if (record.EndTime > 0 && nowSecond > record.EndTime) { TraceLog.Debug("MailSendWithRule.TickSendMailWithRule record type {0} endTime {1} versionSeq {2} timeout, delete it" , record.SendMailRuleType, record.EndTime, record.VersionSeq); mailWithRule.MailRecord.RemoveAt(i); RemoveLangContent((int)mailId); data.mailWithRuleNeedSave = true; continue; } if(record.SendMailRuleType == (int)SendMailRuleType.None && record.TargetUid.Count == 0) { mailWithRule.MailRecord.RemoveAt(i); RemoveLangContent((int)mailId); data.mailWithRuleNeedSave = true; } } if(data.mailWithRuleNeedSave) { SaveDataToFile(); data.mailWithRuleNeedSave = false; } } private static void RemoveLangContent(int mailId) { var data = MailServerUtils.GetMailServerData(); foreach (var item in data.m_mailLang.langContentList) { if (item.MailId == mailId) { data.m_mailLang.langContentList.Remove(item); break; } } return ; } private static string GetSaveFileName() { return "./MailServerDb_" + MailServerUtils.GetApp().ServerID.ToString() + ".fdb"; } private static string GetSaveLangFileName() { return "./MailServerDb_Lang_" + MailServerUtils.GetApp().ServerID.ToString() + ".fdb"; } /// /// 保存到数据库,存本地文件 /// public static void SaveDataToFile() { DateTime begin = DateTime.Now; ref var mailWithRule = ref MailServerUtils.GetMailServerData().m_mailWithRule; string filename = GetSaveFileName(); int iRet = Sog.IO.FileDBSave.SaveToFile(ref mailWithRule, filename); if (iRet != 0) { TraceLog.Error("MailSendWithRule.SaveDataToFile save file {0} error ,ret {1}", filename, iRet); return; } string mailLangfilename = GetSaveLangFileName(); var mailLang = MailServerUtils.GetMailServerData().m_mailLang; // 写入多语言部分 int iLangRet = Sog.IO.FileDBSaveByBinSerialize.SaveToFile(mailLang, mailLangfilename); if (iLangRet != 0) { TraceLog.Error("mailLangList.SaveDataToFile save file {0} error ,ret {1}", mailLangfilename, iLangRet); return; } TraceLog.Trace("MailSendWithRule.SaveDataToFile save file {0} success, mail count {1} costTime {2} ms", filename, mailWithRule.MailRecord.Count, DateTime.Now.Millisecond - begin.Millisecond); } public static void LoadDataFromFile() { ref var mailWithRule = ref MailServerUtils.GetMailServerData().m_mailWithRule; string filename = GetSaveFileName(); TraceLog.Trace("MailSendWithRule.LoadDataFromFile load now file {0} begin", filename); bool success = Sog.IO.FileDBSave.ReadFromFile(ref mailWithRule, filename); if (success) { TraceLog.Trace("MailSendWithRule.LoadDataFromFile load file {0} success", filename); } else { MailServerUtils.GetMailServerData().m_mailWithRule = new SendMailWithRuleDataToFile(); TraceLog.Trace("MailSendWithRule.LoadDataFromFile no Data, new empty object"); //立即保存一下 SaveDataToFile(); } // 邮件多语言部分修改 var langFilename = GetSaveLangFileName(); MailServerUtils.GetMailServerData().m_mailLang = Sog.IO.FileDBSaveByBinSerialize.ReadFromFile(langFilename); if (success) { TraceLog.Trace("MailSendWithRule.LoadDataFromFile load file {0} success", filename); } else { MailServerUtils.GetMailServerData().m_mailLang = new MailLangContentData(); TraceLog.Trace("MailLangList.LoadDataFromFile no Data, new empty object"); SaveDataToFile(); } TraceLog.Trace("MailSendWithRule.LoadDataFromFile end"); } public static bool IsNeedSendMailToPlayer(PlayerInfoMail playerInfo, MailServerData data, ref SendMailWithRuleRecord record, out int ret) { long playerSendMailWithRuleVersionSeq = PlayerMailVerSeqSvc.GetPlayerMailVerSeq(playerInfo); TraceLog.Trace("IsNeedSendMailToPlayer uid {0} verSeq {1}", playerInfo.UserID, playerSendMailWithRuleVersionSeq); ret = 0; //只有设置了创建时间的 if (record.Mail.PlayerCreateTimeLimit > 0 && playerInfo.CreateTime > record.Mail.PlayerCreateTimeLimit) { ret = 1; return false; } // 判断是否需要进行版本限制 if (!record.Mail.ApkVersion.IsEmpty() && AppVersion.ToIntVersion(playerInfo.ApkVersion) >= AppVersion.ToIntVersion(record.Mail.ApkVersion.GetString())) { ret = 2; return false; } //玩家不在邮件指定区服中(老的校验方式) if (record.Mail.Realmlist.Count != 0 && !record.Mail.Realmlist.Contains(playerInfo.RealmID)) { ret = 3; return false; } if(!CheckRealm(playerInfo.RealmID, ref record)) { ret = 4; return false; } //已经发过了 if (record.VersionSeq <= playerSendMailWithRuleVersionSeq) { ret = 5; return false; } //检查指定玩家 if (record.SendMailRuleType == (int)SendMailRuleType.None) { if (record.TargetUid.Contains((int)playerInfo.UserID) == false) { ret = 7; return false; } return true; } if (record.SendMailRuleType == (int)SendMailRuleType.All) { return true; } if (record.SendMailRuleType == (int)SendMailRuleType.Android && playerInfo.PlatformType == 1) { return true; } if (record.SendMailRuleType == (int)SendMailRuleType.Ios && playerInfo.PlatformType == 2) { return true; } ret = 8; return false; } public static void CheckAndSendMailWithRule(PlayerInfoMail playerInfo) { //TraceLog.Debug("MailSendWithRule.CheckAndSendMailWithRule uid {0} begin", playerInfo.UserID); if (playerInfo == null) { return; } if (playerInfo.IsOnline == false) { return; } var data = MailServerUtils.GetMailServerData(); ref var mailWithRule = ref data.m_mailWithRule; // 检查全局邮件 for (int i = 0; i < mailWithRule.MailRecord.Count; i++) { ref var record = ref mailWithRule.MailRecord[i]; bool needSend = IsNeedSendMailToPlayer(playerInfo, data, ref record, out int ret); TraceLog.Trace("CheckAndSendMailWithRule IsNeedSendMailToPlayer uid {0} recordId {1} ret {2}", playerInfo.UserID, record.Mail.MailID, ret); //这个删除逻辑还是留着吧,修改也是修改缓存 //但没有判断是否发送就删除,逻辑是否存在问题? if (record.SendMailRuleType == (int)SendMailRuleType.None) { for (int j = record.TargetUid.Count - 1; j >= 0; j--) { if (record.TargetUid[j] == (int)playerInfo.UserID) { record.TargetUid.RemoveAt(j);//无论是否要发,都要删除uid } } } if (needSend == false) { continue; } MailLangListLang lang = GetMailLangListLang(playerInfo, ref record); if(lang != null) { //语言不一样,不是全玩家发送,就是要指定语言发送 //if(lang.Language.ToLower() != playerInfo.Language.ToLower() // && record.Mail.IsSendAllUser != 1 // && record.SendMailRuleType != (int)SendMailRuleType.None) //{ // //要修改邮件版本号 // TraceLog.Trace("CheckAndSendMailWithRule IsNeedSendMailToPlayer uid {0} mailId {1} need same language mailLang {2} playerLang {3}, skip mail", // playerInfo.UserID, record.Mail.MailID, lang.Language.ToLower(), playerInfo.Language.ToLower()); // PlayerMailVerSeqSvc.SetPlayerVerSeq(playerInfo, record.VersionSeq); // continue; //} //按理说不应该修改record的邮件数据,但先维持原有逻辑吧 record.Mail.Language.SetString(lang.Language.ToLower()); record.Mail.Content.SetString(lang.Content); record.Mail.Title.SetString(lang.Title); record.Mail.SenderName.SetString(lang.senderName); } else//一个语言都没有 { // 邮件不带正文, 那不能发送给指定玩家, 只能给全服发, 这段代码逻辑很坑 // 老的邮件数据 if (record.Mail.IsSendAllUser == 0 && playerInfo.Language.ToLower() != record.Mail.Language.GetString().ToLower()) { // 客户端语言和邮件不同 TraceLog.Trace("CheckAndSendMailWithRule IsNeedSendMailToPlayer uid {0} recordId {1} language 2", playerInfo.UserID, record.Mail.MailID); continue; } } PlayerMailVerSeqSvc.SetPlayerVerSeq(playerInfo, record.VersionSeq); //if (record.VersionSeq > 0)//之前的给玩家发是没有版本号的,所以这里一定要加这个 // data.m_sendMailWithRulePlayerVersion[playerInfo.UserID] = record.VersionSeq; //playerInfo.SendMailWithRuleVersionSeq = record.VersionSeq; DBMail mail = record.Mail; mail.Uid = playerInfo.UserID; mail.ExpirationTime = record.EndTime; SendMail(ref mail, MailOpType.Insert, record.VersionSeq); TraceLog.Debug("MailSendWithRule.CheckAndSendMailWithRule mailId {0} uid {1}", mail.MailID, mail.Uid); } MailServerUtils.GetMailServerData().mailWithRuleNeedSave = true; } private static MailLangListLang GetMailLangListLang(PlayerInfoMail playerInfo, ref SendMailWithRuleRecord record) { // 语言判断 var mailLangContent = GetMailLang((int)record.Mail.MailID); if(mailLangContent == null) { return null; } MailLangListLang retLand = null; MailLangListLang enLand = null; MailLangListLang jpLand = null; MailLangListLang firstLand = mailLangContent.langContent.FirstOrDefault(); foreach (var item in mailLangContent.langContent) { if (item.Language.ToLower() == "en") //英文 { enLand = item; } else if (item.Language.ToLower() == "jp") //日文 { jpLand = item; } // 有对应语言的直接退出 if (item.Language.ToLower() == playerInfo.Language.ToLower()) { retLand = item; break; } } //优先级:相同语言 > 日文 > 英文 > 第一封语言 if(retLand != null) //发相同语言的 { return retLand; } else if (jpLand != null) //发日文的 { return jpLand; } else if (enLand != null) //发英文 { return enLand; } else if (firstLand != null) //发第一种语言 { return firstLand; } return null; } public static void SendMail(ref DBMail mail, MailOpType mType = MailOpType.Insert, long mailVerSeq = 0) { var data = MailServerUtils.GetMailServerData(); if (!data.m_playerTable.ContainsKey(mail.Uid)) { return; } var iTaskIndex = (int)(mail.Uid % MessageTaskHandler.MailDBWorkThreadCount); //构建消息 StructPacket packet = new StructPacket(); packet.Header.Type = (int)SSGameMsgID.MailOpReq; packet.Header.ObjectID = mail.Uid; StructMessageParser parser = new StructMessageParser(); packet.Parser = parser; ref SSMailOpReq req = ref parser.GetMessage(); req.OpType = (int)mType; req.ClientOpUid = mail.Uid;//这里需要,否则通知不到玩家客户端 req.AckGameServerID = data.m_playerTable[mail.Uid].GameServerID; req.UniqueID = WaitAckStructRequestSender.Instance.GeneratorUniqueID(); req.Mail.CopyFrom(ref mail);//先拷贝一份 req.Mail.InsertUniqueID = req.UniqueID; req.MailVerSeq = mailVerSeq; //分派消息 MessageTaskDistributor.Instance.Distribute(data.m_playerTable[mail.Uid].GameServerID, packet, iTaskIndex); TraceLog.Debug("MailSendWithRule.SendMail uniqueId {0} mailId {1} uid {2}", req.UniqueID, mail.MailID, mail.Uid); CommBillLogUtils.LogMailOpBegin(mail.Uid, mail, (int)MailOpType.Insert); } public static MailLang GetMailLang(int mailId) { var langData = MailServerUtils.GetMailServerData().m_mailLang; MailLang retLang = null; foreach (var item in langData.langContentList) { if (mailId == item.MailId) { retLang = item; break; } } return retLang; } public static bool ParseRealm(string realmlistStr, string timeParamStr, ref SendMailWithRuleRecord record) { List posList = new List(); try { var realmStrlist = realmlistStr.Split("#"); foreach (var realmlist in realmStrlist) { if(realmlist == "" && realmStrlist.Length != 1) { continue; } if (realmlist.Contains('-')) { var tempList = realmlist.Split("-"); RealmPos tempPos = new RealmPos(); tempPos.Begin = Convert.ToInt32(tempList[0]); tempPos.End = Convert.ToInt32(tempList[1]); posList.Add(tempPos); } else { if (realmlist.Contains(',')) { var realms = realmlist.Split(','); // 排序 List sortRealmList = new List(); foreach (var _RealmID in realms) { int tempId = Convert.ToInt32(_RealmID); sortRealmList.Add(tempId); } sortRealmList.Sort((x, y) => x.CompareTo(y)); if (sortRealmList.Count == 1) { var realmPos = new RealmPos(); realmPos.Begin = sortRealmList[0]; realmPos.End = sortRealmList[0]; posList.Add(realmPos); } else if (sortRealmList.Count == 2) { if (sortRealmList[0] + 1 == sortRealmList[1]) { var realmPos = new RealmPos(); realmPos.Begin = sortRealmList[0]; realmPos.End = sortRealmList[1]; posList.Add(realmPos); } else { var realmPos1 = new RealmPos(); realmPos1.Begin = sortRealmList[0]; realmPos1.End = sortRealmList[0]; posList.Add(realmPos1); var realmPos2 = new RealmPos(); realmPos2.Begin = sortRealmList[1]; realmPos2.End = sortRealmList[1]; posList.Add(realmPos2); } } else { for (int i = 0; i < sortRealmList.Count; i++) { var realmPos = new RealmPos(); realmPos.Begin = sortRealmList[i]; for (int j = i; j < sortRealmList.Count - 1; j++) { //列表连续 if (sortRealmList[j + 1] == sortRealmList[j] + 1) { continue; } else { realmPos.End = sortRealmList[j]; i = j; break; } } if (realmPos.End == 0) { realmPos.End = sortRealmList[i]; } posList.Add(realmPos); } } } else { if (realmlist != "" && realmlist != null) { int RealmID = Convert.ToInt32(realmlist); if (RealmID != 0) { var realmPos = new RealmPos(); realmPos.Begin = RealmID; realmPos.End = RealmID; posList.Add(realmPos); } } } } } //服务器列表 foreach(var item in posList) { RealmPos pos = new RealmPos(); pos.Begin = item.Begin; pos.End = item.End; record.IdInterValue.Add(pos); } } catch (Exception ex) { TraceLog.Error("MailSendWithRule.ParseRealm error:{0}",ex.Message); return false; } return true; } public static bool CheckRealm(int realmId , ref SendMailWithRuleRecord record) { if (record.IdInterValue.Count == 0) { return true; } for(int i = 0; i < record.IdInterValue.Count; i++) { if(record.IdInterValue[i].Begin <= realmId && realmId <= record.IdInterValue[i].End) { return true; } } return false; } } }