using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using MetaAssets; using META_ID = System.UInt32; namespace GameServer; public class PackageAction : EntityActionBase { private List m_repeat_packages = new (); public PackageAction(Player owner) : base(owner) { } public override void onClear() { m_repeat_packages.Clear(); return; } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } public void addPackage(PackageRepeat packageRepeat) { m_repeat_packages.Add(packageRepeat); } public async Task InsertPackageLastOrderRecodeDoc() { var result = new Result(); var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); var package_last_order_recode_attribute = player.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(package_last_order_recode_attribute, () => $"package_last_order_recode_attribute is null !!! - {player.toBasicString()}"); var package_last_order_recode_doc = package_last_order_recode_attribute.getOriginDocBase(); if (package_last_order_recode_doc == null) { var server_logic = GameServerApp.getServerLogic(); var dynamo_db_client = server_logic.getDynamoDbClient(); ArgumentNullException.ThrowIfNull(dynamo_db_client); result = await dynamo_db_client.simpleInsertDocumentWithDocType(new PackageLastOrderRecodeDoc(player.getUserGuid())); if (result.isFail()) { var err_msg = $"Failed to simpleUpsertDocumentWithDocType !!! - {player.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } return result; } public async Task AddPackageFromDocs(List packageDocs) { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); foreach (var package_repeat_doc in packageDocs) { (result, var package_repeat) = await PackageRepeat.createPackageRepeatFromDoc(player, package_repeat_doc); if (result.isFail()) return result; NullReferenceCheckHelper.throwIfNull(package_repeat, () => $"package_repeat is null !!! - {player.toBasicString()}"); m_repeat_packages.Add(package_repeat); } return result; } public async Task<(Result, PackageRepeat?, List?)> tryOrderNewProduct(string order_guid, META_ID product_meta_id, DateTime buyDateTime, List invokers) { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); if (MetaData.Instance._ProductMetaDataById.TryGetValue((int)product_meta_id, out var productMetaData) == false) { err_msg = $"Failed to find product meta Id. meta_id : {product_meta_id}"; result.setFail(ServerErrorCode.NotFoundItemTableId, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null, null); } (result, var new_package_repeat, var receivedMailDocs) = await newGiveProduct(productMetaData, order_guid, buyDateTime); if (result.isFail()) return (result, null, null); foreach (var receivedMailDoc in receivedMailDocs) { var mail_doc = receivedMailDoc as MailDoc; ArgumentNullException.ThrowIfNull(mail_doc); var task_mail_log_data = MailBusinessLogHelper.toMailLogInfo(mail_doc); ArgumentNullException.ThrowIfNull(task_mail_log_data); invokers.Add(new MailBusinessLog(task_mail_log_data)); } var task_state_log_data = PackageBusinessLogHelper.toStateLogInfo(order_guid, BillingStateType.received, string.Empty); ArgumentNullException.ThrowIfNull(task_state_log_data); invokers.Add(new PackageStateBusinessLog(task_state_log_data)); if (new_package_repeat != null) { var package_repeat_attribute = new_package_repeat.getEntityAttribute(); ArgumentNullException.ThrowIfNull(package_repeat_attribute); var task_repeat_log_data = PackageBusinessLogHelper.toRepeatLogInfo(package_repeat_attribute); invokers.Add(new PackageRepeatBusinessLog(task_repeat_log_data)); } var package_lastorder_recode_attribute = player.getEntityAttribute(); ArgumentNullException.ThrowIfNull(package_lastorder_recode_attribute); package_lastorder_recode_attribute.LastOrderGuid = order_guid; package_lastorder_recode_attribute.LastBuyTime = buyDateTime; package_lastorder_recode_attribute.modifiedEntityAttribute(); return (result, new_package_repeat, receivedMailDocs); } public async Task tryOrderNewProductList() { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); var server_logic = GameServerApp.getServerLogic(); ArgumentNullException.ThrowIfNull(server_logic); var mail_action = player.getEntityAction(); ArgumentNullException.ThrowIfNull(mail_action); var account_attribute = player.getEntityAttribute(); ArgumentNullException.ThrowIfNull(account_attribute); var package_lastorder_recode_attribute = player.getEntityAttribute(); ArgumentNullException.ThrowIfNull(package_lastorder_recode_attribute); var jwt = account_attribute.SsoAccountAuthJWT.ToString(); var account_id = account_attribute.AccountId; if(jwt == string.Empty) return result; //BillingServer에 구매내역 조회 var billingPurchaseRequest = new BillingPurchaseRequest() { account_id = account_id.ToString(), order_id = package_lastorder_recode_attribute.LastOrderGuid }; (result, var billingPurchaseInfo) = await BillingServerConnector.GetBillingPurchaseInfos(billingPurchaseRequest, jwt); if (result.isFail()) return result; NullReferenceCheckHelper.throwIfNull(billingPurchaseInfo, () => $"billingPurchaseInfo is null !!! - {player.toBasicString()}"); if (billingPurchaseInfo.data.Count == 0) return result; (result, var order_id_list) = await mail_action.getReceivedProductMailOrderIds(); if (result.isFail()) return result; var fn_give_product = async delegate () { var result = new Result(); var invokers = new List(); var new_package_repeats = new List(); var received_mail_docs = new List(); var last_order_product_meta_id = 0; var last_order_id = ""; var last_order_buy_time = DateTime.UtcNow; var now = DateTime.UtcNow; foreach (var order_info in billingPurchaseInfo.data) { if (EnumHelper.tryParse(order_info.state, out var state) == false) { result.setFail(ServerErrorCode.BillingInvalidStateType, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (state != BillingStateType.paid) continue; last_order_buy_time = DateTimeOffset.FromUnixTimeSeconds(order_info.buyDateTime).UtcDateTime; if(int.TryParse(order_info.product_meta_id, out last_order_product_meta_id) == false) { result.setFail(ServerErrorCode.BillingFailedParseProductMetaIdType, err_msg); Log.getLogger().error(result.toBasicString()); return result; } bool isAlreadyReceivedMail = false; foreach (var order_id in order_id_list) { if(order_info.order_id == order_id) { isAlreadyReceivedMail = true; var stateList = new List() { new BillingStateList() { order_id = order_info.order_id, state = BillingStateType.received.ToString() } }; var billingChangeState = new BillingChangeState() { account_id = account_id.ToString(), state_list = stateList }; (result, var status_message) = await BillingServerConnector.UpdateBillingState(billingChangeState, jwt); if (result.isFail()) break; break; } } if(isAlreadyReceivedMail == true) { continue; } (result, var new_package_repeat, var receivedMailDocs) = await tryOrderNewProduct(order_info.order_id, (META_ID)last_order_product_meta_id, now, invokers); if (result.isFail()) { return result; } NullReferenceCheckHelper.throwIfNull(receivedMailDocs, () => $"receivedMailDocs is null !!!"); if (new_package_repeat != null) { new_package_repeats.Add(new_package_repeat); } received_mail_docs.AddRange(receivedMailDocs); last_order_id = order_info.order_id; } if(received_mail_docs.Count == 0) { return result; } var task_log_data = PackageBusinessLogHelper.toLastOrderRecodeLogInfo(last_order_id, (META_ID)last_order_product_meta_id, last_order_buy_time, now); ArgumentNullException.ThrowIfNull(task_log_data, $"task_log_data is null !!!"); invokers.Add(new PackageLastOrderRecordBusinessLog(task_log_data)); var batch = new QueryBatchEx(player, LogActionType.ProductGive , server_logic.getDynamoDbClient(), true); { batch.addQuery(new DBQEntityWrite(received_mail_docs)); batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } batch.appendBusinessLogs(invokers); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) { return result; } //BillingServer에 상태값 갱신 foreach (var order_info in billingPurchaseInfo.data) { (result, var isUpdated) = await BillingServerConnector.AbleToChangeBillingState(jwt, account_id.ToString(), order_info.order_id, BillingStateType.received); if (result.isFail() || isUpdated == true) continue; var stateList = new List() { new BillingStateList() { order_id = order_info.order_id, state = BillingStateType.received.ToString() } }; var billingChangeState = new BillingChangeState() { account_id = account_id.ToString(), state_list = stateList }; (result, var status_message) = await BillingServerConnector.UpdateBillingState(billingChangeState, jwt); if (result.isFail()) continue; } foreach (var new_package_repeat in new_package_repeats) { m_repeat_packages.Add(new_package_repeat); } mail_action.NewReceivedMail(); return result; }; result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "give_product", fn_give_product); if (result.isFail()) { err_msg = $"Failed to runTransactionRunnerSafely()!!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); } return result; } public async Task UpdateTick() { return await UpdateRepeatProduct(); } public async Task UpdateRepeatProduct() { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); var server_logic = GameServerApp.getServerLogic(); if (m_repeat_packages.Count == 0) return result; ArgumentNullException.ThrowIfNull(server_logic, $"server_logic is null !!!"); if (isUpdateRepeatProduct() == false) return result; var fn_update_give_product = async delegate () { var result = new Result(); var err_msg = string.Empty; var invokers = new List(); var mail_docs = new List(); var remove_packages = new List(); var mail_action = player.getEntityAction(); ArgumentNullException.ThrowIfNull(mail_action); var copy_packages = m_repeat_packages.ToList(); foreach (var package in copy_packages) { var package_attribute = package.getEntityAttribute(); ArgumentNullException.ThrowIfNull(package_attribute); if (MetaData.Instance._ProductMetaDataById.TryGetValue((int)package_attribute.ProductMetaId, out var productMetaData) == false) { err_msg = $"Failed to find product meta Id. meta_id : {package_attribute.ProductMetaId}"; result.setFail(ServerErrorCode.NotFoundItemTableId, err_msg); Log.getLogger().error(result.toBasicString()); return result; } (result, var receivedMailDocs, bool isDeleteRepeat) = await updateGiveProduct(productMetaData, package_attribute, package_attribute.OrderGuid, invokers); if (result.isFail()) return result; mail_docs.AddRange(receivedMailDocs); if (isDeleteRepeat == true) { remove_packages.Add(package); } var task_log_data = PackageBusinessLogHelper.toRepeatLogInfo(package_attribute); invokers.Add(new PackageRepeatBusinessLog(task_log_data)); } if (mail_docs.Count == 0) { return result; } var batch = new QueryBatchEx(player, LogActionType.ProductGive , server_logic.getDynamoDbClient()); { batch.addQuery(new DBQEntityWrite(mail_docs)); batch.addQuery(new DBQWriteToAttributeAllWithTransactionRunner()); } batch.appendBusinessLogs(invokers); result = await QueryHelper.sendQueryAndBusinessLog(batch); if (result.isFail()) return result; foreach (var remove_package in remove_packages) { m_repeat_packages.Remove(remove_package); } mail_action.NewReceivedMail(); return result; }; result = await player.runTransactionRunnerSafely(TransactionIdType.PrivateContents, "update_give_product", fn_update_give_product); if (result.isFail()) { err_msg = $"Failed to runTransactionRunnerSafely() !!! : {result.toBasicString()} - {player.toBasicString()}"; Log.getLogger().error(err_msg); } return result; } private async Task<(Result, PackageRepeat?, List)> newGiveProduct(ProductMetaData productMetaData, string order_guid, DateTime buyDateTime) { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); var mail_action = player.getEntityAction(); ArgumentNullException.ThrowIfNull(mail_action); var receivedMailDocs = new List(); if(productMetaData.ItemID_First != 0) { var expireDate = DateTimeHelper.MaxTime; if (productMetaData.Storage_Period_First != 0) expireDate = buyDateTime.AddMinutes(productMetaData.Storage_Period_First); var mail_Items = new List() { new ServerCommon.MailItem() { ItemId = (META_ID)productMetaData.ItemID_First, Count = 1, ProductId = (META_ID)productMetaData.Id, isRepeatProduct = false } }; (result, var firstReceivedMailDoc) = await mail_action.tryMakeNewSystemMail(mail_Items, productMetaData.SystemMail_First, expireDate, order_guid); if (result.isFail()) return (result, null, receivedMailDocs); NullReferenceCheckHelper.throwIfNull(firstReceivedMailDoc, () => $"firstReceivedMailDoc is null !!!"); receivedMailDocs.Add(firstReceivedMailDoc); } var nowTime = DateTime.UtcNow; var next_give_time = buyDateTime; var repeat_count = productMetaData.Mail_Repeat_Count; var left_count = productMetaData.Mail_Repeat_Count; for (int i = 0; i < repeat_count; ++i) { if (nowTime < next_give_time) { break; } var expireDate = DateTimeHelper.MaxTime; if (productMetaData.Storage_Period_Repeat != 0) expireDate = next_give_time.AddMinutes(productMetaData.Storage_Period_Repeat); var mail_Items = new List() { new ServerCommon.MailItem() { ItemId = (META_ID)productMetaData.ItemID_Repeat, Count = 1, ProductId = (META_ID)productMetaData.Id, isRepeatProduct = true } }; (result, var receivedMailDoc) = await mail_action.tryMakeNewSystemMail(mail_Items,productMetaData.SystemMail_Repeat, expireDate, order_guid); if (result.isFail()) return (result, null, receivedMailDocs); NullReferenceCheckHelper.throwIfNull(receivedMailDoc, () => $"receivedMailDoc is null !!!"); receivedMailDocs.Add(receivedMailDoc); --left_count; next_give_time = next_give_time.AddMinutes(productMetaData.Mail_Repeat_Interval); } if (left_count <= 0) { return (result, null, receivedMailDocs); } (result, var package) = await PackageRepeat.createPackageRepeat(player, order_guid, (META_ID)productMetaData.Id, left_count, next_give_time); if (result.isFail()) { return (result, null, receivedMailDocs); } return (result, package, receivedMailDocs); } public bool isUpdateRepeatProduct() { var copy_packages = m_repeat_packages.ToList(); var nowTime = DateTime.UtcNow; foreach (var package in copy_packages) { var package_attribute = package.getEntityAttribute(); ArgumentNullException.ThrowIfNull(package_attribute); if (nowTime < package_attribute.NextGiveTime) continue; return true; } return false; } private async Task<(Result, List, bool isDeleteRepeat)> updateGiveProduct(ProductMetaData productMetaData, PackageRepeatAttribute package_attribute, string order_guid, List invokers) { var result = new Result(); var err_msg = string.Empty; var player = getOwner() as Player; ArgumentNullException.ThrowIfNull(player); var receivedMailDocs = new List(); var nowTime = DateTime.UtcNow; var next_give_time = package_attribute.NextGiveTime; var repeat_count = package_attribute.LeftCount; var left_count = package_attribute.LeftCount; for (int i = 0; i < repeat_count; ++i) { if (nowTime < next_give_time) break; --left_count; var expireDate = DateTimeHelper.MaxTime; if (productMetaData.Storage_Period_Repeat != 0) expireDate = next_give_time.AddMinutes(productMetaData.Storage_Period_Repeat); next_give_time = next_give_time.AddMinutes(productMetaData.Mail_Repeat_Interval); if (expireDate < nowTime) { var task_mail_expired_log_data = MailBusinessLogHelper.toMailExpiredLogInfo(order_guid, productMetaData.Id, next_give_time, expireDate, nowTime, left_count); invokers.Add(new MailExpiredBusinessLog(task_mail_expired_log_data)); continue; } var mail_action = player.getEntityAction(); var mail_Items = new List() { new ServerCommon.MailItem() { ItemId = (META_ID)productMetaData.ItemID_Repeat, Count = 1, ProductId = (META_ID)productMetaData.Id, isRepeatProduct = true } }; (result, var receivedMailDoc) = await mail_action.tryMakeNewSystemMail(mail_Items, productMetaData.SystemMail_Repeat, expireDate, order_guid); NullReferenceCheckHelper.throwIfNull(receivedMailDoc, () => $"receivedMailDoc is null !!! - {player.toBasicString()}"); receivedMailDocs.Add(receivedMailDoc); } foreach (var receivedMailDoc in receivedMailDocs) { var mail_doc = receivedMailDoc as MailDoc; ArgumentNullException.ThrowIfNull(mail_doc); var task_mail_log_data = MailBusinessLogHelper.toMailLogInfo(mail_doc); invokers.Add(new MailBusinessLog(task_mail_log_data)); } if (left_count <= 0) { package_attribute.deleteEntityAttribute(); return (result, receivedMailDocs, true); } else { package_attribute.NextGiveTime = next_give_time; package_attribute.LeftCount = left_count; package_attribute.modifiedEntityAttribute(); return (result, receivedMailDocs, false); } } }