using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using ANCHOR_META_GUID = System.String; using OWNER_GUID = System.String; using META_ID = System.UInt32; using GUARD_KEY = System.String; namespace GameServer; public class FarmingEffectAction : EntityActionBase { private AnchorInfo? m_anchor_info_nullable = null; private ServerCommon.BusinessLogDomain.FarmingLogInfo m_farming_log_info = new(); private ServerCommon.BusinessLogDomain.FarmingRewardLogInfo m_farming_reward_log_info = new(); public FarmingEffectAction(FarmingEffect farmingEffect) : base(farmingEffect) { } public override async Task onInit() { await Task.CompletedTask; var result = new Result(); return result; } public override void onClear() { return; } public async Task tryStartEffect( ANCHOR_META_GUID anchorMetaGuid, Int16 farmingActionTryCount) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var result = new Result(); var err_msg = string.Empty; var found_anchor_info = curr_map.findAnchorInfo(anchorMetaGuid); if(null == found_anchor_info) { err_msg = $"Not found Anchor for Farming !!! : toFindAnchorMetaGuid:{anchorMetaGuid} - {curr_map.toBasicString()}, {root_parent.toBasicString()}"; result.setFail(ServerErrorCode.FarmingAnchorNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } result = await isAbleToFarming(anchorMetaGuid, farmingActionTryCount); if(result.isFail()) { return result; } Player? master = null; if(root_parent is Player player) { master = player; } else if(root_parent is UgcNpc ugc_npc) { master = ugc_npc.onGetMasterEntity() as Player; } if (null == master) { err_msg = $"Not found Master !!! : masterGuid:{root_parent.getMasterGuid()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } // FarmingStateType.StandBy 상태 체크 한다. var farming_effect_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {owner.toBasicString()}"); if (FarmingStateType.StandBy != farming_effect_attribute.FarmingState) { err_msg = $"Not state StandBy Farming !!! : masterGuid:{root_parent.getMasterGuid()} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingStandByNotState, err_msg); Log.getLogger().error(result.toBasicString()); return result; } // 결제할 Owner를 설정하고, 요구 수수료를 차감 한다. (result, var payer, var req_currency_type, var req_use_fee_price) = await calculateReqUseFeePrice(anchorMetaGuid, farmingActionTryCount); if (result.isFail()) { return result; } NullReferenceCheckHelper.throwIfNull(payer, () => $"payer is null !!! - {owner.toBasicString()}"); var money_action = payer.getEntityAction(); NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!! - {payer.toBasicString()}"); result = await money_action.spendMoney(req_currency_type, req_use_fee_price); if (result.isFail()) { return result; } // FarmingEffectAttribute 정보를 설정 한다. farming_effect_attribute.AnchorMetaGuid = anchorMetaGuid; farming_effect_attribute.LocationUniqueId = curr_map.getLocationUniqueId(); farming_effect_attribute.FarmingPropMetaId = (META_ID)found_anchor_info.AnchorProp.TableId; farming_effect_attribute.OwnerEntityType = master.onGetOwnerEntityType(); farming_effect_attribute.OwnerGuid = master.onGetGuidOfOwnerEntityType(); farming_effect_attribute.FarmingSummonedEntityType = toFarmingSummonedEntityType(); farming_effect_attribute.FarmingEntityGuid = toFarmingGuid(); farming_effect_attribute.FarmingState = FarmingStateType.Progress; farming_effect_attribute.FarmingActionReqTryCount = farmingActionTryCount; var start_time = DateTimeHelper.Current; farming_effect_attribute.FarmingStartTime = start_time; farming_effect_attribute.FarmingEndTime = toFarmingEndTime(start_time, anchorMetaGuid, farmingActionTryCount); farming_effect_attribute.newEntityAttribute(); var farming_effect_link_pksk = FarmingEffectDoc.makeLINK_PKSK(anchorMetaGuid, farming_effect_attribute.LocationUniqueId); // FarmingEffectLocationInTargetAttribute 정보를 설정 한다. var farming_effect_location_in_target_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_location_in_target_attribute, () => $"farming_effect_location_in_target_attribute is null !!! - {owner.toBasicString()}"); var server_logic = GameServerApp.getServerLogic(); var location_target_type = server_logic.getServerType().toLocationTargetType(); farming_effect_location_in_target_attribute.LocationUniqueId = curr_map.getLocationUniqueId(); farming_effect_location_in_target_attribute.AnchorMetaGuid = anchorMetaGuid; farming_effect_location_in_target_attribute.UserGuid = master.getUserGuid(); farming_effect_location_in_target_attribute.UserNickname = master.getUserNickname(); farming_effect_location_in_target_attribute.FarmingEffectDocLinkPKSK = farming_effect_link_pksk; farming_effect_location_in_target_attribute.newEntityAttribute(); // FarmingEffectOwnerAttribute 정보를 설정 한다. var farming_effect_owner_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_owner_attribute, () => $"farming_effect_owner_attribute is null !!! - {owner.toBasicString()}"); farming_effect_owner_attribute.OwnerEntityType = owner.onGetOwnerEntityType(); farming_effect_owner_attribute.OwnerGuid = owner.onGetGuidOfOwnerEntityType(); farming_effect_owner_attribute.FarmingEffectDocLinkPKSK = farming_effect_link_pksk; farming_effect_owner_attribute.newEntityAttribute(); // FarmingEffectAction에 AnchorInfo를 설정 한다. attachAnchorInfo(found_anchor_info); (var farming_doc_result, var doc_base) = await farming_effect_attribute.toDocBase(false); if(farming_doc_result.isFail()) { err_msg = $"Failed to toDocBase() !!!, in tryStartEffect() : {farming_doc_result.toBasicString()} - {owner.toBasicString()}, masterGuid:{root_parent.getMasterGuid()}"; Log.getLogger().error(err_msg); } else { var farming_doc = doc_base as FarmingEffectDoc; NullReferenceCheckHelper.throwIfNull(farming_doc, () => $"farming_doc is null !!! - {owner.toBasicString()}"); m_farming_log_info.setLogInfo(farming_doc, master.onGetGuidOfOwnerEntityType(), (UInt32)req_use_fee_price); } return result; } public async Task isAbleToFarming(ANCHOR_META_GUID anchorMetaGuid, Int32 farmingActionTryCount) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var result = new Result(); var err_msg = string.Empty; var farming_action = root_parent.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_action, () => $"farming_action is null !!! - {owner.toBasicString()}"); // 1. 중복 파밍 예외를 체크 한다. if(true == farming_action.isFarmingHere()) { err_msg = $"Farming by me !!! : AnchorMetaGuid:{anchorMetaGuid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingByMe, err_msg); return result; } // 2. 메타 데이터 기준 예외를 체크 한다. var curr_map = owner.getCurrMap(); var found_anchor_info = curr_map.findAnchorInfo(anchorMetaGuid); NullReferenceCheckHelper.throwIfNull(found_anchor_info, () => $"found_anchor_info is null !!! - {owner.toBasicString()}"); if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue(found_anchor_info.AnchorProp.TableId, out var found_farming_prop_meta)) { err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingPropMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } // 2.1. 파밍 가능 횟수를 체크 한다. var max_count = found_farming_prop_meta.FarmingMaxCount; if ( farmingActionTryCount < 1 || found_farming_prop_meta.FarmingMaxCount < farmingActionTryCount) { err_msg = $"Invalid Farming Try Count !!! : 0 < tryCount:{farmingActionTryCount} <= maxCount:{max_count} - FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId}, {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingTryCountInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return result; } // 2.2. 요구 능력치를 체크 한다. var ability_action = root_parent.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ability_action, () => $"ability_action is null !!! - {owner.toBasicString()}"); var to_check_abilities = found_farming_prop_meta.AttributeCondition.ToDictionary(item => item.AttributeName, item => item.AttributeValue); result = ability_action.hasAbilities(to_check_abilities); if (result.isFail()) { return result; } // 2.3. 결제할 Owner를 설정하고, 요구 수수료를 계산 한다. (result, var payer, var req_currency_type, var req_use_fee_price) = await calculateReqUseFeePrice(anchorMetaGuid, farmingActionTryCount); if(result.isFail()) { return result; } NullReferenceCheckHelper.throwIfNull(payer, () => $"payer is null !!! - {owner.toBasicString()}"); // 2.4. 요구 비용을 체크 한다. var money_action = payer.getEntityAction(); NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!! - {owner.toBasicString()}"); var currency_type = found_farming_prop_meta.UsageFeeType; if(false == money_action.hasMoney(currency_type, req_use_fee_price)) { var has_money = money_action.getMoney(currency_type); err_msg = $"Not enough Money !!! : currencyType:{currency_type}, currMoney:{has_money} >= reqMoney:{has_money} - FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.MoneyNotEnough, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return await Task.FromResult(result); } public async Task<(Result, Player?, CurrencyType, double)> calculateReqUseFeePrice(ANCHOR_META_GUID anchorMetaGuid, Int32 farmingActionTryCount) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var result = new Result(); var err_msg = string.Empty; // 1. 메타 데이터 기준 예외를 체크 한다. var curr_map = owner.getCurrMap(); var found_anchor_info = curr_map.findAnchorInfo(anchorMetaGuid); NullReferenceCheckHelper.throwIfNull(found_anchor_info, () => $"found_anchor_info is null !!! - {owner.toBasicString()}"); if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue(found_anchor_info.AnchorProp.TableId, out var found_farming_prop_meta)) { err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingPropMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null, CurrencyType.None, 0); } // 2. 파밍 가능 횟수를 체크 한다. var max_count = found_farming_prop_meta.FarmingMaxCount; if ( farmingActionTryCount < 1 || found_farming_prop_meta.FarmingMaxCount < farmingActionTryCount) { err_msg = $"Invalid Farming Try Count !!! : 0 < tryCount:{farmingActionTryCount} <= maxCount:{max_count} - FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId}, {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingTryCountInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null, CurrencyType.None, 0); } // 3. 결제할 Owner를 설정하고, 요구 수수료를 계산 한다. Player? payer = null; double req_use_fee_price = 0; if (root_parent is Player player) { payer = player; // 유저 선택시 수수료를 계산 한다. req_use_fee_price = found_farming_prop_meta.UsageFeePrice * farmingActionTryCount; } else if (root_parent is UgcNpc ugc_npc) { var ugc_npc_attribute = ugc_npc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}"); var curr_state = ugc_npc_attribute.State; if (EntityStateType.None != curr_state) { err_msg = $"UgcNpc is Busy !!! : currState:{curr_state}, AnchorMetaGuid:{anchorMetaGuid} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.NpcIsBusy, err_msg); return (result, null, CurrencyType.None, 0); } payer = ugc_npc.onGetMasterEntity() as Player; NullReferenceCheckHelper.throwIfNull(payer, () => $"payer is null !!! - {owner.toBasicString()}"); // UgcNpc 선택시 수수료를 계산 한다. var use_fee_price = found_farming_prop_meta.UsageFeePrice * farmingActionTryCount; double ratio_100 = ConstValue.default_100_ratio_f.toDoubleWithEpsilon(2); double usage_fee_price_npc_sale = found_farming_prop_meta.UsageFeePriceNpcSale; req_use_fee_price = Double.Max(0, use_fee_price - use_fee_price * usage_fee_price_npc_sale * ratio_100); } else { err_msg = $"Invalid EntityType !!! : EntityType:{root_parent.getEntityType()} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.EntityTypeInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null, CurrencyType.None, 0); } req_use_fee_price = CurrencyControlHelper.roundMoneyByCurrencyType(found_farming_prop_meta.UsageFeeType, req_use_fee_price); return await Task.FromResult((result, payer, found_farming_prop_meta.UsageFeeType, req_use_fee_price)); } public async Task<(Result, FarmingEffect?, ReceivedMailDoc?)> tryCancelFarming( ANCHOR_META_GUID anchorMetaGuid ) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var result = new Result(); var err_msg = string.Empty; result = await isAbleToCancel(anchorMetaGuid); if(result.isFail()) { return (result, null, null); } (result, ReceivedMailDoc? to_receive_mail_doc_nullable) = await tryStopFarming(anchorMetaGuid); if (result.isFail()) { return (result, null, null); } return (result, owner, to_receive_mail_doc_nullable); } public async Task isAbleToCancel(ANCHOR_META_GUID anchorMetaGuid) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var curr_anchor_info = getAnchorInfo(); NullReferenceCheckHelper.throwIfNull(curr_anchor_info, () => $"curr_anchor_info is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; // FarmingEffectOwnerAttribute 정보 설정 하기 var farming_effect_owner_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_owner_attribute, () => $"farming_effect_owner_attribute is null !!! - {owner.toBasicString()}"); var owner_entity_type = farming_effect_owner_attribute.OwnerEntityType; var owner_guid = farming_effect_owner_attribute.OwnerGuid; switch (owner_entity_type) { case OwnerEntityType.User: { var player = root_parent as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {owner.toBasicString()}"); var user_guid = player.getUserGuid(); if (user_guid != owner_guid) { err_msg = $"Not match owner of Farming !!! : currOwnerGuid:{owner_guid} == reqUserGuid:{user_guid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingOwnerNotMatch, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } break; case OwnerEntityType.UgcNpc: { var ugc_npc = root_parent as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!! - {owner.toBasicString()}"); var ugc_npc_meta_guid = ugc_npc.getUgcNpcMetaGuid(); if (ugc_npc_meta_guid != owner_guid) { err_msg = $"Not match owner of Farming !!! : currOwnerGuid:{owner_guid} == reqUgcNpcMetaGuid:{ugc_npc_meta_guid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingOwnerNotMatch, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } break; } if (PropState.Progress != curr_anchor_info.PropState) { err_msg = $"Not state Farming !!! : currState:{curr_anchor_info.PropState} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingNotState, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return await Task.FromResult(result); } public async Task<(Result, ReceivedMailDoc?)> tryStopFarming(ANCHOR_META_GUID anchorMetaGuid) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var result = new Result(); var err_msg = string.Empty; var curr_anchor_info = getAnchorInfo(); var farming_prop_meta_id = curr_anchor_info.AnchorProp.TableId; if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue(farming_prop_meta_id, out var found_farming_prop_meta)) { err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{curr_anchor_info.AnchorProp.TableId} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingPropMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } if (root_parent is UgcNpc ugc_npc) { var ugc_npc_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {owner.toBasicString()}"); if (false == MapHelper.getAnchor(curr_anchor_info.AnchorGuid, out var anchor)) { err_msg = $"Not found FarmingAnchor in Map !!! : FarmingAnchorGuid:{curr_anchor_info.AnchorGuid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingAnchorNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var base_pos = new System.Numerics.Vector3(curr_anchor_info.AnchorPos.X, curr_anchor_info.AnchorPos.Y, curr_anchor_info.AnchorPos.Z); var delta_pos = new System.Numerics.Vector3(found_farming_prop_meta.FarmingEndPosX, found_farming_prop_meta.FarmingEndPosY, found_farming_prop_meta.FarmingEndPosZ); var target_position = ugc_npc_action.makeToLocatePosition( base_pos, anchor.Rotation.Pitch, anchor.Rotation.Yaw, anchor.Rotation.Roll , delta_pos, found_farming_prop_meta.FarmingEndRotate ); ugc_npc_action.modifyStateInfo( EntityStateType.None, target_position ); result = await ugc_npc_action.tryRemoveInGameZone(); if (result.isFail()) { return (result, null); } } (result, ReceivedMailDoc? to_receive_mail_doc_nullable) = await tryRewardFarming( anchorMetaGuid, DateTimeHelper.Current ); if (result.isFail()) { return (result, null); } var farming_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_attribute, () => $"farming_attribute is null !!! - {owner.toBasicString()}"); (var farming_doc_result, var doc_base) = await farming_attribute.toDocBase(false); if (farming_doc_result.isFail()) { err_msg = $"Failed to toDocBase() !!!, in tryStopFarming() : {farming_doc_result.toBasicString()} - {owner.toBasicString()}, masterGuid:{root_parent.getMasterGuid()}"; Log.getLogger().error(err_msg); } else { var farming_doc = doc_base as FarmingEffectDoc; NullReferenceCheckHelper.throwIfNull(farming_doc, () => $"farming_doc is null !!! - {owner.toBasicString()}"); var farming_location_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_location_attribute, () => $"farming_location_attribute is null !!! - {owner.toBasicString()}"); m_farming_log_info.setLogInfo(farming_doc, farming_location_attribute.UserGuid); } // FarmingEffectOwnerAttribute 정보 삭제 설정 var farming_effect_owner_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_owner_attribute, () => $"farming_effect_owner_attribute is null !!! - {owner.toBasicString()}"); farming_effect_owner_attribute.deleteEntityAttribute(); // FarmingEffectLocationInTargetAttribute 정보 삭제 설정 var farming_effect_location_in_target_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_location_in_target_attribute, () => $"farming_effect_location_in_target_attribute is null !!! - {owner.toBasicString()}"); farming_effect_location_in_target_attribute.deleteEntityAttribute(); // FarmingEffectAttribute 정보 삭제 설정 var farming_effect_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {owner.toBasicString()}"); farming_effect_attribute.deleteEntityAttribute(); return (result, to_receive_mail_doc_nullable); } public async Task<(Result, ReceivedMailDoc?)> tryRewardFarming( ANCHOR_META_GUID anchorMetaGuid , DateTime farmingEndTime) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var curr_anchor_info = getAnchorInfo(); var result = new Result(); var err_msg = string.Empty; if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue(curr_anchor_info.AnchorProp.TableId, out var found_farming_prop_meta)) { err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{curr_anchor_info.AnchorProp.TableId} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingPropMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var reward_gacha_group_id = found_farming_prop_meta.RewardGachaGroupID; if (false == MetaData.Instance._GachaMetaTable.TryGetValue(reward_gacha_group_id, out var found_gacha_meta)) { err_msg = $"Not found GachaMeta in GachaMetaTable !!! : RewardGachaGroupId:{reward_gacha_group_id} - {curr_map.toBasicString()}, {owner.toBasicString()}"; result.setFail(ServerErrorCode.RewardInfoNotExist, err_msg); return (result, null); } Player? master = null; if (root_parent is Player player) { master = player; } else if (root_parent is UgcNpc ugc_npc) { master = ugc_npc.onGetMasterEntity() as Player; } if (null == master) { err_msg = $"Not found Master !!! : masterGuid:{root_parent.getMasterGuid()} : {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var user_guid = master.getUserGuid(); var user_nickname = master.getUserNickname(); // FarmingEffectAttribute 정보 설정 하기 var farming_effect_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {owner.toBasicString()}"); ReceivedMailDoc? to_receive_mail_doc_nullable = null; (result, DynamoDbDocBase? make_doc) = await farming_effect_attribute.toDocBase(false); if (result.isFail()) { err_msg = $"Failed to makeDoc() !!! : {result.toBasicString()} - anchorMetaGuid:{anchorMetaGuid}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } var farming_effect_doc = make_doc as FarmingEffectDoc; NullReferenceCheckHelper.throwIfNull(farming_effect_doc, () => $"farming_effect_attrfarming_effect_docibute is null !!! - {owner.toBasicString()}"); (result, to_receive_mail_doc_nullable) = await FarmingHelper.tryReward( farming_effect_doc , farmingEndTime, new TimeSpan() , (UInt16)found_farming_prop_meta.InteractionCoolTime , (META_ID)reward_gacha_group_id, found_gacha_meta , user_guid, user_nickname ); if(result.isFail()) { err_msg = $"Failed to tryReward() !!! : {result.toBasicString()} - anchorMetaGuid:{anchorMetaGuid}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } if(false == farming_effect_attribute.copyEntityAttributeFromDoc(farming_effect_doc)) { err_msg = $"Failed to copyEntityAttributeFromDoc() !!!, to:{farming_effect_attribute.getTypeName()}, from:{farming_effect_doc.getTypeName()}"; result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var farming_location_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_location_attribute, () => $"farming_location_attribute is null !!! - {owner.toBasicString()}"); var reward_meta_type = (found_gacha_meta != null ? found_gacha_meta.getTypeName() : string.Empty); m_farming_reward_log_info.setLogInfo(farming_effect_doc, farming_location_attribute.UserGuid, reward_meta_type, (META_ID)reward_gacha_group_id); return await Task.FromResult((result, to_receive_mail_doc_nullable)); } public virtual async Task onUpdateFarming() { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var result = new Result(); var err_msg = string.Empty; var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var curr_anchor_info = getAnchorInfo(); // FarmingEffectAttribute 정보 설정 하기 var farming_effect_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {owner.toBasicString()}"); var current_time = DateTimeHelper.Current; // 파밍 시간 만료를 체크 한다. if ( FarmingStateType.Progress == farming_effect_attribute.FarmingState && farming_effect_attribute.FarmingEndTime <= current_time) { (result, var master_profile) = await farming_effect_attribute.getMasterProfile(); if (result.isFail()) { err_msg = $"Failed to getMasterProile() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } NullReferenceCheckHelper.throwIfNull(master_profile, () => $"master_profile is null !!! - {owner.toBasicString()}"); farming_effect_attribute.modifiedEntityAttribute(); (result, var doc_base) = await farming_effect_attribute.toDocBase(); if (result.isFail()) { err_msg = $"Failed to farming_effect_attribute.toDocBase() !!! : {result.toBasicString()} - {curr_map.toBasicString()}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var farming_effect_doc = doc_base as FarmingEffectDoc; NullReferenceCheckHelper.throwIfNull(farming_effect_doc, () => $"farming_effect_doc is null !!! - {owner.toBasicString()}"); result = await FarmingHelper.tryRewardAndPurge( farming_effect_doc , farming_effect_attribute.FarmingEndTime, new TimeSpan() , master_profile.MasterGuid, master_profile.MasterNickname , LogActionType.FarmingComplete ); if (result.isFail()) { err_msg = $"Failed to FarmingHelper.tryRewardFarming() !!! : {result.toBasicString()} - {curr_map.toBasicString()}, {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var origin_entity_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); origin_entity_attribute.copyEntityAttributeFromDoc(farming_effect_doc); curr_map.PropModifyNoti(curr_anchor_info.AnchorGuid); var farming_summary = owner.toFarmingSummary(); FarmingNotifyHelper.send_GS2C_NTF_FARMING_END(curr_map, curr_anchor_info, farming_summary); var player_manager = server_logic.getPlayerManager(); if (false == player_manager.tryGetUserByPrimaryKey(master_profile.MasterGuid, out var found_user)) { var login_cache_request = new LoginCacheOtherUserRequest(server_logic, server_logic.getRedisConnector(), master_profile.MasterGuid); await login_cache_request.fetchLogin(); var login_cache = login_cache_request.getLoginCache(); if (login_cache != null) { FarmingNotifyHelper.send_GS2MQS_NTF_FARMING_END( server_logic , login_cache.CurrentServer , master_profile.MasterGuid, farming_summary , false ); } } result = await tryRemoveFarmingEntity(root_parent); if (result.isFail()) { err_msg = $"Failed to tryRemoveFarmingEntity() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var remove_result = await curr_map.tryRemoveFarming(owner); if (remove_result.isFail()) { err_msg = $"Failed to tryRemoveInGameZone() !!! : {remove_result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } } else { result = await tryUpdateFarmig(); if (result.isFail()) { err_msg = $"Failed to tryUpdateFarmig() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } return result; } public async Task tryUpdateFarmig() { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var result = new Result(); var err_msg = string.Empty; var to_update_time = DateTimeHelper.Current; var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); // FarmingEffectAttribute 정보 설정 하기 var farming_effect_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(farming_effect_attribute, () => $"farming_effect_attribute is null !!! - {owner.toBasicString()}"); if(ServerCommon.Constant.FARMING_LAST_UPDATE_STORE_INTERVAL_MSEC < DateTimeHelper.differFromToMilliSeconds(farming_effect_attribute.FarmingLastUpdateTime, to_update_time)) { farming_effect_attribute.modifiedEntityAttribute(); (result, var doc_base) = await farming_effect_attribute.toDocBase(); if (result.isFail()) { err_msg = $"Failed to cloned_farming_effect_attribute.toDocBase() !!! : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var farming_effect_doc = doc_base as FarmingEffectDoc; NullReferenceCheckHelper.throwIfNull(farming_effect_doc, () => $"farming_effect_doc is null !!! - {owner.toBasicString()}"); result = await db_client.simpleUpdateDocumentWithDocType(farming_effect_doc); if (result.isFail()) { err_msg = $"Failed to simpleUpsertDocumentWithDocType() !!! in tryUpdateFarmig() : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); farming_effect_attribute.FarmingState = FarmingStateType.CoolingTime; // 예외 상황 !!! // 서버 메모리와 DB가 일치 않는다 !!! // 일단 DB 기준으로 메모리를 맞춘다 !!! - kangms result = await tryRemoveFarmingEntity(root_parent); if (result.isFail()) { err_msg = $"Failed to tryRemoveFarmingEntity() !!! in tryUpdateFarmig() : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } var remove_result = await curr_map.tryRemoveFarming(owner); if (remove_result.isFail()) { err_msg = $"Failed to tryRemoveInGameZone() !!! in tryUpdateFarmig() : {remove_result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } return result; } var origin_entity_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); origin_entity_attribute.copyEntityAttributeFromDoc(farming_effect_doc); } return result; } public async Task tryRemoveFarmingEntity(EntityBase farmingEntity) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var server_logic = GameServerApp.getServerLogic(); var db_client = server_logic.getDynamoDbClient(); var curr_anchor_info = getAnchorInfo(); var anchor_meta_guid = curr_anchor_info.AnchorGuid; var meta_id_of_anchor = (META_ID)curr_anchor_info.AnchorProp.TableId; var result = new Result(); var err_msg = string.Empty; if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue((int)meta_id_of_anchor, out var found_farming_prop_meta)) { err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{meta_id_of_anchor} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingPropMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (farmingEntity is Player player) { var farming_action = player.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_action, () => $"farming_action is null !!! - {owner.toBasicString()}"); farming_action.detachFarmingEffect(owner); } else if (farmingEntity is UgcNpc ugc_npc) { var ugc_npc_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {owner.toBasicString()}"); if (false == MapHelper.getAnchor(anchor_meta_guid, out var anchor)) { err_msg = $"Not found FarmingAnchor in Map !!! : FarmingAnchorGuid:{anchor_meta_guid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.FarmingAnchorNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } var farming_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(farming_action, () => $"farming_action is null !!! - {owner.toBasicString()}"); farming_action.detachFarmingEffect(owner); var base_pos = new System.Numerics.Vector3(curr_anchor_info.AnchorPos.X, curr_anchor_info.AnchorPos.Y, curr_anchor_info.AnchorPos.Z); var delta_pos = new System.Numerics.Vector3(found_farming_prop_meta.FarmingEndPosX, found_farming_prop_meta.FarmingEndPosY, found_farming_prop_meta.FarmingEndPosZ); var target_position = ugc_npc_action.makeToLocatePosition( base_pos, anchor.Rotation.Pitch, anchor.Rotation.Yaw, anchor.Rotation.Roll , delta_pos, found_farming_prop_meta.FarmingEndRotate ); ugc_npc_action.modifyStateInfo( EntityStateType.None, target_position ); var ugc_ncp_result = await ugc_npc_action.tryRemoveInGameZone(); if (ugc_ncp_result.isFail()) { err_msg = $"Failed to tryRemoveInGameZone() !!! in onUpdateFarming() : {ugc_ncp_result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); } var ugc_npc_attribute = ugc_npc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}"); (result, var ugc_npc_doc) = await ugc_npc_attribute.toDocBase(); if (result.isFail() || null == ugc_npc_doc) { err_msg = $"Failed to toDocBase() or Doc is null !!! in onUpdateFarming() : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } result = await DynamoDBDocBaseHelper.simpleUpdateDocumentWithDocType(db_client, ugc_npc_doc); if (result.isFail()) { err_msg = $"Failed to simpleUpsertDocumentWithDocType() !!! in onUpdateFarming() : {result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return result; } var origin_entity_attribute = ugc_npc.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(origin_entity_attribute, () => $"origin_entity_attribute is null !!! - {owner.toBasicString()}"); origin_entity_attribute.copyEntityAttributeFromDoc(ugc_npc_doc); Log.getLogger().info($"UgcNpc reset of State !!! in tryRemoveFarmingEntity() : {ugc_npc.toStateString()} - {ugc_npc.toBasicString()}"); var master_guid = ugc_npc.getMasterGuid(); var player_manager = server_logic.getPlayerManager(); if (false == player_manager.tryGetUserByPrimaryKey(master_guid, out var found_user)) { var login_cache_request = new LoginCacheOtherUserRequest(server_logic, server_logic.getRedisConnector(), master_guid); await login_cache_request.fetchLogin(); var login_cache = login_cache_request.getLoginCache(); if (login_cache != null) { UgcNpcNotifyHelper.send_GS2MQS_NTF_BEACON_COMPACT_SYNC( server_logic , login_cache.CurrentServer , master_guid, ugc_npc_action.toUgcNpcCompact() , origin_entity_attribute.LocatedInstanceGuid ); } } else { UgcNpcNotifyHelper.send_GS2C_NTF_BEACON_COMPACT_UPDATE(found_user, ugc_npc, ugc_npc_action.toUgcNpcCompact()); } } return result; } public DateTime toFarmingEndTime(DateTime startTime, ANCHOR_META_GUID anchorMetaGuid, Int16 farmingActionTryCount) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var curr_map = owner.getCurrMap(); var found_anchor_info = curr_map.findAnchorInfo(anchorMetaGuid); NullReferenceCheckHelper.throwIfNull(found_anchor_info, () => $"found_anchor_info is null !!! - {owner.toBasicString()}"); if (false == MetaData.Instance._FarmingPropMetaTable.TryGetValue(found_anchor_info.AnchorProp.TableId, out var found_farming_prop_meta)) { var err_msg = $"Not found FarmingPropMeta in FarmingPropMetaTable !!! : FarmingPropMetaId:{found_anchor_info.AnchorProp.TableId} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); return DateTimeHelper.MinTime; } var farming_reward_sec = 0; var entity_type = root_parent.getEntityType(); switch (entity_type) { case EntityType.Player: var player = root_parent as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {root_parent.toBasicString()}"); farming_reward_sec = found_farming_prop_meta.RewardCycleTimeUser * farmingActionTryCount; break; case EntityType.UgcNpc: var ugc_npc = root_parent as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!! - {root_parent.toBasicString()}"); farming_reward_sec = found_farming_prop_meta.RewardCycleTimeNpc * farmingActionTryCount; break; default: var err_msg = $"Invalid EntityType of FarmingEndTime !!! : entityType:{entity_type} - {root_parent.toBasicString()}"; Log.getLogger().error(err_msg); return DateTimeHelper.MinTime; } return startTime.AddSeconds(farming_reward_sec); } public OWNER_GUID toFarmingGuid() { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var entity_type = root_parent.getEntityType(); switch (entity_type) { case EntityType.Player: var player = root_parent as Player; NullReferenceCheckHelper.throwIfNull(player, () => $"player is null !!! - {root_parent.toBasicString()}"); return player.getUserGuid(); case EntityType.UgcNpc: case EntityType.Beacon: var ugc_npc = root_parent as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!! - {root_parent.toBasicString()}"); return ugc_npc.getUgcNpcMetaGuid(); default: var err_msg = $"Invalid EntityType of FarmingGuid !!! : entityType:{entity_type} - {root_parent.toBasicString()}"; Log.getLogger().error(err_msg); break; } return string.Empty; } public FarmingSummonedEntityType toFarmingSummonedEntityType() { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); var entity_type = root_parent.getEntityType(); switch(entity_type) { case EntityType.Player: return FarmingSummonedEntityType.User; case EntityType.UgcNpc: return FarmingSummonedEntityType.Beacon; default: var err_msg = $"Invalid EntityType of FarmingSummonedEntityType !!! : entityType:{entity_type} - {root_parent.toBasicString()}"; Log.getLogger().error(err_msg); break; } return FarmingSummonedEntityType.None; } public void attachAnchorInfo(AnchorInfo anchoInfo) { var owner = getOwner() as FarmingEffect; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); m_anchor_info_nullable = anchoInfo; } public AnchorInfo getAnchorInfo() { NullReferenceCheckHelper.throwIfNull(m_anchor_info_nullable, () => $"m_anchor_info_nullable is null !!!"); return m_anchor_info_nullable; } public FarmingRewardLogInfo getFarmingRewardLogInfo() => m_farming_reward_log_info; public FarmingLogInfo getFarmingLogInfo() => m_farming_log_info; }