using System.Collections.Concurrent; using Newtonsoft.Json; using ServerCore; using ServerBase; using ServerCommon; using MetaAssets; namespace GameServer; public class QuestCheckReferrence { public int m_quest_id { get; set; } = 0; public QuestBaseInfo? m_quest_meta_info_nullable { get; set; } = null; public ServerCommon.Quest? m_quest_nullable { get; set; } = null; public List m_rewardable_items { get; set; } = new(); public List m_deletable_items { get; set; } = new(); public List m_rewared_money { get; set; } = new(); public List m_rewarded_items { get; set; } = new(); } public class QuestManager : Singleton { public QuestManager() { Load(); } /// /// 퀘스트 활성화가 되기 위한 타입별로 저장 /// 캐릭터 생성시, 특정퀘스트 완료시 /// public Dictionary> _QuestReqTypeGroup = new(); /// /// 퀘스트 활성화가 되기 위한 값별로 정보저장 /// 특정퀘스트 완료시에만 의미가 있으며 QuesId 기준으로 정리해서 저장 /// NORAM 은 반복수 행 처리를 위해 해당 리스트에서 제외 /// public Dictionary> _QuestReqValueGroup = new(); /// /// Normal 퀘스트만 저장. 반복 수행을 위함 /// public Dictionary> _QuestNormalGroup = new(); /// /// requirementType 이 DailyUniqueLogin 인것과 OncePeriodRange가 Daily 인것만 저장 /// public HashSet _DailyQuestQuestGroup = new(); /// /// OncePeriodRange가 Weekly 인것만 저장 /// public HashSet _WeeklyQuestQuestGroup = new(); /// /// OncePeriodRange가 Monthly 인것만 저장 /// public HashSet _MonthlyQuestQuestGroup = new(); /// /// Dialoque 에 따른 퀘스트 정보 /// public Dictionary<(string, string), HashSet> _QuestDialogueGroup = new(); /// /// 타이머 체크 필요한 유저 /// public ConcurrentDictionary m_timer_check_users { get; set; } = new(); public ConcurrentDictionary m_normal_quest_check_users { get; set; } = new(); public void Load() { foreach (var questData in MetaData.Instance._QuestBaseinfoTable.Values) { if (_QuestReqTypeGroup.TryGetValue(questData.RequirementType, out var outTypeList) == false) { outTypeList = new HashSet(); } outTypeList.Add(questData.QuestId); _QuestReqTypeGroup.TryAdd(questData.RequirementType, outTypeList); if (_QuestReqValueGroup.TryGetValue(questData.RequirementValue, out var outValueList) == false) { outValueList = new HashSet(); } outValueList.Add(questData.QuestId); if (!questData.QuestType.Equals(EQuestType.NORMAL.ToString())) { _QuestReqValueGroup.TryAdd(questData.RequirementValue, outValueList); } else { normalQuestLoad(questData); } if (!questData.Dialogue.Equals(string.Empty)) { if (_QuestDialogueGroup.TryGetValue((questData.Dialogue, questData.DialogueResult), out var outDialugueQuestList) == false) { outDialugueQuestList = new(); } outDialugueQuestList.Add(questData.QuestId); } } } private void normalQuestLoad(QuestBaseInfo questData) { UInt32 requireValue = 0; if (questData.RequirementType.Equals(EAssignRequireType.PlayerInitial.ToString())) requireValue = 0; else requireValue = questData.RequirementValue; addNormalList(questData, requireValue); addPeriodList(questData); } private void addPeriodList(QuestBaseInfo questData) { if (questData.OncePeriodRangeType.Equals(OncePeriodRangeType.Daily)) { if (false == questData.NormalPoolActive) { _DailyQuestQuestGroup.Add(questData.QuestId); } } else if(questData.OncePeriodRangeType.Equals(OncePeriodRangeType.Weekly) && questData.NormalPoolActive == false) { _WeeklyQuestQuestGroup.Add(questData.QuestId); } else if(questData.OncePeriodRangeType.Equals(OncePeriodRangeType.Monthly) && questData.NormalPoolActive == false) { _MonthlyQuestQuestGroup.Add(questData.QuestId); } } private void addNormalList(QuestBaseInfo questData, UInt32 requireValue) { if (requireValue == 0) return; if (_QuestNormalGroup.TryGetValue(requireValue, out var outNormalList) == false) { outNormalList = new HashSet(); } if (questData.RequirementType.Equals(EAssignRequireType.PlayerInitial.ToString())) { if (questData.OncePeriodRangeType.Equals(OncePeriodRangeType.Nolimit)) { outNormalList.Add(questData.QuestId); _QuestNormalGroup.TryAdd(requireValue, outNormalList); } else { //기간 반복 메일인데 NormalPoolActive 가 true면 반복메일에서 제외하고 노멀 메일로 넣는다. if (questData.NormalPoolActive) { outNormalList.Add(questData.QuestId); _QuestNormalGroup.TryAdd(requireValue, outNormalList); } } } else if (questData.RequirementType.Equals(EAssignRequireType.Quest_Complete.ToString())) { if (questData.OncePeriodRangeType.Equals(OncePeriodRangeType.Nolimit)) { outNormalList.Add(questData.QuestId); _QuestNormalGroup.TryAdd(requireValue, outNormalList); } else { //기간 반복 메일인데 NormalPoolActive 가 true면 반복메일에서 제외하고 노멀 메일로 넣는다. if (questData.NormalPoolActive) { outNormalList.Add(questData.QuestId); _QuestNormalGroup.TryAdd(requireValue, outNormalList); } } } } public async Task QuestCheckOneByOne(Player player, IQuest quest_base, ServerCommon.Quest quest, bool hasTransaction) { //todo : 성눙 안좋다. 바꾸는게 좋다... var quest_attribute = quest.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(quest_attribute, () => $"quest_attribute is null !!!"); var quest_id = quest_attribute.QuestId; var quest_revision = quest_attribute.UgqInfo.QuestRevision; QuestTaskUpdateHandler quest_update_handler = new(player, quest_id, quest_revision, ""); var result = await quest_update_handler.init(player, quest); if (result.isFail()) { return result; } result = await QuestCheckOne(player, quest_base, hasTransaction, quest_update_handler); if (result.isFail()) { return result; } if (quest_update_handler.m_need_counter_check) { QuestTaskUpdateHandler sequential_quest_update_handler = new(player, quest_id, quest_revision, $"{EQuestEventTargetType.COUNTER.ToString()}{EQuestEventNameType.MAX.ToString()}"); result = await sequential_quest_update_handler.init(player, quest); if (result.isFail()) return result; IQuest sequential_quest_base = new QuestCount(EQuestEventTargetType.COUNTER, EQuestEventNameType.MAX); await QuestSequentialCheck(player, sequential_quest_base, hasTransaction, sequential_quest_update_handler); } return result; } public async Task QuestSequentialCheck(Player player, IQuest quest_base, bool hasTransaction, QuestTaskUpdateHandler questUpdateHandler) { var result = await QuestCheckOne(player, quest_base, hasTransaction, questUpdateHandler); return result; } public async Task QuestCheckOne(Player player, IQuest quest_base, bool hasTransaction, QuestTaskUpdateHandler questUpdateHandler) { ArgumentNullReferenceCheckHelper.throwIfNull(questUpdateHandler.m_quest_meta_info, () => $"questUpdateHandler.m_quest_meta_info is null !!!"); var result = new Result(); NullReferenceCheckHelper.throwIfNull(questUpdateHandler.m_quest, () => $"questUpdateHandler.m_quest is null !!!"); var quest_attribute = questUpdateHandler.m_quest.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(quest_attribute, () => $"quest_attribute is null !!!"); var quest_id = questUpdateHandler.m_quest_id; var quest_revision = quest_attribute.UgqInfo.QuestRevision; if (false == questUpdateHandler.m_quest_meta_info.QuestTaskGroupList.TryGetValue( quest_attribute.CurrentTaskNum, out var quest_task_info)) { var err_msg = $"quest_task_info not found. quest_id : {quest_id}, quest_attribute.CurrentTaskNum : {quest_attribute.CurrentTaskNum}"; result.setFail(ServerErrorCode.QuestInvalidTaskNum, err_msg); Log.getLogger().error(result.toBasicString()); return result; } questUpdateHandler.m_quest_task_group = quest_task_info; List active_events = new(); active_events.AddRange(quest_attribute.ActiveEvents); bool is_valid = false; foreach (var active_event in active_events) { if (quest_attribute.IsComplete == 1) break; questUpdateHandler.m_active_event = new(active_event); if (false == quest_task_info.QuestEventGroupByEventString.TryGetValue(active_event, out var quest_event_info)) { var err_msg = $"QuestEventGroupByEventString not found. quest_id : {quest_id}, event_str : {active_event}"; result.setFail(ServerErrorCode.QuestInvalidValue, err_msg); Log.getLogger().error(result.toBasicString()); break; } is_valid = quest_base.checkValidTaskScript(quest_attribute, questUpdateHandler); if (is_valid) { if (hasTransaction) { result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "QuestCheckAndTaskUpdate", delegateQuestCheckAndTaskUpdate); Task delegateQuestCheckAndTaskUpdate() => questCheckAndTaskUpdate(player, quest_base, questUpdateHandler, hasTransaction); } else { result = await questCheckAndTaskUpdate(player, quest_base, questUpdateHandler, hasTransaction); } if (result.isFail()) { string err_msg = $"Failed to questCheckAndTaskUpdate()!!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); return result; } await quest_base.postProcess(player, questUpdateHandler); break; } } // rank 누적 ( ugcNpc 제공 ugq quest 만 ) if (is_valid && quest_attribute.UgqInfo.QuestRevision > 0 && quest_attribute.IsComplete == 1) { var ugq_info_action = player.getEntityAction(); (result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(player, quest_attribute.QuestId, quest_attribute.UgqInfo.QuestRevision, quest_attribute.UgqInfo.UqgState); if (result.isFail()) return result; var game_quest_data = quest_meta_all_base_info.m_quest_data_entity; NullReferenceCheckHelper.throwIfNull(game_quest_data, () => $"game_quest_data is null !!!"); if (string.IsNullOrEmpty(game_quest_data.UgcBeaconGuid)) return result; var rank_entity = GameServerApp.getServerLogic().findGlobalEntity(); NullReferenceCheckHelper.throwIfNull(rank_entity, () => $"rank_entity is null !!!"); var quest_rank_action = rank_entity.getEntityAction(); NullReferenceCheckHelper.throwIfNull(quest_rank_action, () => $"quest_rank_action is null !!!"); await quest_rank_action.setRankScore(player.getUserGuid(), game_quest_data.UserGuid, game_quest_data.UgcBeaconGuid, true); } return result; } private async Task questCheckAndTaskUpdate(Player player, IQuest questBase, QuestTaskUpdateHandler questTaskUpdateHandler, bool hasTransaction) { var quest = questTaskUpdateHandler.m_quest; NullReferenceCheckHelper.throwIfNull(quest, () => $"quest is null !!!"); var quest_attribute = quest.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(quest_attribute, () => $"quest_attribute is null !!!"); //해당 태스크 달성 했으므로 다음 펑션 처리 var result = questBase.questTaskFunctionProgress(player, ref questTaskUpdateHandler); if (result.isFail()) return result; if (quest_attribute.CurrentTaskComplete == 1) { result = QuestNextTaskUpdate(quest_attribute, ref questTaskUpdateHandler); if (result.isFail()) return result; } quest_attribute.modifiedEntityAttribute(); //보상 줄게 있으면 보상처리 IReward reward_base = new RewardQuestTask(player, questTaskUpdateHandler); //보상 주다 아이템 풀차면 풀찼다고 예외 처리 20240828 result = await RewardManager.It.proceedRewardProcess(reward_base); if (result.isFail()) { return result; } var inventory_action = player.getEntityAction(); //삭제할게 있으면 삭체 처리 foreach (var delete_item in questTaskUpdateHandler.m_deletable_items) { var item_id = delete_item.Key; var delete_count = delete_item.Value; (result, var found_delete_item) = await inventory_action.tryDeleteItemByMetaId((uint)item_id, (ushort)delete_count); if (result.isFail()) { return result; } questTaskUpdateHandler.m_deleted_items.AddRange(found_delete_item); } var common_result = new CommonResult(); if (hasTransaction) { var server_logic = GameServerApp.getServerLogic(); var batch = new QueryBatchEx(player, LogActionType.QuestMainTask, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } questTaskUpdateHandler.m_log_invokers.Add(new QuestTaskUpdateBusinessLog(questTaskUpdateHandler)); batch.appendBusinessLogs(questTaskUpdateHandler.m_log_invokers); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) return result; var found_transaction_runner = player.findTransactionRunner(TransactionIdType.PrivateContents); NullReferenceCheckHelper.throwIfNull(found_transaction_runner, () => $"found_transaction_runner is null !!! - {player.toBasicString()}"); common_result = found_transaction_runner.getCommonResult(); } //여기서 QuestUpdateNoti 보낸다. await player.send_GS2C_NTF_QUEST_UPDATE(quest_attribute.QuestId, quest_attribute.UgqInfo.QuestRevision, common_result, false); await checkLastFunction(player, questTaskUpdateHandler); return result; } public async Task checkLastFunction(Player player, QuestTaskUpdateHandler questTaskUpdateHandler) { var result = new Result(); var quest_accep_action = player.getEntityAction(); foreach (var last_check_func in questTaskUpdateHandler.m_last_check_functions) { if (last_check_func.FunctionTarget.Equals(nameof(EQuestFunctionTargetType.QUEST)) && last_check_func.FunctionName.Equals(nameof(EQuestFunctionNameType.ASSIGN))) { var new_quest_id = uint.Parse(last_check_func.FunctionCondition1); (result, var quest) = await quest_accep_action.questAcceptWithTransaction(new_quest_id, 0, 0); } else if (last_check_func.FunctionTarget.Equals(nameof(EQuestFunctionTargetType.PROP)) && last_check_func.FunctionName.Equals(nameof(EQuestFunctionNameType.SWITCH))) { var prop_id = int.Parse(last_check_func.FunctionCondition1); var prop_state = int.Parse(last_check_func.FunctionCondition2); var switching_prop_action = player.getEntityAction(); await switching_prop_action.updateSwitchingProp(questTaskUpdateHandler, prop_id, prop_state); } } return result; } public async Task checkNotifyByServerChange(Player player, QuestTaskUpdateHandler questTaskUpdateHandler, string activeEvent) { if (activeEvent.Contains(EQuestEventTargetType.INTERIORMODE.ToString() + EQuestEventNameType.STOPPED.ToString())) { await QuestNotifyHelper.sendRedisQuestNotifyRequest(player, questTaskUpdateHandler); } } public async Task QuestCheck(Player player, IQuest quest) { var quest_action = player.getEntityAction(); var quests = quest_action.getQuests(); var result = new Result(); foreach (var curr_quest in quests.Values) { result = await QuestCheckOneByOne(player, quest, curr_quest, true); if (result.isFail()) { var err_msg = $"quest fail : {JsonConvert.SerializeObject(curr_quest)}"; continue; } } return result; } public async Task QuestCheckWithoutTransaction(Player player, IQuest quest) { var quest_action = player.getEntityAction(); var quests = quest_action.getQuests(); var result = new Result(); foreach (var curr_quest in quests.Values) { result = await QuestCheckOneByOne(player, quest, curr_quest, false); if (result.isFail()) { var err_msg = $"quest fail : {JsonConvert.SerializeObject(curr_quest)}"; continue; } } return result; } private void collectQuestTaskLog(object player, IReward reward_proc, UserQuestInfo userQuest, List rewardDatas, Dictionary deleteItem) { // int quest_id = userQuest.m_quest_id; // int task_num = userQuest.m_current_task_num; // int is_complete = userQuest.m_is_complete; // collectQuestTaskLog(player, reward_proc, quest_id, task_num, is_complete, rewardDatas, deleteItem); } private void collectQuestTaskLog(object player, IReward reward_proc, int questId, int taskNum, int isComplete, List rewardData, Dictionary deleteItem) { // List logInvokers = new List(); // // if (!TableData.Instance._QuestBaseinfoTable.TryGetValue(questId, out var questBaseInfo)) // { // Log.getLogger().error($"collectQuestTaskLogs _QuestScriptDataTable InvalidData questId : {questId}"); // return; // } // // if (!questBaseInfo.QuestTaskGroupList.TryGetValue(taskNum, out var taskGroup)) // { // Log.getLogger().error($"collectQuestTaskLogs QuestTaskGroupList InvalidData task_num : {taskNum}"); // return; // } // // QuestMainRewardLogInfo loginfo = new QuestMainRewardLogInfo(questId, questBaseInfo.AssignableQuestsAfterEnd, taskNum, taskGroup.NextTaskNum, isComplete, rewardData, deleteItem); // ILogInvoker questRewardInvoker = new QuestRewardBusinessLog(loginfo); // logInvokers.Add(questRewardInvoker); // logInvokers.AddRange(reward_proc.getCurrencyLogInvokers()); // logInvokers.AddRange(reward_proc.getItemLogInvokers()); // logInvokers.AddRange(reward_proc.getItemDeleteInvokers()); // // var log_action = new LogAction(LogActionType.QuestMainTask); // BusinessLogger.collectLogs(log_action, player, logInvokers); } //QuestCheck 랑 분리 (QuestTaskUpdateReq를 위한 코드 - 나중에 일반화 필요) // internal ServerErrorCode QuestTaskCheck(object player, UserQuestInfo userQuestInfo, QuestBaseInfo dbQuestInfo, QuestTaskGroup questTaskInfo, int activeIdx, IQuest quest, // ref QuestListEntity newQuestListEntity, // ref ConcurrentDictionary rewardDocument, // ref List itemEntityList, // ref List rewards) // { // //내 퀘스트 정보에 현재 활성화 중인 태스크 돌면서 체크 // // ServerErrorCode errorCode = ServerErrorCode.Success; // // // // int quest_id = userQuestInfo.m_quest_id; // // // // //해당 태스크 달성 했으므로 다음 태스크 따라간다. // // errorCode = quest.questTaskFunctionProgress(player.accountId, dbQuestInfo, ref userQuestInfo, questTaskInfo, activeIdx, out var rewardList, out var deletedItems); // // if (errorCode != ServerErrorCode.Success) // // { // // Log.getLogger().error($"questTaskNextProgress return false accountId : {player.accountId}, questId : {quest_id}, idx : {activeIdx}"); // // return ServerErrorCode.QuestInvalidValue; // // } // // // // rewards.AddRange(rewardList); // // // // if (userQuestInfo.m_is_current_task_complete == 1) // // { // // errorCode = QuestNextTaskUpdate(dbQuestInfo, userQuestInfo, quest_id, questTaskInfo); // // if (errorCode != ServerErrorCode.Success) return errorCode; // // } // // Dictionary> delete_items= new(); // // delete_items.TryAdd(quest_id, deletedItems); // // // // Dictionary> reward_dictionary = new(); // // reward_dictionary.TryAdd(quest_id, rewards); // // // // List update_quest_ids = new() { quest_id }; // // (_, IReward reward_proc) = save(player, update_quest_ids, newQuestListEntity, reward_dictionary, delete_items); // // itemEntityList = reward_proc.getItemEntities(); // // // // collectQuestTaskLog(player, reward_proc, userQuestInfo, rewards, deletedItems); // // return ServerErrorCode.Success; // } // internal ServerErrorCode QuestTaskCheckWithEventString(object player, UserQuestInfo userQuestInfo, QuestBaseInfo dbQuestInfo, QuestTaskGroup questTaskInfo, string activeEvent, IQuest quest, // ref QuestListEntity newQuestListEntity, // ref ConcurrentDictionary rewardDocument, // ref List itemEntityList, // ref List rewards) // { // //내 퀘스트 정보에 현재 활성화 중인 태스크 돌면서 체크 // // ServerErrorCode errorCode = ServerErrorCode.Success; // // // // int quest_id = userQuestInfo.m_quest_id; // // //해당 태스크 달성 했으므로 다음 태스크 따라간다. // // errorCode = quest.questTaskFunctionProgressWithEventString(player.accountId, dbQuestInfo, ref userQuestInfo, questTaskInfo, activeEvent, out var rewardList, out var deletedItems); // // if (errorCode != ServerErrorCode.Success) // // { // // Log.getLogger().error($"questTaskNextProgress return false accountId : {player.accountId}, questId : {quest_id}, activeEvent : {activeEvent}"); // // return ServerErrorCode.QuestInvalidValue; // // } // // rewards.AddRange(rewardList); // // if (userQuestInfo.m_is_current_task_complete == 1 && userQuestInfo.m_is_complete == 0) // { // errorCode = QuestNextTaskUpdate(dbQuestInfo, userQuestInfo, quest_id, questTaskInfo); // if (errorCode != ServerErrorCode.Success) return errorCode; // } // // Dictionary> delete_items = new(); // delete_items.TryAdd(quest_id, deletedItems); // // Dictionary> reward_dictionary = new(); // reward_dictionary.TryAdd(quest_id, rewards); // // List update_quest_ids = new() { quest_id }; // (_, IReward reward_proc) = save(player, update_quest_ids, newQuestListEntity, reward_dictionary, delete_items); // itemEntityList = reward_proc.getItemEntities(); // // collectQuestTaskLog(player, reward_proc, userQuestInfo, rewards, deletedItems); // // return ServerErrorCode.Success; // } // internal async Task cheatQuestCheck(object player, IQuest quest, int questId) // { // QuestListEntity newQuestListEntity = new QuestListEntity(player.ownedQuestList.questListEntity.DocumentForUpdate()); // // List rewards = new List(); // ServerErrorCode error_code = ServerErrorCode.Success; // // if (!newQuestListEntity.Quests.TryGetValue(questId, out var userQuestInfo)) // { // Log.getLogger().error($"userQuestInfo is null questId : {questId}, player : {player.accountId}"); // return; // } // // if (!TableData.Instance._QuestBaseinfoTable.TryGetValue(questId, out var dbQuestInfo)) // { // Log.getLogger().error($"_QuestScriptDataTable InvalidData questId : {questId}"); // return; // } // if (!dbQuestInfo.QuestTaskGroupList.TryGetValue(userQuestInfo.m_current_task_num, out var dbQuestTaskInfo)) // { // Log.getLogger().error($"questTaskInfo InvalidData CurrentTaskNum : {userQuestInfo.m_current_task_num}, questId : {questId}"); // return; // } // // if (userQuestInfo.m_active_idx_list.Count == 0) // { // Log.getLogger().error($"m_active_idx_list count zero : {userQuestInfo.m_current_task_num}, questId : {questId}"); // return; // } // // //다음 태스크 따라간다. // error_code = quest.questTaskFunctionProgress(player.accountId, dbQuestInfo, ref userQuestInfo, dbQuestTaskInfo, userQuestInfo.m_active_idx_list.First(), out var rewardList, out var deletedItems); // if(error_code != ServerErrorCode.Success) // { // Log.getLogger().error($"chea questTaskFunctionProgress error : {userQuestInfo.m_current_task_num}, questId : {questId}"); // return; // } // rewards.AddRange(rewardList); // // if (userQuestInfo.m_is_current_task_complete == 1) // { // error_code = QuestNextTaskUpdate(dbQuestInfo, userQuestInfo, questId, dbQuestTaskInfo); // if (error_code != ServerErrorCode.Success) return; // } // // Dictionary> delete_items = new(); // delete_items.TryAdd(questId, deletedItems); // Dictionary> reward_dictionary = new(); // reward_dictionary.TryAdd(questId, rewards); // // List update_quest_ids = new() { questId }; // (error_code, IReward reward_proc ) = save(player, update_quest_ids, newQuestListEntity, reward_dictionary, delete_items); // if (error_code != ServerErrorCode.Success) // { // return; // } // // if (userQuestInfo.m_is_complete == 1) // { // } // } // internal (ServerErrorCode, IReward) save(object player, List updatedQuests, QuestListEntity questListEntity, Dictionary> rewards, Dictionary> deleteItem) // { // ConcurrentDictionary rewardDocument = new(); // // ServerErrorCode error_code = ServerErrorCode.Success; // //IReward reward_proc = new QuestTaskSave(player, updatedQuests, questListEntity, rewards, deleteItem); // //RewardManager.Instance.proceedRewardProcess(reward_proc).Wait(); // //RewardManager.Instance.postRewardProcess(reward_proc); // // //return (error_code, reward_proc); // return (error_code, null); // } private Result QuestNextTaskUpdate(QuestAttribute questAttribute, ref QuestTaskUpdateHandler questTaskUdateHandler) { var task_group = questTaskUdateHandler.m_quest_task_group; var next_task_num = questTaskUdateHandler.m_next_task_number; var result = new Result(); if (questAttribute.IsComplete == 1) { return result; } var quest_id = questTaskUdateHandler.m_quest_id; var meta_info = questTaskUdateHandler.m_quest_meta_info; NullReferenceCheckHelper.throwIfNull(meta_info, () => $"meta_info is null !!!"); if (!meta_info.QuestTaskGroupList.TryGetValue(next_task_num, out var next_quest_task)) { var err_msg = $"QuestNextTaskUpdate - QuestTaskGroupList not found. quest_id : {quest_id}, next_task_num : {next_task_num}"; result.setFail(ServerErrorCode.QuestInvalidValue, err_msg); Log.getLogger().error(result.toBasicString()); return result; } questAttribute.CurrentTaskNum = next_task_num; questAttribute.CurrentTaskComplete = 0; DateTime now = DateTime.UtcNow; questAttribute.TaskStartTime = now; questAttribute.ActiveEvents.Clear(); questAttribute.ActiveEvents.AddRange(next_quest_task.EventStringList); questAttribute.HasTimer = 0; questAttribute.TimerCompleteTime = now; questAttribute.CompletedIdxStrings.Clear(); questAttribute.HasCounter = 0; questAttribute.MaxCounter = 0; questAttribute.MinCounter = 0; questAttribute.CurrentCounter = 0; if (next_quest_task.HasCounter) { questAttribute.HasCounter = 1; questAttribute.MaxCounter = next_quest_task.MaxCounter; questAttribute.MinCounter = next_quest_task.MinCounter; questAttribute.CurrentCounter = next_quest_task.MinCounter; } return result; } public List getActiveEventStrings(QuestAttribute questAttribute, QuestTaskGroup questTaskInfo) { List activeEventStrings = new List(); if (questAttribute.IsComplete == 1) return activeEventStrings; if (questTaskInfo.IsParallel) { foreach (var dbEventInfo in questTaskInfo.QuestEventGroupByEventString.Values) { string str = MetaData.Instance.makeEventScriptStringByEventInfo(dbEventInfo); if (questAttribute.CompletedIdxStrings.Contains(str)) { continue; } activeEventStrings.Add(str); } } else { foreach (string eventString in questTaskInfo.EventStringList) { activeEventStrings.Add(eventString); } } return activeEventStrings; } public List getTimerCheckUser() { return m_timer_check_users.Keys.ToList(); } public void deleteTimerCheckUser(List deleteChecker) { foreach (string deleteUser in deleteChecker) { m_timer_check_users.TryRemove(deleteUser, out _); } } public async Task questTimerEventCheck() { List timer_check_user = m_timer_check_users.Keys.ToList(); List delete_checker = new(); var server_logic = GameServerApp.getServerLogic(); var player_manager = server_logic.getPlayerManager(); foreach (string user_guid in timer_check_user) { if (false == player_manager.tryGetUserByPrimaryKey(user_guid, out var player)) { delete_checker.Add(user_guid); continue; } var user_create_or_load_action = player.getEntityAction(); if (false == user_create_or_load_action.isCompletedLoadUser()) { continue; } await QuestCheck(player, new QuestTimer(EQuestEventTargetType.TIMER, EQuestEventNameType.ENDED)); } foreach (string deleteUser in delete_checker) { m_timer_check_users.TryRemove(deleteUser, out _); } } public async Task questNormalActiveCheck() { List delete_checker = new(); var server_logic = GameServerApp.getServerLogic(); var player_manager = server_logic.getPlayerManager(); foreach (var user_guid in m_normal_quest_check_users.Keys) { if (false == player_manager.tryGetUserByPrimaryKey(user_guid, out var player)) { delete_checker.Add(user_guid); continue; } var user_create_or_load_action = player.getEntityAction(); if (false == user_create_or_load_action.isCompletedLoadUser()) { continue; } await questNormalActiveCheckAndAssign(player); } foreach (var delete_user in delete_checker) { m_normal_quest_check_users.TryRemove(delete_user, out _); Log.getLogger() .debug($"setNormalMailTimer m_normal_quest_check_users remove accountId : {delete_user}"); } } private async Task questNormalActiveCheckAndAssign(Player player) { DateTime now_dt = DateTimeHelper.Current; var repeat_quest_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(repeat_quest_attribute, () => $"repeat_quest_attribute is null !!!"); if (repeat_quest_attribute.m_is_checking == 0) return; if (now_dt < repeat_quest_attribute.m_next_allocate_time) return; //플레이어 정보는 퀘스트 메일 보내기 전에 로드 된다. 메일이 로드 안되어 있으면 리턴 할수 있도록 처리 해줘야된다. var quest_mail_action = player.getEntityAction(); if (false == quest_mail_action.isMailLoadedAll()) return; HashSet my_current_normal_mail_set = await quest_mail_action.getNormalMailSet(); int normal_mail_count = my_current_normal_mail_set.Count; HashSet assignable_mail_set = await getAssignableMailSet(player, my_current_normal_mail_set); //체크 대상이라 여기 들어왔는데 메일이 넘치면 미체크 대상으로 처리 if (MetaHelper.GameConfigMeta.MaxQuestSendMail <= normal_mail_count || assignable_mail_set.Count == 0) { m_normal_quest_check_users.TryRemove(player.getUserGuid(), out _); return; } //여기까지 왔으면 메일 보낼게 있다는 얘기이므로 전송한다. var assignable_quest_id = getAssignableQuestMailId(assignable_mail_set); //여기서 normalMailSet + 1 의 개수가 최대 개수를 넘으면 비활성화 처리 var check_value = questCheckUserAssignableQuestMaxCheck(player, my_current_normal_mail_set); var result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "updateRepeatQuest", delegateSendQuestMailAndUpdateRepeatQuestInfo); async Task delegateSendQuestMailAndUpdateRepeatQuestInfo() => await sendQuestMailAndUpdateRepeatQuestInfo(player, assignable_quest_id, check_value); } public UInt32 getAssignableQuestMailId(HashSet assignableMailSet) { //여기까지 왔으면 메일 보낼게 있다는 얘기이므로 전송한다. Random random = new Random(); List assignableMailList = assignableMailSet.ToList(); var mails_count = assignableMailList.Count; var idx = random.Next(mails_count); var assign_quest_id = assignableMailList[idx]; return assign_quest_id; } public bool questCheckUserAssignableQuestMaxCheck(Player player, HashSet assignableMailSet) { bool check_value = true; if (MetaHelper.GameConfigMeta.MaxQuestSendMail <= assignableMailSet.Count + 1) { m_normal_quest_check_users.TryRemove(player.getUserGuid(), out _); check_value = false; } return check_value; } private async Task sendQuestMailAndUpdateRepeatQuestInfo(Player player, UInt32 questId, bool checkVlaue) { var quest_mail_action = player.getEntityAction(); (var result, var quest_mail, var quest_mail_log_invoker) = await quest_mail_action.sendQuestMail(questId); if (result.isFail()) { return result; } var repeat_quest_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(repeat_quest_attribute, () => $"repeat_quest_attribute is null !!!"); repeat_quest_attribute.m_next_allocate_time = repeat_quest_attribute.m_next_allocate_time.AddMinutes(MetaHelper.GameConfigMeta .CooltimeNormalQuestSendMail); if (false == checkVlaue) { repeat_quest_attribute.m_is_checking = 0; } repeat_quest_attribute.modifiedEntityAttribute(); var server_logic = GameServerApp.getServerLogic(); var batch = new QueryBatchEx(player, LogActionType.QuestMailSend, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } batch.appendBusinessLog(new RepeatQuestBusinessLog(repeat_quest_attribute.m_next_allocate_time, repeat_quest_attribute.m_is_checking)); batch.appendBusinessLog(quest_mail_log_invoker); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { Log.getLogger() .error($"cheatSendQuestMail - QueryHelper.sendQueryAndBusinessLog Fail : {result.toBasicString()}"); return result; } await quest_mail_action.addNewQuestMails(new List() { quest_mail }); await quest_mail_action.send_NTF_RECEIVED_QUEST_MAIL(player, new List() { questId }); await QuestCheck(player, new QuestMail(EQuestEventTargetType.MAIL, EQuestEventNameType.RECEIVED, "")); return result; } public async Task> getAssignableMailSet(Player player, HashSet normalMailSet) { var quest_action = player.getEntityAction(); var end_quest_action = player.getEntityAction(); var end_quests = end_quest_action.getEndQuests(); var quest_ids = quest_action.getSystemQuestIds(); HashSet assignableMailSet = new(); foreach (var quest_group in _QuestNormalGroup) { if (quest_group.Key == 0 || end_quests.ContainsKey((quest_group.Key, 0))) { foreach (var quest_id in quest_group.Value.ToList()) { (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 !!!"); //noLimit가 아니면 메일 줄수 없다. if(false == quest_base_info.OncePeriodRangeType.Equals(OncePeriodRangeType.Nolimit)) continue; //메일에 있으니 continue if (normalMailSet.Contains(quest_id)) continue; if (quest_ids.Contains(quest_id)) continue; assignableMailSet.Add(quest_id); } } } return assignableMailSet; } public async Task QuestCheckAtWarpRet(Player player, string roomString, int roomId) { var server_logic = GameServerApp.getServerLogic(); if (server_logic.getServerType().toServerType() == ServerType.Channel) return; var location_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(location_attribute, () => $"location_attribute is null !!!"); if (false == MetaData.Instance._IndunTable.TryGetValue(roomId, out var indun_data)) { return; } if (roomString == string.Empty) { return; } switch (indun_data.ContentsType) { case ContentsType.MyHome: { var roomIdElemnet = roomString.Split(":"); if (roomIdElemnet.Length != ServerCommon.Constant.MYHOME_INSTANCE_ROOM_ID_ELEMENT_COUNT) return; if (roomIdElemnet[1] == player.getUserGuid()) { //await player.send_GS2CS_NTF_FRIEND_LEAVE_MYHOME(); await QuestCheckWithoutTransaction(player, new QuestMyHome(EQuestEventTargetType.MYHOME, EQuestEventNameType.EXITED, "MYSELF")); } else { await QuestCheckWithoutTransaction(player, new QuestMyHome(EQuestEventTargetType.MYHOME, EQuestEventNameType.EXITED, "OTHERS")); } } break; case ContentsType.DressRoom: { await QuestCheckWithoutTransaction(player, new QuestDressRoom(EQuestEventTargetType.DRESSROOM, EQuestEventNameType.EXITED, "MYSELF")); } break; } } private void send_NTF_UGQ_STATE_CHANGED(Player player, Result result, UInt32 questId, UInt32 questRevision) { if (result.getErrorCode() != ServerErrorCode.UgqQuestRevisionChanged && result.getErrorCode() != ServerErrorCode.UgqQuestShutdowned) return; var server_logic = GameServerApp.getServerLogic(); var ntf_packet = new ClientToGame(); ntf_packet.Message = new(); ntf_packet.Message.NtfUgqStateChanged = new(); ntf_packet.Message.NtfUgqStateChanged.ComposedQuestId = QuestHelper.convertQuestIdAndRevisionToUgqQuestId(questId, questRevision); ntf_packet.Message.NtfUgqStateChanged.State = UgqStateType.Shutdown; if (result.getErrorCode() == ServerErrorCode.UgqQuestRevisionChanged) { ntf_packet.Message.NtfUgqStateChanged.State = UgqStateType.RevisionChanged; } server_logic.onSendPacket(player, ntf_packet); } public async Task PeriodRepeatQuestCheck(Player player) { await Task.CompletedTask; var result = new Result(); var user_create_or_load_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_create_or_load_action, () => $"user_create_or_load_action is null !!! - {player.toBasicString()}"); if(false == user_create_or_load_action.isCompletedLoadUser()) { return result; } var server_logic = GameServerApp.getServerLogic(); List quest_mails = new(); List log_invokers = new(); List sended_quest_ids = new(); Dictionary assigned_quests = new(); var quest_mail_action = player.getEntityAction(); var end_quest_action = player.getEntityAction(); var quest_action = player.getEntityAction(); var end_quests = end_quest_action.getEndQuests(); var now = DateTimeHelper.Current; var fn_transaction_runner = async delegate() { //리프레시 하면서 바로 퀘스트 메일도 지급 var period_repeat_quest_attibute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(period_repeat_quest_attibute, () => $"period_repeat_quest_attibute is null !!!"); if (period_repeat_quest_attibute.m_period_repeat_quests.Count == 0) makeDefaultPeriodRepeatQuests(period_repeat_quest_attibute); bool need_update = false; foreach (var info in period_repeat_quest_attibute.m_period_repeat_quests) { var periodRangeType = info.Key; var periodSendedQuest = info.Value; var all_sendable_mail_ids = getSendableMailIds(periodRangeType); foreach (var id in all_sendable_mail_ids) { (result, var quest_meta_all_base_info) = await QuestMetaManager.It.getQuestMeta(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.RequirementType.Equals(EAssignRequireType.Quest_Complete.ToString())) { //완료한 퀘스트가 없으면 daily에서 당장은 줄수 없다. if (false == end_quests.ContainsKey((quest_base_info.RequirementValue, 0))) { continue; } } if (quest_base_info.NormalPoolActive) continue; if (periodSendedQuest.m_sended_quest.TryGetValue(id, out var nextRefleshTime)) { //아직 refleshTime이 시간이 안됐으면 continue if (now < nextRefleshTime) continue; } if (quest_base_info.ForceAccept) { //이미 들고 있는 퀘스트면 continue; result = quest_action.hasQuest(EQuestType.NORMAL, id, 0); if (result.isSuccess()) continue; //AcceptQuestVariable? accept_quest_var = null; var quest_accept_action = player.getEntityAction(); (result, var accept_quest_var) = await quest_accept_action.questAccept(id); if (result.isFail() || accept_quest_var is null) return result; var assigned_quest = accept_quest_var.m_new_quest_nullable; NullReferenceCheckHelper.throwIfNull(assigned_quest, () => $"Quest is null !!!"); var assigned_quest_attribute = assigned_quest.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(assigned_quest_attribute, () => "QuestAttribute is null !!!"); var assigned_quest_id = assigned_quest_attribute.QuestId; assigned_quests.TryAdd(assigned_quest_id, assigned_quest); } else { //이미 받은 메일이 있다면 체크 안한다. result = quest_mail_action.hasQuestMail(EQuestType.NORMAL, id, false); if (result.isSuccess()) continue; (result, var quest_mail, var quest_mail_log_invoker) = await quest_mail_action.sendQuestMail(id); if (result.isFail()) { Log.getLogger().error(result.toBasicString()); continue; } sended_quest_ids.Add(id); quest_mails.Add(quest_mail); log_invokers.Add(quest_mail_log_invoker); } var next_refresh_time = getNextRefreshTime(periodRangeType, now); periodSendedQuest.m_sended_quest.AddOrUpdate(id, next_refresh_time, (key, value) => next_refresh_time); need_update = true; } } if (false == need_update) return result; period_repeat_quest_attibute.modifiedEntityAttribute(true); var batch = new QueryBatchEx(player, LogActionType.DailyQuestCheck, server_logic.getDynamoDbClient()); { batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } foreach (var assigned_quest in assigned_quests) { batch.appendBusinessLog(new QuestAcceptBusinessLog(assigned_quest.Key, 0, EQuestType.NORMAL)); } batch.appendBusinessLog(new QueatDailyCheckBusinessLog( period_repeat_quest_attibute.m_period_repeat_quests)); batch.appendBusinessLogs(log_invokers); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { Log.getLogger().error($"DailyQuestCheck - QueryHelper.sendQueryAndBusinessLog Fail : {result.toBasicString()}"); return result; } foreach (var assigned_quest in assigned_quests) { quest_action.addNewQuest(assigned_quest.Key, assigned_quest.Value); await QuestNotifyHelper.makeQuestUpdateNotify(player, assigned_quest.Key, assigned_quest.Value, true); } await quest_mail_action.addNewQuestMails(quest_mails); await quest_mail_action.send_NTF_RECEIVED_QUEST_MAIL(player, sended_quest_ids); await QuestCheck(player, new QuestMail(EQuestEventTargetType.MAIL, EQuestEventNameType.RECEIVED, "")); return result; }; result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "DailyQuestCheckRefresh", fn_transaction_runner); if (result.isFail()) { var err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); return result; } return result; } private void makeDefaultPeriodRepeatQuests(QuestPeriodRepeatCheckAttribute attribute) { attribute.m_period_repeat_quests.TryAdd(OncePeriodRangeType.Daily, new QuestPeriodRepeatInfo()); attribute.m_period_repeat_quests.TryAdd(OncePeriodRangeType.Weekly, new QuestPeriodRepeatInfo()); attribute.m_period_repeat_quests.TryAdd(OncePeriodRangeType.Monthly, new QuestPeriodRepeatInfo()); attribute.modifiedEntityAttribute(true); } private List getSendableMailIds(OncePeriodRangeType type) { switch (type) { case OncePeriodRangeType.Daily: return _DailyQuestQuestGroup.ToList(); case OncePeriodRangeType.Weekly: return _WeeklyQuestQuestGroup.ToList(); case OncePeriodRangeType.Monthly: return _MonthlyQuestQuestGroup.ToList(); default: return new(); } } private DateTime getNextRefreshTime(OncePeriodRangeType type, DateTime now) { var currentDate = now.Date; switch (type) { case OncePeriodRangeType.Daily: return currentDate.AddDays(1); case OncePeriodRangeType.Weekly: int daysUntilNextMonday = ((int)DayOfWeek.Monday - (int)currentDate.DayOfWeek + 7) % 7; daysUntilNextMonday = daysUntilNextMonday == 0 ? 7 : daysUntilNextMonday; // 오늘이 월요일이면 다음 주 월요일로 설정 return currentDate.AddDays(daysUntilNextMonday); case OncePeriodRangeType.Monthly: // 다음 달 1일 0시로 설정 var currentMonth = DateTimeHelper.Current; var nextMonth = new DateTime(currentMonth.Year, currentMonth.Month, 1).AddMonths(1); return nextMonth; default: return currentDate; } } public async Task newAssignableQuestCheck(Player player) { var result = new Result(); var user_create_or_load_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_create_or_load_action, () => $"user_create_or_load_action is null !!! - {player.toBasicString()}"); if (false == user_create_or_load_action.isCompletedLoadUser()) { return; } var quest_ids = _DailyQuestQuestGroup.ToList(); var end_quest_action = player.getEntityAction(); var end_quests = end_quest_action.getEndQuests(); var quest_action = player.getEntityAction(); var quest_mail_action = player.getEntityAction(); List need_send_quest_mail_ids = new(); foreach (var end_quest in end_quests) { if (end_quest.Key.Item2 > 0) continue; //ugq 제외 var end_quest_id = end_quest.Key.Item1; if (false == _QuestReqValueGroup.TryGetValue(end_quest_id, out var next_quests)) continue; foreach (var next_quest_id in next_quests) { //이미 깬 퀘스트면 continue; if(end_quests.ContainsKey((next_quest_id, 0)) ) continue; //진행 중 퀘스트면 continue; (result, var quest_meta_base_info) = await QuestMetaManager.It.getQuestMeta(next_quest_id); if (result.isFail()) continue; NullReferenceCheckHelper.throwIfNull(quest_meta_base_info.m_quest_base_info, () => $"quest_meta_base_info.m_quest_base_info is null !!!"); if (quest_meta_base_info.m_quest_base_info.QuestType.Equals(EQuestType.UGQ.ToString())) continue; if (quest_meta_base_info.m_quest_base_info.QuestType.Equals(EQuestType.NORMAL.ToString())) continue; result = quest_action.getQuest(quest_meta_base_info.m_quest_base_info.QuestType, next_quest_id, out _); if (result.isSuccess()) continue; var quest_type = EnumHelper.convertEnumTypeAndValueStringToEnum(quest_meta_base_info.m_quest_base_info.QuestType, EQuestType.NORMAL); //메일에 있으면 continue; result = quest_mail_action.hasQuestMail(quest_type, next_quest_id, false); if (result.isSuccess()) continue; need_send_quest_mail_ids.Add(next_quest_id); } } if (need_send_quest_mail_ids.Count > 0) { await quest_mail_action.sendQuestMailsWithTransaction(need_send_quest_mail_ids); } } }