using Amazon.S3.Model; using Google.Protobuf.WellKnownTypes; using MetaAssets; using Newtonsoft.Json; using ServerCommon; using ServerCommon.Cache; using ServerCore; using ServerBase; using System.Collections.Concurrent; using System.Collections.Generic; using System.Dynamic; using System.Linq; using UGQDatabase.Models; namespace GameServer { public class QuestMailAction : EntityActionBase { private ConcurrentDictionary<(EQuestType, UInt32) /*quest_type, quest_id*/, ServerCommon.QuestMail> m_quest_mails = new(); private bool m_is_quest_mail_loaded = false; public QuestMailAction(EntityBase owner) : base(owner) { } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } public override void onClear() { return; } public Result hasQuestMail(EQuestType questType, UInt32 questId, bool needLog = false) { var result = new Result(); if (false == m_quest_mails.ContainsKey((questType, questId))) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var err_msg = $"Quest Mail Not Exist, questId : {questId}, owner : {owner.toBasicString()}"; result.setFail(ServerErrorCode.QuestMailNotExist, err_msg); if (needLog) { Log.getLogger().error(result.toBasicString()); } return result; } result.setSuccess(); return result; } public async Task loadQuestMails() { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var server_logic = GameServerApp.getServerLogic(); var batch = new QueryBatchEx(owner, LogActionType.None , server_logic.getDynamoDbClient()); { batch.addQuery(new DBQQuestMailsReadAll(owner.getUserGuid())); } var result = await QueryHelper.sendQueryAndBusinessLog(batch); return result; } public List getQuestMailsInfo() { return convertQuestMailsToProtoQuestMailInfo(); } public (Result, ServerCommon.QuestMail) getQuestMail(EQuestType questType, UInt32 questId) { var result = new Result(); if (false == m_quest_mails.TryGetValue((questType, questId), out var quest_mail)) { var err_msg = $"Quest Mail Not Exist, questType : {questType}, questId : {questId}"; result.setFail(ServerErrorCode.QuestMailNotExist, err_msg); Log.getLogger().error(err_msg); return (result, null!); } return (result, quest_mail); } public List makeNewSystemQuestMail(List<(UInt32, EQuestType)> mailQuestIds) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); List < ServerCommon.QuestMail > quest_mails = new(); foreach (var quest_id in mailQuestIds) { (var quest_mail, var log_invoker) = createQuestMail(quest_id.Item1, 0, false); if (quest_mail is not null) quest_mails.Add(quest_mail); } return quest_mails; } public async Task<(List<(UInt32, EQuestType)>, List)> makeQuestMailDocByType(EAssignRequireType type) { if (QuestManager.It._QuestReqTypeGroup.TryGetValue(type.ToString(), out var quest_ids) == false) { Log.getLogger().error($"SendQuestMailByType - QuestReqTypeGroup not found. type : {type}, " + $"groupInfo : {JsonConvert.SerializeObject(QuestManager.It._QuestReqTypeGroup)}"); return (new(), new()); } List<(UInt32, EQuestType)> mail_quests = new(); List force_accept_quests = new(); foreach (var quest_id in quest_ids) { (var result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(quest_id); if (result.isFail()) continue; var quest_base_info = quest_meta_all_base_info.m_quest_base_info; NullReferenceCheckHelper.throwIfNull(quest_base_info, () => $"quest_base_info is null !!!"); if (quest_base_info.AssignType.Equals(nameof(EAssignType.MAIL)) && quest_base_info.ForceAccept == false) { var quest_type = EnumHelper.convertEnumTypeAndValueStringToEnum(quest_base_info.QuestType, EQuestType.NORMAL); mail_quests.Add((quest_id, quest_type)); continue; } else if (quest_base_info.AssignType.Equals(nameof(EAssignType.MAIL)) && quest_base_info.ForceAccept == true) { force_accept_quests.Add(quest_id); continue; } } return (mail_quests, force_accept_quests); } public async Task readQuestMail(UInt32 questId, UInt32 questRevision) { var result = new Result(); var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var quest_type = await QuestMetaHelper.getQuestType(player, questId, questRevision); if (false == m_quest_mails.TryGetValue((quest_type, questId), out var quest_mail)) { var err_msg = $"Quest Mail Not Exist, questId : {questId}"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.QuestMailNotExist, err_msg); return result; } var quest_mail_attribute = quest_mail.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(quest_mail_attribute, () => $"quest_mail_attribute is null !!!"); quest_mail_attribute.IsRead = 1; quest_mail_attribute.modifiedEntityAttribute(); return result; } public async Task convertQuestMailsToProtoQuestMailInfo(UInt32 questId, UInt32 questRevision) { QuestMailInfo info = new(); var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var quest_type = await QuestMetaHelper.getQuestType(player, questId, questRevision); if (false == m_quest_mails.TryGetValue((quest_type, questId), out var questMail)) { Log.getLogger().error($"QuestInfo {questId} not exist, {player.toBasicString()}"); return null; } (var result, var quest_mail_attibute) = getQuestMailAttribFromAttribute(questMail); if (result.isFail()) { Log.getLogger().error($"QuestInfo {questId} Attrivute not exist, {player.toBasicString()}"); return null; } NullReferenceCheckHelper.throwIfNull(quest_mail_attibute, () => $"quest_mail_attibute is null !!!"); info.ComposedQuestId = QuestHelper.convertQuestIdAndRevisionToUgqQuestId(quest_mail_attibute.QuestId, quest_mail_attibute.QuestRevision); info.CreateTime = Timestamp.FromDateTime(quest_mail_attibute.CreateTime); info.IsRead = quest_mail_attibute.IsRead; return info; } public List convertQuestMailsToProtoQuestMailInfo() { List quest_mails = new(); foreach (var quest_mail in m_quest_mails) { QuestMailInfo info = new(); (var result, var quest_mail_attibute) = getQuestMailAttribFromAttribute(quest_mail.Value); if (result.isFail()) { continue; } NullReferenceCheckHelper.throwIfNull(quest_mail_attibute, () => $"quest_mail_attibute is null !!!"); info.ComposedQuestId = quest_mail_attibute.QuestId; info.CreateTime = Timestamp.FromDateTime(quest_mail_attibute.CreateTime); info.IsRead = quest_mail_attibute.IsRead; quest_mails.Add(info); } return quest_mails; } private (Result, QuestMailAttribute?) getQuestMailAttribFromAttribute(ServerCommon.QuestMail questMail) { var attribute = questMail.getEntityAttribute(); var result = new Result(); if (attribute is null) { var err_msg = $"Fail to get QuestMailAttribute"; Log.getLogger().error(err_msg); result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); return (result, null); } return (result, attribute); } public Result setEndQuestsFromDoc(QuestMailDoc doc) { var result = new Result(); var owner = getOwner(); // ServerCommon.QuestMail quest_mail = new ServerCommon.QuestMail(owner); // // var attribute = quest_mail.getEntityAttribute(); // if (attribute is null) // { // var err_msg = $"Fail to get QuestMailAttribute"; // result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); // return result; // } // // if (false == doc.getAttribWrappers().TryGetValue(typeof(QuestMailAttrib), out var to_copy_doc_attrib)) // { // var err_msg = $"Fail to get QuestMailAttrib"; // result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); // return result; // } // var attrib_base = to_copy_doc_attrib.getAttribBase(); // var doc_attrib = attrib_base as QuestMailAttrib; // if (doc_attrib is null) // { // var err_msg = $"Fail to get FriendAttrib"; // result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg); // return result; // } // // attribute.QuestId = doc_attrib.QuestId; // attribute.CreateTime = doc_attrib.CreateTime; // attribute.IsRead = doc_attrib.IsRead; // // // if (false == m_quest_mails.TryAdd(attribute.QuestId, quest_mail)) // { // var err_msg = $"m_quest_mails try Add Fail"; // result.setFail(ServerErrorCode.ServerLogicError, err_msg); // return result; // } return result; } public bool GS2C_NTF_DELETE_QUEST_MAIL_NOTI(UInt32 questId) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ClientToGame ntf_packet = new(); ntf_packet.Message = new(); ntf_packet.Message.DeleteQuestMailNoti = new(); ntf_packet.Message.DeleteQuestMailNoti.QuestId = questId; if (false == GameServerApp.getServerLogic().onSendPacket(owner, ntf_packet)) return false; return true; } public async Task normalMailActiveCheck() { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var normal_mail_set = await getNormalMailSet(); var result = new Result(); //메일이 삭제 됐으니 무조건 보낼수 있다. 단, 컨피그 데이터에서 최대 메일수를 줄여버리면 몇개 거절해도 할당 되면 안되니 여기서 리턴 if (MetaHelper.GameConfigMeta.MaxQuestSendMail <= normal_mail_set.Count) return result; //메일 보낼 수 있는 퀘스트가 없으면 return; var assignable_mail_set = await getAssignableMailSet(); if (assignable_mail_set.Count < 1) { return result; } var repeat_quest_action = player.getEntityAction(); //이미 체크 중이면 리턴 if (repeat_quest_action.isChecking() == 1) return result; var server_logic = GameServerApp.getServerLogic(); var fn_transaction_runner = async delegate () { var result = new Result(); DateTime now_dt = DateTimeHelper.Current; var next_allocate_dt = now_dt.AddMinutes(MetaHelper.GameConfigMeta.CooltimeNormalQuestSendMail); repeat_quest_action.setRepeatQuestInfo(next_allocate_dt, 1); var batch = new QueryBatchEx(player, LogActionType.QuestMainRepeatTimeRefresh, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } var repeat_quest_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(repeat_quest_attribute, () => $"repeat_quest_attribute is null !!!"); batch.appendBusinessLog(new RepeatQuestBusinessLog(repeat_quest_attribute.m_next_allocate_time, repeat_quest_attribute.m_is_checking)); result = await QueryHelper.sendQueryAndBusinessLog(batch); return result; }; result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "NormalMailCheckActive", fn_transaction_runner); if (result.isFail()) { return result; } QuestManager.It.m_normal_quest_check_users.TryAdd(player.getUserGuid(), 1); return result; } public async Task> getNormalMailSet() { HashSet normalMailSet = new(); foreach (var mailQuestKey in m_quest_mails.Keys) { (var result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(mailQuestKey.Item2); if (result.isFail()) continue; var quest_base_info = quest_meta_all_base_info.m_quest_base_info; NullReferenceCheckHelper.throwIfNull(quest_base_info, () => $"quest_base_info is null !!!"); if (quest_base_info.QuestType.Equals(MetaAssets.EQuestType.NORMAL.ToString()) && quest_base_info.OncePeriodRangeType.Equals(OncePeriodRangeType.Nolimit)) { normalMailSet.Add(mailQuestKey.Item2); } } return normalMailSet; } public async Task> getAssignableMailSet() { var normal_mails = await getNormalMailSet(); var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var quest_action = owner.getEntityAction(); var end_quest_action = owner.getEntityAction(); var quest_ids = quest_action.getSystemQuestIds(); //var my_quest_ids = quests.Keys.ToList(); var end_quests = end_quest_action.getEndQuests(); HashSet assignable_quest_ids = new(); foreach (var questGroup in QuestManager.It._QuestNormalGroup) { if (questGroup.Key == 0 || end_quests.ContainsKey((questGroup.Key, 0))) { foreach (var quest_id in questGroup.Value.ToList()) { //메일에 있으니 continue if (normal_mails.Contains(quest_id)) continue; if (quest_ids.Contains(quest_id)) continue; assignable_quest_ids.Add(quest_id); } } } return assignable_quest_ids; } public async Task<(Result, List, List)> sendQuestMails(List questIds) { var result = new Result(); var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); List quest_mails = new(); List log_invokers = new(); foreach (var quest_id in questIds) { (result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(quest_id); if (result.isFail()) continue; //여기에 Revision이 들어야가되면 0 대신 revision 값 셋팅 해야함 (var quest_mail, var log_invoker) = createQuestMail(quest_id, 0, true); if (quest_mail is null) continue; NullReferenceCheckHelper.throwIfNull(log_invoker, () => $"log_invoker is null !!!"); quest_mails.Add(quest_mail); log_invokers.Add(log_invoker); } return (result, quest_mails, log_invokers); } public async Task<(Result, List)> sendQuestMailsWithTransaction(List questIds) { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var server_logic = GameServerApp.getServerLogic(); List quest_mails = new(); var fn_transaction_runner = async delegate () { (var result, quest_mails, var log_invokers) = await sendQuestMails(questIds); var batch = new QueryBatchEx(player, LogActionType.None, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } batch.appendBusinessLogs(log_invokers); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { return result; } return result; }; var result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "SendQuestMails", fn_transaction_runner); if (result.isFail()) { var err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); return (result, new()); } await addNewQuestMails(quest_mails); await send_NTF_RECEIVED_QUEST_MAIL(player, questIds); await QuestManager.It.QuestCheck(player, new QuestMail(EQuestEventTargetType.MAIL, EQuestEventNameType.RECEIVED, "")); return (result, quest_mails); } public async Task send_NTF_RECEIVED_QUEST_MAIL(Player owner, List questIds) { List infos = new(); foreach (var id in questIds) { var quest_mail_info = await getQuestMailInfo(id); if (quest_mail_info is null) continue; infos.Add(quest_mail_info); } ClientToGame clientToGameNoti = new(); clientToGameNoti.Message = new(); clientToGameNoti.Message.ReceiveQuestMailNoti = new(); clientToGameNoti.Message.ReceiveQuestMailNoti.QuestMailInfo.AddRange(infos); if (false == GameServerApp.getServerLogic().onSendPacket(owner, clientToGameNoti)) { Log.getLogger().error($"NTF_RECEIVED_QUEST_MAIL send error questIds = {JsonConvert.SerializeObject(questIds)}"); } } private async Task getQuestMailInfo(UInt32 questId) { //여기에 Revision이 들어야가되면 0 대신 revision 값 셋팅 해야함 return await convertQuestMailsToProtoQuestMailInfo(questId, 0); } public async Task deleteQuestMail(UInt32 questId, UInt32 questRevision) { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); var quest_type = await QuestMetaHelper.getQuestType(player, questId, questRevision); var result = new Result(); if (false == m_quest_mails.TryRemove((quest_type, questId), out var quest_mail)) { var err_msg = $"deleteQuestMail, Quest Mail Not Exist, questId : {questId}"; result.setFail(ServerErrorCode.QuestMailNotExist, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } private (ServerCommon.QuestMail?, ILogInvokerEx?) createQuestMail(UInt32 questId, UInt32 questRevision, bool checkPeriod) { var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); DateTime now_dt = DateTimeHelper.Current; var quest_mail = new ServerCommon.QuestMail(owner); //await quest_mail.onInit(); var attribute = quest_mail.getEntityAttribute(); if (attribute is null) { return (null, null); } attribute.QuestId = questId; attribute.CreateTime = now_dt; attribute.IsRead = 0; attribute.QuestRevision = questRevision; //checkPeriod == false면 기간을 체크하지 않는다. if (checkPeriod && MetaHelper.GameConfigMeta.QuestMailStoragePeriod > 0) { attribute.ExpireTime = now_dt.AddSeconds(MetaHelper.GameConfigMeta.QuestMailStoragePeriod); } attribute.newEntityAttribute(); var log_invoker = new QuestMailBusinessLog(attribute, QuestMailLogType.Create); return (quest_mail, log_invoker); } public async Task setQuestMailFromDoc(QuestMailDoc doc) { var result = new Result(); var quest_mail_attrib = doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(quest_mail_attrib, () => $"quest_mail_attrib is null !!!"); var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ServerCommon.QuestMail mail = new ServerCommon.QuestMail (owner); //await mail.onInit(); var mail_attribute = mail.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(mail_attribute, () => $"mail_attribute is null !!!"); mail_attribute.QuestId = quest_mail_attrib.QuestId; mail_attribute.QuestRevision = quest_mail_attrib.QuestRevision; mail_attribute.CreateTime = quest_mail_attrib.CreateTime; mail_attribute.ExpireTime = quest_mail_attrib.ExpireTime; mail_attribute.IsRead = quest_mail_attrib.IsRead; mail_attribute.syncOriginDocBaseWithNewDoc(doc); var quest_type = await QuestMetaHelper.getQuestType(owner, quest_mail_attrib.QuestId, quest_mail_attrib.QuestRevision); m_quest_mails.TryAdd((quest_type, quest_mail_attrib.QuestId), mail); return result; } public async Task addNewQuestMails(List questMails) { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); foreach (var quest_mail in questMails) { var quest_mail_attribute = quest_mail.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(quest_mail_attribute, () => $"quest_mail_attribute is null !!!"); //여기에 Revision이 들어야가되면 0 대신 revision 값 셋팅 해야함 var quest_type = await QuestMetaHelper.getQuestType(player, quest_mail_attribute.QuestId, 0); m_quest_mails.TryAdd((quest_type, quest_mail_attribute.QuestId), quest_mail); } } public void setQuestMailLoadedAll() { m_is_quest_mail_loaded = true; } public bool isMailLoadedAll() { return m_is_quest_mail_loaded; } public Result GS2C_NTF_RECEIVED_QUEST_MAIL(QuestMailInfo mailInfo) { var result = new Result(); var owner = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ClientToGame ntf_packet = new(); ntf_packet.Message = new(); ntf_packet.Message.ReceiveQuestMailNoti = new(); ntf_packet.Message.ReceiveQuestMailNoti.QuestMailInfo.Add(mailInfo); GameServerApp.getServerLogic().onSendPacket(owner, ntf_packet); return result; } public async Task<(Result, ServerCommon.QuestMail, ILogInvoker)> sendQuestMail(UInt32 questId) { (var result, var quest_mails, var log_invokers) = await sendQuestMails(new List() { questId } ); return (result, quest_mails[0], log_invokers[0]); } public async Task<(List, List, List)> questMailSendOrForceAccept(List questIds) { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); List mail_quests = new(); List accept_quest_variables = new(); List send_quest_mails = new(); List log_invokers = new(); var result = new Result(); foreach (var quest_id in questIds) { (result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(quest_id); if (result.isFail()) continue; var quest_base_info = quest_meta_all_base_info.m_quest_base_info; NullReferenceCheckHelper.throwIfNull(quest_base_info, () => $"quest_base_info is null !!!"); if (quest_base_info.AssignType.Equals(nameof(EAssignType.MAIL)) && quest_base_info.ForceAccept == false) { mail_quests.Add(quest_id); continue; } else if (quest_base_info.AssignType.Equals(nameof(EAssignType.MAIL)) && quest_base_info.ForceAccept == true) { var quest_accept_action = player.getEntityAction(); (result, var quest_var) = await quest_accept_action.questAccept(quest_id); if (result.isFail()) continue; NullReferenceCheckHelper.throwIfNull(quest_var, () => $"quest_var is null !!!"); accept_quest_variables.Add(quest_var); } } if (mail_quests.Count > 0) { (result, var quest_mails, log_invokers) = await sendQuestMails(mail_quests); if(result.isSuccess()) send_quest_mails.AddRange(quest_mails); } return (accept_quest_variables, send_quest_mails, log_invokers); } public async Task questMailSendOrForceAcceptWithTransaction(QuestRewardHandler questRewardHandler, List questIds) { var player = getOwner() as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!!"); List mail_quests = new(); foreach (UInt32 quest_id in questIds) { (var result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(quest_id); if (result.isFail()) continue; var quest_base_info = quest_meta_all_base_info.m_quest_base_info; NullReferenceCheckHelper.throwIfNull(quest_base_info, () => $"quest_base_info is null !!!"); if (quest_base_info.QuestType.Equals(nameof(MetaAssets.EQuestType.NORMAL)) && quest_base_info.OncePeriodRangeType.Equals(OncePeriodRangeType.Nolimit)) { continue; } //Nolimit 아님 Normal 중에 이미 한번 받은 이력있는건 더 못받는다.. if (quest_base_info.QuestType.Equals(nameof(MetaAssets.EQuestType.NORMAL)) && questRewardHandler.m_has_already_end_quest) { continue; } if (quest_base_info.AssignType.Equals(nameof(MetaAssets.EAssignType.MAIL)) && quest_base_info.ForceAccept == false) { mail_quests.Add(quest_id); continue; } else if (quest_base_info.AssignType.Equals(nameof(MetaAssets.EAssignType.MAIL)) && quest_base_info.ForceAccept == true) { var quest_action = player.getEntityAction(); await quest_action.questForceAcceptWithTransaction(quest_id); } } if (mail_quests.Count > 0) { (var result, var quest_mails) = await sendQuestMailsWithTransaction(mail_quests); } } } }