using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Numerics; using Amazon.S3.Model; using ServerCore; using ServerBase; using ServerCommon; using ServerCommon.BusinessLogDomain; using static ServerCommon.MetaHelper; using MetaAssets; using static ClientToGameReq.Types; using static ClientToGameRes.Types; using SESSION_ID = System.Int32; using WORLD_META_ID = System.UInt32; using META_ID = System.UInt32; using ENTITY_GUID = System.String; using ACCOUNT_ID = System.String; using OWNER_GUID = System.String; using USER_GUID = System.String; using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; namespace GameServer { public class UgcNpcEditAction : EntityActionBase { private UgcNpc? m_reserved_ugc_npc_for_edit_nullable; private readonly Dictionary> m_reserved_slot_types = new(); public UgcNpcEditAction(UgcNpc owner) : base(owner) { } public override Task onInit() { var result = new Result(); return Task.FromResult(result); } public override void onClear() { return; } public void clearReservedSlotTypes() { m_reserved_slot_types.Clear(); } public void resetReserved4UgcNpcEdit() { m_reserved_ugc_npc_for_edit_nullable = null; } private async Task checkEditConditions(C2GS_REQ_UGC_NPC_EDIT request) { await Task.CompletedTask; var owner = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); NullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var master = owner.onGetMasterEntity(); if(null == master) { err_msg = $"Not found Master !!! - {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); return result; } var ugc_npc_attribute = owner.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}"); var ugc_npc_inventory_action = owner.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_inventory_action, () => $"ugc_npc_inventory_action is null !!! - {owner.toBasicString()}"); var user_inventory_action = master.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_inventory_action, () => $"user_inventory_action is null !!! - {owner.toBasicString()}"); //===================================================================================== // 1. 중복된 ITEM_GUID 존재 체크 하기 //===================================================================================== var found_duplicated_guids = InventoryRuleHelper.findContainDuplicatedItemGuid(request.MaterialItemGuids.ToList()); if(0 < found_duplicated_guids.Count) { err_msg = $"ItemGuid duplicated !!! - {owner.toBasicString()}"; result.setFail(ServerErrorCode.ItemGuidDuplicated, err_msg); return result; } //===================================================================================== // 2. 문자열 길이 제한 체크 하기 //===================================================================================== if (request.Description.Length > GameConfigMeta.MaxNpcDescriptionChar) { err_msg = $"UgcNpc Description max count exceed !!! : currCount:{request.Description.Length} < maxCount:{GameConfigMeta.MaxNpcDescriptionChar} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcDescriptionLengthExceed, err_msg); return result; } if (request.WorldScenario.Length > GameConfigMeta.MaxNpcBackGroundChar) { err_msg = $"UgcNpc WorldScenario max count exceed !!! : currCount:{request.WorldScenario.Length} < maxCount:{GameConfigMeta.MaxNpcBackGroundChar} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcWordScenarioLengthExceed, err_msg); return result; } if (request.Greeting.Length > GameConfigMeta.MaxNpcGreetingChar) { err_msg = $"UgcNpc Greeting max count exceed !!! : currCount:{request.Greeting.Length} < maxCount:{GameConfigMeta.MaxNpcGreetingChar} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcGreetingLengthExceed, err_msg); return result; } if (request.Introduction.Length > GameConfigMeta.MaxNpcIntroductionChar) { err_msg = $"UgcNpc Introduction max count exceed !!! : currCount:{request.Introduction.Length} < maxCount:{GameConfigMeta.MaxNpcIntroductionChar} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcIntroductionLengthExceed, err_msg); return result; } //===================================================================================== // 3. 소셜 액션 체크 하기 //===================================================================================== if (false == MetaData.Instance._SocialActionTable.TryGetValue(request.DefaultSocialActionId, out var social_action_meta_data)) { err_msg = $"Not found Default SocialAction MetaId !!! : SocialMetaId:{request.DefaultSocialActionId} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.SocialActionMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } if (request.HabitSocialActionIds.Count > GameConfigMeta.MaxNpcFrequentSocial) { err_msg = $"Ugc Npc Habit SocialAction Count Exceed !!! : currCount:{request.HabitSocialActionIds.Count} < maxCount:{GameConfigMeta.MaxNpcFrequentSocial} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcHabitSocialActionCountExceed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } foreach (var social_action_meta_id in request.HabitSocialActionIds) { if (false == MetaData.Instance._SocialActionTable.TryGetValue(social_action_meta_id, out social_action_meta_data)) { err_msg = $"Not found Habit SocialAction MetaId !!! : SocialMetaId:{social_action_meta_id} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.SocialActionMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } if (request.DialogueSocialActionIds.Count > GameConfigMeta.MaxNpcDialogueSocial) { err_msg = $"Ugc Npc Dialogue SocialAction Count Exceed !!! : currCount:{request.DialogueSocialActionIds.Count} < maxCount:{GameConfigMeta.MaxNpcDialogueSocial} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcDialogueSocialActionCountExceed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } foreach (var social_action_meta_id in request.DialogueSocialActionIds) { if (false == MetaData.Instance._SocialActionTable.TryGetValue(social_action_meta_id, out social_action_meta_data)) { err_msg = $"Not found Inaction SocialAction MetaId !!! : SocialMetaId:{social_action_meta_id} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.SocialActionMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } //===================================================================================== // 4. Npc Tag 체크 하기 //===================================================================================== if (request.HashTagMetaIds.Count > GameConfigMeta.MaxNpcTag) { err_msg = $"Ugc Npc Tag Count Exceed !!! : currCount:{request.HashTagMetaIds.Count} < maxCount:{GameConfigMeta.MaxNpcTag} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcMaxTagExceed, err_msg); Log.getLogger().error(result.toBasicString()); return result; } //===================================================================================== // 5. 재료 아이템 체크 하기 : 의상 & 타투 //===================================================================================== var to_chek_cloth_item_count = GameConfigMeta.NpcCreateDressRequirement; var to_chek_tattoo_item_count = GameConfigMeta.NpcCreateTattooRequirement; if (false == MetaData.Instance.Meta.BeaconNpcMetaTable.BeaconNpcMetaDataListbyId.TryGetValue((Int32)ugc_npc_attribute.BodyItemMetaId, out var found_beacon_npc_meta)) { err_msg = $"Not found BeaconNpcMetaData !!! : bodyItem < maxCount:{GameConfigMeta.MaxNpcIntroductionChar} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcIntroductionLengthExceed, err_msg); return result; } var default_cloth_item_meta_ids = found_beacon_npc_meta.DefaultWears.toMetaIds(); var default_cloth_item_count = default_cloth_item_meta_ids.Count; var selected_cloth_item_count = 0; var selected_tattoo_item_count = 0; foreach (var material_item_guid in request.MaterialItemGuids) { var found_item = user_inventory_action.tryGetItemByItemGuid(material_item_guid); if (null == found_item) { found_item = ugc_npc_inventory_action.tryGetEquipItemByItemGuid(material_item_guid); if(null == found_item) { err_msg = $"Not found Material Item for Ugc Npc !!! : itemGuid:{material_item_guid} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.ItemNotFound, err_msg); return result; } } var item_meta = found_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {owner.toBasicString()}"); if (true == item_meta.isClothType()) { selected_cloth_item_count++; to_chek_cloth_item_count--; Int32.Max(to_chek_cloth_item_count, 0); } else if (true == item_meta.isTattooType()) { selected_tattoo_item_count++; to_chek_tattoo_item_count--; Int32.Max(to_chek_tattoo_item_count, 0); } if (0 == to_chek_cloth_item_count && 0 == to_chek_tattoo_item_count) { break; } } if (0 < Int32.Max(to_chek_cloth_item_count - default_cloth_item_count, 0)) { err_msg = $"Not enough Cloth Item for Ugc Npc !!!" + $" : defaultClothItemCount:{default_cloth_item_count} + selectedClothItemCount:{selected_cloth_item_count} <= toCheckClothItemCount:{to_chek_cloth_item_count} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcClothItemNotEnough, err_msg); return result; } if (0 < to_chek_tattoo_item_count) { err_msg = $"Not enough Tattoo Item for Ugc Npc !!!" + $" : selectedTattooItemCount:{selected_tattoo_item_count} <= toCheckTattooItemCount:{to_chek_tattoo_item_count} - {owner.toBasicString()}"; result.setFail(ServerErrorCode.UgcNpcTattooItemNotEnough, err_msg); return result; } return result; } public async Task<(Result, UgcNpc?)> tryEditUgcNpc(C2GS_REQ_UGC_NPC_EDIT request) { var ugc_npc = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!!"); var result = new Result(); var err_msg = string.Empty; var master = ugc_npc.onGetMasterEntity() as Player; if (null == master) { err_msg = $"Not found Master !!! - {ugc_npc.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); return (result, null); } // 직전에 예약되어 있는 슬롯을 모두 초기화 한다. clearReservedSlotTypes(); var account_attribute = master.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {ugc_npc.toBasicString()}"); var user_inventory_action = master.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_inventory_action, () => $"user_inventory_action is null !!! - {ugc_npc.toBasicString()}"); //===================================================================================== // 1. 기본적인 편집 조건 체크 하기 //===================================================================================== result = await checkEditConditions(request); if (result.isFail()) { return (result, null); } //===================================================================================== // 2. 금전 소모 처리 //===================================================================================== var money_action = master.getEntityAction(); NullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {master.toBasicString()}"); var req_currency_type = (CurrencyType)ServerCommon.MetaHelper.GameConfigMeta.NpcEditCurrency; result = await money_action.spendMoney(req_currency_type, GameConfigMeta.NpcEditCost); if (result.isFail()) { return (result, null); } //===================================================================================== // 3. 기타 재설정 //===================================================================================== var ugc_npc_attribute = ugc_npc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {master.toBasicString()}"); ugc_npc_attribute.Title = request.Title; ugc_npc_attribute.Greeting = request.Greeting; ugc_npc_attribute.Introduction = request.Introduction; ugc_npc_attribute.Description = request.Description; ugc_npc_attribute.WorldScenario = request.WorldScenario; ugc_npc_attribute.DefaultSocialActionMetaId = (META_ID)request.DefaultSocialActionId; ugc_npc_attribute.HabitSocialActionMetaIds = request.HabitSocialActionIds.Select(x => (META_ID)x).ToList(); ugc_npc_attribute.DialogueSocialActionMetaIds = request.DialogueSocialActionIds.Select(x => (META_ID)x).ToList(); ugc_npc_attribute.HashTagMetaIds = request.HashTagMetaIds.Select(x => (META_ID)x).ToList(); ugc_npc_attribute.modifiedEntityAttribute(); //===================================================================================== // 4. User의 보유 아이템들을 ugc npc로 소유권 이전 시킨다. (의상 & 타투 아이템 및 타투 Visible 설정) //===================================================================================== result = await tryTakableItemFromUser( request.MaterialItemGuids.ToList() , request.TattooSlotVisibles.ToDictionary() , m_reserved_slot_types , master ); if (result.isFail()) { return (result, null); } m_reserved_ugc_npc_for_edit_nullable = ugc_npc; return (result, ugc_npc); } public async Task tryTakableItemFromUser( List materialItemGuids, Dictionary tattooSlotVisibles , Dictionary> reservedSlotTypes , Player master) { var ugc_npc = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!!"); var result = new Result(); var err_msg = string.Empty; var user_inventory_action = master.getEntityAction(); NullReferenceCheckHelper.throwIfNull(user_inventory_action, () => $"user_inventory_action is null !!! - {master.toBasicString()}"); var ugc_npc_inventory_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_inventory_action, () => $"ugc_npc_inventory_action is null !!! - {ugc_npc.toBasicString()}"); //===================================================================================== // 1. 삭제해야 하는 아이템 목록을 Ugc Npc 장착 인벤에서 제거 한다. //===================================================================================== // 장착된 모든 아이템을 얻는다. var ugc_npc_equip_items = ugc_npc_inventory_action.getEquipItemAll(); // 삭제해야 하는 ITEM_GUID 목록을 추출 한다. var to_delete_item_guids = KeyComparer.getKeysOnlyInSecond( materialItemGuids , ugc_npc_equip_items.Select(x => { var ugc_npc_item_attribute = x.getEntityAttributeWithReadOnly(); NullReferenceCheckHelper.throwIfNull(ugc_npc_item_attribute, () => $"ugc_npc_item_attribute is null !!! - {ugc_npc.toBasicString()}"); return ugc_npc_item_attribute.ItemGuid; }).ToList() ); foreach(var to_delete_item_guid in to_delete_item_guids) { (result, var deleted_item) = await ugc_npc_inventory_action.tryDeleteItemByGuid(to_delete_item_guid); if (result.isFail()) { return result; } } //===================================================================================== // 2. 추가해야 하는 아이템 목록을 Ugc Npc 장착 인벤에 추가 한다. User => UgcNpc //===================================================================================== // 소유자를 변경해야 하는 ITEM_GUID 목록을 추출 한다. var to_change_owner_item_guids = KeyComparer.getKeysOnlyInSecond( ugc_npc_equip_items.Select(x => { var ugc_npc_item_attribute = x.getEntityAttributeWithReadOnly(); NullReferenceCheckHelper.throwIfNull(ugc_npc_item_attribute, () => $"ugc_npc_item_attribute is null !!! - {ugc_npc.toBasicString()}"); return ugc_npc_item_attribute.ItemGuid; }).ToList() , materialItemGuids ); foreach (var to_change_owner_item_guid in to_change_owner_item_guids) { var found_item = user_inventory_action.tryGetItemByItemGuid(to_change_owner_item_guid); if(null == found_item) { err_msg = $"Not found Item for Owner Change !!!, in UserInventory : itemGuid:{to_change_owner_item_guid} - {ugc_npc.toBasicString()}"; result.setFail(ServerErrorCode.ItemNotFound, err_msg); return result; } var item_meta = found_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {master.toBasicString()}"); var backup_item_attribute = found_item.getClonedEntityAttribute(); NullReferenceCheckHelper.throwIfNull(backup_item_attribute, () => $"backup_item_attribute is null !!! - {master.toBasicString()}"); (result, var deleted_item) = await user_inventory_action.tryDeleteItemByGuid(to_change_owner_item_guid); if (result.isFail()) { return result; } (result, ItemDoc? to_create_item_doc_of_ugc_npc) = await backup_item_attribute.changeOwnerAndCreateItemDoc(OwnerEntityType.UgcNpc, ugc_npc.getUgcNpcMetaGuid()); if (result.isFail()) { return result; } NullReferenceCheckHelper.throwIfNull(to_create_item_doc_of_ugc_npc, () => $"to_create_item_doc_of_ugc_npc is null !!! - {master.toBasicString()}"); (result, var equipable_slot) = ugc_npc_inventory_action.getEquipableSlotOfEquipInven(item_meta, reservedSlotTypes); if (result.isFail()) { return result; } (result, _) = await ugc_npc_inventory_action.tryTakableToBagWithEquip(to_create_item_doc_of_ugc_npc, equipable_slot); if (result.isFail()) { return result; } } //===================================================================================== // 2. 타투 아이템 보이기/안보이기 설정 //===================================================================================== var tattoo_inven = ugc_npc_inventory_action.getEquipInvens()[InvenEquipType.Tattoo] as TattooEquipInven; NullReferenceCheckHelper.throwIfNull(tattoo_inven, () => $"tattoo_inven is null !!! - {master.toBasicString()}"); foreach (var each in tattooSlotVisibles) { var slot_index = each.Key; var slot_visible = each.Value; if (false == InvenEquipType.Tattoo.isEquipableTattooSlotType((TattooSlotType)slot_index, ugc_npc.getEntityType())) { err_msg = $"Not found TattooSlotType !!! : slotType:{slot_index} - {toBasicString()}, {master.toBasicString()}"; result.setFail(ServerErrorCode.TattooEquipRuleTattooSlotTypeNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } var custom_defined_ui_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(custom_defined_ui_action, () => $"custom_defined_ui_action is null !!! - {toBasicString()}, {master.toBasicString()}"); var tattoo_slot = (TattooSlotType)slot_index; result = await custom_defined_ui_action.setTattooVisible(tattoo_slot, (slot_visible == BoolType.True ? true : false)); if(result.isFail()) { return result; } } return result; } public async Task completedUgcNpcEdit(QueryExecutorBase queryExecutorBase) { await Task.CompletedTask; var owner = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var result = new Result(); var err_msg = string.Empty; var query_result = QueryBatchBase.QueryResultType.Success; var master = owner.onGetMasterEntity() as Player; if (null == master) { err_msg = $"Not found Master !!! - {owner.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return query_result; } // AiChatServer에 UgcNpc를 시도 한다. result = await tryEditCharacterWithAiChatServer(master); if (result.isFail()) { err_msg = $"Failed to tryEditCharacterWithAiChatServer() !!!, {result.toBasicString()} - {master.toBasicString()}"; Log.getLogger().error(err_msg); } var query_batch = queryExecutorBase.getQueryBatch(); NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!! - {master.toBasicString()}"); var log_action = query_batch.getLogAction(); NullReferenceCheckHelper.throwIfNull(log_action, () => $"log_action is null !!! - {master.toBasicString()}"); // 비즈니스 로그 추가 queryExecutorBase.appendBusinessLog(new BeaconBusinessLog(log_action, owner.toBeaconLogInfo())); return QueryBatchBase.QueryResultType.Success; } public async Task tryEditCharacterWithAiChatServer(Player master) { var ugc_npc = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!!"); var server_logic = GameServerApp.getServerLogic(); var result = new Result(); var err_msg = string.Empty; var ugc_npc_action = ugc_npc.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {ugc_npc.toBasicStringWithMaster()}"); var ai_chat_action = master.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ai_chat_action, () => $"ai_chat_action is null !!! - {master.toBasicString()}"); var ugc_npc_attribute = ugc_npc.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {ugc_npc.toBasicStringWithMaster()}"); if(false == ugc_npc_attribute.IsRegisteredAiChatServer) { err_msg = $"Not registered UgcNpc at AiChatServer !!! : ugcNpcMetaGuid:{ugc_npc_attribute.UgcNpcMetaGuid}, ugcNpcNickname:{ugc_npc_attribute.Nickname}, " + $" - {ugc_npc.toBasicStringWithMaster()}"; Log.getLogger().error(err_msg); result = await ai_chat_action.registerCharacter(ugc_npc_action.toRegisterInfoWithAiChatServer()); if (result.isFail()) { err_msg = $"Failed to AiChatAction.registerCharacter() !!!, {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } } result = await ai_chat_action.updateCharacter(ugc_npc_action.toRegisterInfoWithAiChatServer()); if (result.isFail()) { err_msg = $"Failed to AiChatAction.updateCharacter() !!!, {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } ugc_npc_attribute.IsRegisteredAiChatServer = true; ugc_npc_attribute.modifiedEntityAttribute(); (result, var ugc_npc_doc) = await ugc_npc_attribute.toDocBase(); if (result.isFail() || null == ugc_npc_doc) { return result; } var dynamo_db_client = server_logic.getDynamoDbClient(); NullReferenceCheckHelper.throwIfNull(dynamo_db_client, () => $"dynamo_db_client is null !!! - {ugc_npc.toBasicStringWithMaster()}"); result = await dynamo_db_client.simpleUpsertDocumentWithDocType(ugc_npc_doc); if (result.isFail()) { err_msg = $"Failed to DynamoDbClient.simpleUpsertDocumentWithDocType() !!! : {result.toBasicString()} - {ugc_npc.toBasicStringWithMaster()}"; Log.getLogger().error(err_msg); return result; } return result; } public async Task<(Result, GS2C_ACK_UGC_NPC_EDIT?)> toAckUgcNpcEdit() { var ugc_npc = getOwner() as UgcNpc; NullReferenceCheckHelper.throwIfNull(ugc_npc, () => $"ugc_npc is null !!!"); var result = new Result(); var err_msg = string.Empty; var master = ugc_npc.onGetMasterEntity(); if (null == master) { err_msg = $"Not found Master !!! - {ugc_npc.toBasicString()}"; result.setFail(ServerErrorCode.MasterNotFound, err_msg); return (result, null); } NullReferenceCheckHelper.throwIfNull(m_reserved_ugc_npc_for_edit_nullable, () => $"m_reserved_ugc_npc_for_edit_nullable is null !!! - {ugc_npc.toBasicString()}"); var ugc_npc_attribute = m_reserved_ugc_npc_for_edit_nullable.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {ugc_npc.toBasicString()}"); var ugc_npc_inventory_action = m_reserved_ugc_npc_for_edit_nullable.getEntityAction(); NullReferenceCheckHelper.throwIfNull(ugc_npc_inventory_action, () => $"ugc_npc_inventory_action is null !!! - {ugc_npc.toBasicString()}"); var ack = new GS2C_ACK_UGC_NPC_EDIT(); ack.EditedUgcNpcMetaGuid = ugc_npc_attribute.UgcNpcMetaGuid; ack.Title = ugc_npc_attribute.Title; ack.Greeting = ugc_npc_attribute.Greeting; ack.Introduction = ugc_npc_attribute.Introduction; ack.Description = ugc_npc_attribute.Description; ack.WorldScenario = ugc_npc_attribute.WorldScenario; ack.DefaultSocialActionId = (Int32)ugc_npc_attribute.DefaultSocialActionMetaId; ack.HabitSocialActionIds.AddRange(ugc_npc_attribute.HabitSocialActionMetaIds.Select(x => (Int32)x).ToList()); ack.DialogueSocialActionIds.AddRange(ugc_npc_attribute.DialogueSocialActionMetaIds.Select(x => (Int32)x).ToList()); ack.HashTagMetaIds.Add(ugc_npc_attribute.HashTagMetaIds.Select(x => (Int32)x).ToList()); resetReserved4UgcNpcEdit(); var found_transaction_runner = master.findTransactionRunner(TransactionIdType.PrivateContents); if (null == found_transaction_runner) { err_msg = $"Not found TransactionRunner !!! : {toBasicString()} - {ugc_npc.toBasicString()}"; Log.getLogger().warn(err_msg); return (result, null); } ack.CommonResult = found_transaction_runner.getCommonResult(); return await Task.FromResult((result, ack)); } } }