Files
2025-05-01 07:20:41 +09:00

1357 lines
57 KiB
C#

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<Item> m_rewardable_items { get; set; } = new();
public List<Item> m_deletable_items { get; set; } = new();
public List<MetaAssets.Reward> m_rewared_money { get; set; } = new();
public List<Item> m_rewarded_items { get; set; } = new();
}
public class QuestManager : Singleton<QuestManager>
{
public QuestManager()
{
Load();
}
/// <summary>
/// 퀘스트 활성화가 되기 위한 타입별로 저장
/// 캐릭터 생성시, 특정퀘스트 완료시
/// </summary>
public Dictionary<string, HashSet<UInt32>> _QuestReqTypeGroup = new();
/// <summary>
/// 퀘스트 활성화가 되기 위한 값별로 정보저장
/// 특정퀘스트 완료시에만 의미가 있으며 QuesId 기준으로 정리해서 저장
/// NORAM 은 반복수 행 처리를 위해 해당 리스트에서 제외
/// </summary>
public Dictionary<UInt32, HashSet<UInt32>> _QuestReqValueGroup = new();
/// <summary>
/// Normal 퀘스트만 저장. 반복 수행을 위함
/// </summary>
public Dictionary<UInt32, HashSet<UInt32>> _QuestNormalGroup = new();
/// <summary>
/// requirementType 이 DailyUniqueLogin 인것과 OncePeriodRange가 Daily 인것만 저장
/// </summary>
public HashSet<UInt32> _DailyQuestQuestGroup = new();
/// <summary>
/// OncePeriodRange가 Weekly 인것만 저장
/// </summary>
public HashSet<UInt32> _WeeklyQuestQuestGroup = new();
/// <summary>
/// OncePeriodRange가 Monthly 인것만 저장
/// </summary>
public HashSet<UInt32> _MonthlyQuestQuestGroup = new();
/// <summary>
/// Dialoque 에 따른 퀘스트 정보
/// </summary>
public Dictionary<(string, string), HashSet<UInt32>> _QuestDialogueGroup = new();
/// <summary>
/// 타이머 체크 필요한 유저
/// </summary>
public ConcurrentDictionary<string, string> m_timer_check_users { get; set; } = new();
public ConcurrentDictionary<string, int> 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<UInt32>();
}
outTypeList.Add(questData.QuestId);
_QuestReqTypeGroup.TryAdd(questData.RequirementType, outTypeList);
if (_QuestReqValueGroup.TryGetValue(questData.RequirementValue, out var outValueList) == false)
{
outValueList = new HashSet<UInt32>();
}
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<UInt32>();
}
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<Result> QuestCheckOneByOne(Player player, IQuest quest_base, ServerCommon.Quest quest,
bool hasTransaction)
{
//todo : 성눙 안좋다. 바꾸는게 좋다...
var quest_attribute = quest.getEntityAttribute<QuestAttribute>();
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<Result> QuestSequentialCheck(Player player, IQuest quest_base, bool hasTransaction,
QuestTaskUpdateHandler questUpdateHandler)
{
var result = await QuestCheckOne(player, quest_base, hasTransaction, questUpdateHandler);
return result;
}
public async Task<Result> 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<QuestAttribute>();
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<string> 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<Result> 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<UgqInfoAction>();
(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<UgcNpcRankEntity>();
NullReferenceCheckHelper.throwIfNull(rank_entity, () => $"rank_entity is null !!!");
var quest_rank_action = rank_entity.getEntityAction<UgcNpcQuestRankAction>();
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<Result> 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<QuestAttribute>();
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<InventoryActionBase>();
//삭제할게 있으면 삭체 처리
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<QueryRunnerWithDocument>(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<Result> checkLastFunction(Player player, QuestTaskUpdateHandler questTaskUpdateHandler)
{
var result = new Result();
var quest_accep_action = player.getEntityAction<QuestAcceptAction>();
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<SwitchingPropAction>();
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<Result> QuestCheck(Player player, IQuest quest)
{
var quest_action = player.getEntityAction<QuestAction>();
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<Result> QuestCheckWithoutTransaction(Player player, IQuest quest)
{
var quest_action = player.getEntityAction<QuestAction>();
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<RewardMetaData> rewardDatas, Dictionary<string, string> 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<RewardMetaData> rewardData, Dictionary<string, string> deleteItem)
{
// List<ILogInvoker> logInvokers = new List<ILogInvoker>();
//
// 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<ERewardType, Document> rewardDocument,
// ref List<ItemEntity> itemEntityList,
// ref List<RewardData> 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<int, Dictionary<string, string>> delete_items= new();
// // delete_items.TryAdd(quest_id, deletedItems);
// //
// // Dictionary<int, List<RewardData>> reward_dictionary = new();
// // reward_dictionary.TryAdd(quest_id, rewards);
// //
// // List<int> 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<ERewardType, Document> rewardDocument,
// ref List<ItemEntity> itemEntityList,
// ref List<RewardData> 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<int, Dictionary<string, string>> delete_items = new();
// delete_items.TryAdd(quest_id, deletedItems);
//
// Dictionary<int, List<RewardData>> reward_dictionary = new();
// reward_dictionary.TryAdd(quest_id, rewards);
//
// List<int> 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<RewardData> rewards = new List<RewardData>();
// 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<int, Dictionary<string, string>> delete_items = new();
// delete_items.TryAdd(questId, deletedItems);
// Dictionary<int, List<RewardData>> reward_dictionary = new();
// reward_dictionary.TryAdd(questId, rewards);
//
// List<int> 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<int> updatedQuests, QuestListEntity questListEntity, Dictionary<int, List<RewardData>> rewards, Dictionary<int, Dictionary<string, string>> deleteItem)
// {
// ConcurrentDictionary<ERewardType, Document> 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<string> getActiveEventStrings(QuestAttribute questAttribute, QuestTaskGroup questTaskInfo)
{
List<string> activeEventStrings = new List<string>();
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<string> getTimerCheckUser()
{
return m_timer_check_users.Keys.ToList();
}
public void deleteTimerCheckUser(List<string> deleteChecker)
{
foreach (string deleteUser in deleteChecker)
{
m_timer_check_users.TryRemove(deleteUser, out _);
}
}
public async Task questTimerEventCheck()
{
List<string> timer_check_user = m_timer_check_users.Keys.ToList();
List<string> 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<UserCreateOrLoadAction>();
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<string> 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<UserCreateOrLoadAction>();
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<RepeatQuestAttribute>();
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<QuestMailAction>();
if (false == quest_mail_action.isMailLoadedAll()) return;
HashSet<UInt32> my_current_normal_mail_set = await quest_mail_action.getNormalMailSet();
int normal_mail_count = my_current_normal_mail_set.Count;
HashSet<UInt32> 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<Result> delegateSendQuestMailAndUpdateRepeatQuestInfo() =>
await sendQuestMailAndUpdateRepeatQuestInfo(player, assignable_quest_id, check_value);
}
public UInt32 getAssignableQuestMailId(HashSet<UInt32> assignableMailSet)
{
//여기까지 왔으면 메일 보낼게 있다는 얘기이므로 전송한다.
Random random = new Random();
List<UInt32> 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<UInt32> 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<Result> sendQuestMailAndUpdateRepeatQuestInfo(Player player, UInt32 questId, bool checkVlaue)
{
var quest_mail_action = player.getEntityAction<QuestMailAction>();
(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<RepeatQuestAttribute>();
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<QueryRunnerWithDocument>(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<ServerCommon.QuestMail>() { quest_mail });
await quest_mail_action.send_NTF_RECEIVED_QUEST_MAIL(player, new List<UInt32>() { questId });
await QuestCheck(player, new QuestMail(EQuestEventTargetType.MAIL, EQuestEventNameType.RECEIVED, ""));
return result;
}
public async Task<HashSet<UInt32>> getAssignableMailSet(Player player, HashSet<UInt32> normalMailSet)
{
var quest_action = player.getEntityAction<QuestAction>();
var end_quest_action = player.getEntityAction<EndQuestAction>();
var end_quests = end_quest_action.getEndQuests();
var quest_ids = quest_action.getSystemQuestIds();
HashSet<UInt32> 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<LocationAttribute>();
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<Result> PeriodRepeatQuestCheck(Player player)
{
await Task.CompletedTask;
var result = new Result();
var user_create_or_load_action = player.getEntityAction<UserCreateOrLoadAction>();
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<ServerCommon.QuestMail> quest_mails = new();
List<ILogInvoker> log_invokers = new();
List<UInt32> sended_quest_ids = new();
Dictionary<UInt32, ServerCommon.Quest> assigned_quests = new();
var quest_mail_action = player.getEntityAction<QuestMailAction>();
var end_quest_action = player.getEntityAction<EndQuestAction>();
var quest_action = player.getEntityAction<QuestAction>();
var end_quests = end_quest_action.getEndQuests();
var now = DateTimeHelper.Current;
var fn_transaction_runner = async delegate()
{
//리프레시 하면서 바로 퀘스트 메일도 지급
var period_repeat_quest_attibute = player.getEntityAttribute<QuestPeriodRepeatCheckAttribute>();
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<QuestAcceptAction>();
(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<QuestAttribute>();
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<QueryRunnerWithDocument>(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<UInt32> 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<UserCreateOrLoadAction>();
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<EndQuestAction>();
var end_quests = end_quest_action.getEndQuests();
var quest_action = player.getEntityAction<QuestAction>();
var quest_mail_action = player.getEntityAction<QuestMailAction>();
List<UInt32> 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);
}
}
}