869 lines
43 KiB
C#
869 lines
43 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
using Amazon.S3.Model;
|
|
|
|
|
|
using ServerCore; using ServerBase;
|
|
using ServerCommon;
|
|
using ServerCommon.BusinessLogDomain;
|
|
using static ServerCommon.MetaHelper;
|
|
|
|
|
|
using MetaAssets;
|
|
|
|
|
|
using static ClientToGameMessage.Types;
|
|
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 UGC_NPC_META_GUID = System.String;
|
|
using ITEM_GUID = System.String;
|
|
|
|
|
|
|
|
namespace GameServer
|
|
{
|
|
public class UgcNpcCreationOrLoadAction : EntityActionBase
|
|
{
|
|
private UgcNpc? m_reserved_ugc_npc_for_creation_nullable;
|
|
|
|
private readonly Dictionary<InvenEquipType, HashSet<Int16>> m_reserved_slot_types = new();
|
|
|
|
private ServerCommon.BusinessLogDomain.BeaconCreateLogInfo m_beacon_create_log_info = new();
|
|
private ServerCommon.BusinessLogDomain.BeaconLogInfo m_beacon_log_info = new();
|
|
|
|
public UgcNpcCreationOrLoadAction(Player owner)
|
|
: base(owner)
|
|
{
|
|
}
|
|
|
|
public override Task<Result> onInit()
|
|
{
|
|
var result = new Result();
|
|
|
|
return Task.FromResult(result);
|
|
}
|
|
|
|
public override void onClear()
|
|
{
|
|
return;
|
|
}
|
|
|
|
public void clearReservedSlotTypes()
|
|
{
|
|
m_reserved_slot_types.Clear();
|
|
}
|
|
|
|
public void resetReserved4UgcNpcCreation()
|
|
{
|
|
m_reserved_ugc_npc_for_creation_nullable = null;
|
|
}
|
|
|
|
public async Task<Result> tryLoadUgcNpcFromDoc(UgcNpcDoc ugcNpcDoc)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(ugcNpcDoc, () => $"ugcNpcDoc is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var to_load_ugc_npc = new UgcNpc(owner.getUserGuid());
|
|
result = await to_load_ugc_npc.onInit();
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
result = await player_action.tryLoadUgcNpc(to_load_ugc_npc, ugcNpcDoc);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
var ugc_npc_attribute = to_load_ugc_npc.getEntityAttribute<UgcNpcAttribute>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
m_beacon_log_info.setInfo(ugc_npc_attribute.toBeaconLogInfo());
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> tryLoadUgcNpcInGameZone(UgcNpc ugcNpcInGameZone)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(ugcNpcInGameZone, () => $"ugcNpcInGameZone is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
NullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
result = await player_action.tryAttachUgcNpcInGameZone(ugcNpcInGameZone);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> tryLoadApprearanceCustomize(DynamoDbClient dbClient, UGC_NPC_META_GUID ugcNpcMetaGuid)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(dbClient, () => $"dbClient is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<AppearanceCustomizeDoc>(ugcNpcMetaGuid);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - {owner.toBasicString()}");
|
|
|
|
var appearance_customize_query_config = dbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK);
|
|
// DB에 AppearanceCustomize 정보 없는 경우도 있을 수 있다 !!!
|
|
(var appearance_customize_result, var appearance_customize_read_doc) = await dbClient.simpleQueryDocTypeWithQueryOperationConfig<AppearanceCustomizeDoc>(appearance_customize_query_config);
|
|
if (appearance_customize_result.isSuccess())
|
|
{
|
|
NullReferenceCheckHelper.throwIfNull(appearance_customize_read_doc, () => $"appearance_customize_read_doc is null !!! - {owner.toBasicString()}");
|
|
|
|
appearance_customize_result = await tryLoadApprearanceCustomizeFromDoc(ugcNpcMetaGuid, appearance_customize_read_doc);
|
|
if (appearance_customize_result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected async Task<Result> tryLoadApprearanceCustomizeFromDoc(UGC_NPC_META_GUID ugcNpcMetaGuid, AppearanceCustomizeDoc appearanceCustomizeDoc)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(appearanceCustomizeDoc, () => $"appearanceCustomizeDoc is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var found_ugc_npc = player_action.findUgcNpc(ugcNpcMetaGuid);
|
|
if(null == found_ugc_npc)
|
|
{
|
|
err_msg = $"Not found UgcNpc !!! : UgcNpcMetaGuid:{ugcNpcMetaGuid} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
var beacon_appearance_customize_action = found_ugc_npc.getEntityAction<BeaconAppearanceCustomizeAction>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(beacon_appearance_customize_action, () => $"beacon_appearance_customize_action is null !!! - {owner.toBasicString()}");
|
|
|
|
result = await beacon_appearance_customize_action.tryLoadAppearanceCustomizeFromDoc(appearanceCustomizeDoc);
|
|
if(result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> tryLoadBeaconShopItem(DynamoDbClient dbClient, UGC_NPC_META_GUID ugcNpcMetaGuid)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(dbClient, () => $"dbClient is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<BeaconShopItemDoc>(ugcNpcMetaGuid);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - {owner.toBasicString()}");
|
|
|
|
var beacon_shop_item_query_config = dbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK);
|
|
(var beacon_shop_item_result, var beacon_shop_item_read_docs) = await dbClient.simpleQueryDocTypesWithQueryOperationConfig<BeaconShopItemDoc>(beacon_shop_item_query_config);
|
|
if (beacon_shop_item_result.isSuccess())
|
|
{
|
|
NullReferenceCheckHelper.throwIfNull(beacon_shop_item_read_docs, () => $"beacon_shop_item_read_docs is null !!! - {owner.toBasicString()}");
|
|
|
|
beacon_shop_item_result = await tryLoadBeaconShopItemDocFromDoc(beacon_shop_item_read_docs, ugcNpcMetaGuid);
|
|
if (beacon_shop_item_result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected async Task<Result> tryLoadBeaconShopItemDocFromDoc(List<BeaconShopItemDoc> beaconShopItemDocs, UGC_NPC_META_GUID ugcNpcMetaGuid)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(beaconShopItemDocs, () => $"beaconShopItemDocs is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var found_ugc_npc = player_action.findUgcNpc(ugcNpcMetaGuid);
|
|
if (null == found_ugc_npc)
|
|
{
|
|
err_msg = $"Not found UgcNpc !!! : UgcNpcMetaGuid:{ugcNpcMetaGuid} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
var ugc_npc_beacon_shop_action = found_ugc_npc.getEntityAction<UgcNpcBeaconShopAction>();
|
|
|
|
foreach (var read_doc in beaconShopItemDocs)
|
|
{
|
|
result = await ugc_npc_beacon_shop_action.addBeaconShopItem(read_doc);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> tryLoadBeaconShopProfile(DynamoDbClient dbClient, UGC_NPC_META_GUID ugcNpcMetaGuid)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(dbClient, () => $"dbClient is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<BeaconShopProfileDoc>(ugcNpcMetaGuid);
|
|
if (result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - {owner.toBasicString()}");
|
|
|
|
var beacon_shop_item_query_config = dbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK);
|
|
(var beacon_shop_item_result, var beacon_shop_profile_read_docs) = await dbClient.simpleQueryDocTypesWithQueryOperationConfig<BeaconShopProfileDoc>(beacon_shop_item_query_config);
|
|
if (beacon_shop_item_result.isSuccess())
|
|
{
|
|
NullReferenceCheckHelper.throwIfNull(beacon_shop_profile_read_docs, () => $"beacon_shop_profile_read_docs is null !!! - {owner.toBasicString()}");
|
|
|
|
if (beacon_shop_profile_read_docs.Count <= 0)
|
|
{
|
|
var beaconShopProfileDoc = new BeaconShopProfileDoc(ugcNpcMetaGuid);
|
|
var beaconShopProfileAttrib = beaconShopProfileDoc.getAttrib<BeaconShopProfileAttrib>();
|
|
NullReferenceCheckHelper.throwIfNull(beaconShopProfileAttrib, () => $"beaconShopProfileAttrib is null !!! - {owner.toBasicString()}");
|
|
beaconShopProfileAttrib.BeaconGuid = ugcNpcMetaGuid;
|
|
|
|
result = await dbClient.simpleInsertDocumentWithDocType(beaconShopProfileDoc);
|
|
if (result.isFail())
|
|
{
|
|
err_msg = $"Failed to simpleUpsertDocumentWithDocType<BeaconShopProfileDoc> !!! - {owner.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
return result;
|
|
}
|
|
(beacon_shop_item_result, beacon_shop_profile_read_docs) = await dbClient.simpleQueryDocTypesWithQueryOperationConfig<BeaconShopProfileDoc>(beacon_shop_item_query_config);
|
|
}
|
|
|
|
beacon_shop_item_result = tryLoadBeaconShopProfileDocFromDoc(beacon_shop_profile_read_docs, ugcNpcMetaGuid);
|
|
if (beacon_shop_item_result.isFail())
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected Result tryLoadBeaconShopProfileDocFromDoc(List<BeaconShopProfileDoc> beaconShopProfileDocs, UGC_NPC_META_GUID ugcNpcMetaGuid)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(beaconShopProfileDocs, () => $"beaconShopProfileDocs is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var found_ugc_npc = player_action.findUgcNpc(ugcNpcMetaGuid);
|
|
if (null == found_ugc_npc)
|
|
{
|
|
err_msg = $"Not found UgcNpc !!! : UgcNpcMetaGuid:{ugcNpcMetaGuid} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.UgcNpcNotFound, err_msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
var beacon_shop_profile_attribute = found_ugc_npc.getEntityAttribute<BeaconShopProfileAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(beacon_shop_profile_attribute, () => $"beacon_shop_profile_attribute is null !!! - {found_ugc_npc.toBasicString()}");
|
|
|
|
beacon_shop_profile_attribute.copyEntityAttributeFromDoc(beaconShopProfileDocs[0]);
|
|
|
|
beacon_shop_profile_attribute.copyEntityAttributeFromDoc(beaconShopProfileDocs[0]);
|
|
if (beacon_shop_profile_attribute.BeaconGuid == string.Empty)
|
|
beacon_shop_profile_attribute.BeaconGuid = ugcNpcMetaGuid;
|
|
|
|
return result;
|
|
}
|
|
|
|
private async Task<Result> checkCreateConditions(C2GS_REQ_UGC_NPC_CREATION request)
|
|
{
|
|
await Task.CompletedTask;
|
|
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var user_inventory_action = owner.getEntityAction<UserInventoryAction>();
|
|
NullReferenceCheckHelper.throwIfNull(user_inventory_action, () => $"user_inventory_action is null !!! - {owner.toBasicString()}");
|
|
|
|
//=====================================================================================
|
|
// 1. Body Item 보유 확인 하기
|
|
//=====================================================================================
|
|
var found_body_item = user_inventory_action.tryGetItemByItemGuid(request.BodyItemGuid);
|
|
if (null == found_body_item)
|
|
{
|
|
err_msg = $"Not found Body Item !!! : bodyItemGuid:{request.BodyItemGuid} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.ItemNotFound, err_msg);
|
|
|
|
return result;
|
|
}
|
|
var item_meta_data = found_body_item.getItemMeta();
|
|
NullReferenceCheckHelper.throwIfNull(item_meta_data, () => $"item_meta_data is null !!! - {owner.toBasicString()}");
|
|
if(item_meta_data.TypeSmall != EItemSmallType.BEACON_BODY)
|
|
{
|
|
err_msg = $"Not Beacon Body Item !!! : EItemSmallType.BEACON_BODY == {item_meta_data.TypeSmall}, bodyItemGuid:{request.BodyItemGuid} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.BeaconBodyItemInvalid, err_msg);
|
|
return result;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 2. 중복된 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;
|
|
}
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
NullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
//=====================================================================================
|
|
// 3. Ugc Npc 개수 제한 체크 하기
|
|
//=====================================================================================
|
|
if (player_action.getHadUgcNpcCount() >= GameConfigMeta.BeaconCreateMaxCount)
|
|
{
|
|
err_msg = $"UgcNpc max count exceed !!! : currCount:{player_action.getHadUgcNpcCount()} <= maxCount:{GameConfigMeta.BeaconCreateMaxCount} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.UgcNpcMaxCountExceed, err_msg);
|
|
|
|
return result;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 4. Ugc Npc 닉네임 제한 체크 하기
|
|
//=====================================================================================
|
|
var error_code = isValidNickname(request.Nickname);
|
|
if (error_code.isFail())
|
|
{
|
|
err_msg = $"Not valid UgcNpc Nickname !!! : {error_code.toBasicString()}, nick:{request.Nickname} - {owner.toBasicString()}";
|
|
result.setFail(error_code, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 5. 문자열 길이 제한 체크 하기
|
|
//=====================================================================================
|
|
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;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 6. 재료 아이템 체크 하기 : 의상 & 타투
|
|
//=====================================================================================
|
|
var to_chek_cloth_item_count = GameConfigMeta.NpcCreateDressRequirement;
|
|
var to_chek_tattoo_item_count = GameConfigMeta.NpcCreateTattooRequirement;
|
|
|
|
var found_user_item_attribute = found_body_item.getEntityAttribute<UserItemAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(found_user_item_attribute, () => $"found_user_item_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
if(false == MetaData.Instance.Meta.BeaconNpcMetaTable.BeaconNpcMetaDataListbyId.TryGetValue((Int32)found_user_item_attribute.ItemMetaId, out var found_beacon_npc_meta))
|
|
{
|
|
err_msg = $"Not found BeaconNpcMetaData !!! : bodyItemMetaId:{found_user_item_attribute.ItemMetaId} - {owner.toBasicString()}";
|
|
result.setFail(ServerErrorCode.UgcNpcMetaDataNotFound, 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)
|
|
{
|
|
err_msg = $"Not found Material Item for UgcNpc create !!! - {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;
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 6. 소셜 액션 체크 하기
|
|
//=====================================================================================
|
|
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;
|
|
}
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 7. 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;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<(Result, UgcNpc?)> tryCreateUgcNpc(C2GS_REQ_UGC_NPC_CREATION request)
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(request, () => $"request is null !!! - {owner.toBasicString()}");
|
|
|
|
// 직전에 예약되어 있는 슬롯을 모두 초기화 한다.
|
|
clearReservedSlotTypes();
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var account_attribute = owner.getEntityAttribute<AccountAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
var user_inventory_action = owner.getEntityAction<UserInventoryAction>();
|
|
NullReferenceCheckHelper.throwIfNull(user_inventory_action, () => $"user_inventory_action is null !!! - {owner.toBasicString()}");
|
|
|
|
//=====================================================================================
|
|
// 1. 기본적인 생성 조건 체크 하기
|
|
//=====================================================================================
|
|
result = await checkCreateConditions(request);
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 2. 금전 소모 처리
|
|
//=====================================================================================
|
|
var money_action = owner.getEntityAction<MoneyAction>();
|
|
NullReferenceCheckHelper.throwIfNull(money_action, () => $"money_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var req_currency_type = (CurrencyType)ServerCommon.MetaHelper.GameConfigMeta.NpcCreateCurrency;
|
|
var req_cost = GameConfigMeta.NpcCreateCost * -1;
|
|
result = await money_action.changeMoney(req_currency_type, req_cost);
|
|
if(result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 3. 바디 아이템 소모 처리
|
|
//=====================================================================================
|
|
(result, Item? deleted_body_item) = await user_inventory_action.tryDeleteItemByGuid(request.BodyItemGuid);
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
NullReferenceCheckHelper.throwIfNull(deleted_body_item, () => $"deleted_body_item is null !!! - {owner.toBasicString()}");
|
|
|
|
var body_item_attribute_base = deleted_body_item.getEntityAttribute<ItemAttributeBase>();
|
|
NullReferenceCheckHelper.throwIfNull(body_item_attribute_base, () => $"body_item_attribute_base is null !!! - {owner.toBasicString()}");
|
|
|
|
|
|
//=====================================================================================
|
|
// 4. UgcNpc 생성
|
|
//=====================================================================================
|
|
var ugc_npc = new UgcNpc(owner.getUserGuid());
|
|
result = await ugc_npc.onInit();
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
|
|
//=====================================================================================
|
|
// 5. ugc npc 생성 정보 설정 하기
|
|
//=====================================================================================
|
|
var ugc_npc_attribute = ugc_npc.getEntityAttribute<UgcNpcAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
ugc_npc_attribute.OwnerEntityType = OwnerEntityType.User;
|
|
ugc_npc_attribute.OwnerGuid = owner.getUserGuid();
|
|
ugc_npc_attribute.LanguageType = account_attribute.LanguageType;
|
|
|
|
ugc_npc_attribute.UgcNpcMetaGuid = request.BodyItemGuid;
|
|
ugc_npc_attribute.Nickname = request.Nickname;
|
|
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<Int32, META_ID>(x => (META_ID)x).ToList();
|
|
ugc_npc_attribute.DialogueSocialActionMetaIds = request.DialogueSocialActionIds.Select<Int32, META_ID>(x => (META_ID)x).ToList();
|
|
ugc_npc_attribute.HashTagMetaIds = request.HashTagMetaIds.Select<Int32, META_ID>(x => (META_ID)x).ToList();
|
|
ugc_npc_attribute.BodyItemMetaId = body_item_attribute_base.ItemMetaId;
|
|
|
|
ugc_npc_attribute.newEntityAttribute();
|
|
|
|
//=====================================================================================
|
|
// 6. User의 보유 아이템들을 ugc npc로 소유권 이전 시킨다. (의상 & 타투 아이템 및 타투 Visible 설정)
|
|
//=====================================================================================
|
|
var ugc_npc_action = ugc_npc.getEntityAction<UgcNpcAction>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {owner.toBasicString()}");
|
|
|
|
result = await ugc_npc_action.tryTakableItemFromUser( request.MaterialItemGuids.ToList()
|
|
, request.TattooSlotVisibles.ToDictionary<Int32, BoolType>()
|
|
, m_reserved_slot_types
|
|
, owner );
|
|
if (result.isFail())
|
|
{
|
|
return (result, null);
|
|
}
|
|
|
|
m_reserved_ugc_npc_for_creation_nullable = ugc_npc;
|
|
|
|
return (result, ugc_npc);
|
|
}
|
|
|
|
public async Task<QueryBatchBase.QueryResultType> completedUgcNpcCreation(QueryExecutorBase queryExecutorBase)
|
|
{
|
|
await Task.CompletedTask;
|
|
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
ArgumentNullReferenceCheckHelper.throwIfNull(queryExecutorBase, () => $"queryExecutorBase is null !!! - {owner.toBasicString()}");
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var player_action = owner.getEntityAction<PlayerAction>();
|
|
NullReferenceCheckHelper.throwIfNull(player_action, () => $"player_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var reserved_to_create_ugc_npc = getReservedUgcNpcForCreation();
|
|
NullReferenceCheckHelper.throwIfNull(reserved_to_create_ugc_npc, () => $"reserved_to_create_ugc_npc is null !!! - {owner.toBasicString()}");
|
|
|
|
var ugc_npc_attribute = reserved_to_create_ugc_npc.getOriginEntityAttribute<UgcNpcAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
var origin_ugc_npc_doc = ugc_npc_attribute.getOriginDocBase<UgcNpcAttribute>() as UgcNpcDoc;
|
|
NullReferenceCheckHelper.throwIfNull(origin_ugc_npc_doc, () => $"origin_ugc_npc_doc is null !!! - {owner.toBasicString()}");
|
|
|
|
result = await player_action.tryLoadUgcNpc(reserved_to_create_ugc_npc, origin_ugc_npc_doc);
|
|
if (result.isFail())
|
|
{
|
|
Log.getLogger().error(result.toBasicString());
|
|
}
|
|
|
|
var ugc_npc_action = reserved_to_create_ugc_npc.getEntityAction<UgcNpcAction>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_action, () => $"ugc_npc_action is null !!! - {owner.toBasicString()}");
|
|
|
|
// AiChatServer에 UgcNpc를 등록 시도 한다.
|
|
result = await ugc_npc_action.tryRegisterCharacterWithAiChatServer(owner);
|
|
if (result.isFail())
|
|
{
|
|
err_msg = $"Failed to tryRegisterCharacterWithAiChatServer() !!! : {result.toBasicString()} - {owner.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
}
|
|
// origin 메모리에 반영한다.
|
|
result = await player_action.commitTransaction(TransactionIdType.PrivateContents);
|
|
if (result.isFail())
|
|
{
|
|
err_msg = $"Failed to commitTransaction() !!! : {result.toBasicString()} - {owner.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
}
|
|
|
|
var query_batch = queryExecutorBase.getQueryBatch();
|
|
NullReferenceCheckHelper.throwIfNull(query_batch, () => $"query_batch is null !!! - {owner.toBasicString()}");
|
|
var log_action = query_batch.getLogAction();
|
|
NullReferenceCheckHelper.throwIfNull(log_action, () => $"log_action is null !!! - {owner.toBasicString()}");
|
|
|
|
// 비즈니스 로그 추가
|
|
queryExecutorBase.appendBusinessLog(new BeaconCreateBusinessLog(log_action, DateTimeHelper.Current, m_beacon_create_log_info));
|
|
queryExecutorBase.appendBusinessLog(new BeaconBusinessLog(log_action, reserved_to_create_ugc_npc.toBeaconLogInfo()));
|
|
|
|
return QueryBatchBase.QueryResultType.Success;
|
|
}
|
|
|
|
public ServerErrorCode isValidNickname(string nickName)
|
|
{
|
|
// 첫번째 글자가 숫자인지 체크 한다.
|
|
if (Regex.IsMatch(nickName[0].ToString(), "[0-9]") == true)
|
|
{
|
|
return ServerErrorCode.UserNicknameNotAllowedNumberAtFirstChars;
|
|
}
|
|
|
|
// 특수문자가 포함되어 있는지 체크 한다.
|
|
if (true == nickName.isContainSpecialChars(out _))
|
|
{
|
|
return ServerErrorCode.UserNicknameNotAllowWithSpecialChars;
|
|
}
|
|
|
|
// 길이 체크
|
|
if (nickName.Length < MetaHelper.GameConfigMeta.UserNicknameMinLength ||
|
|
nickName.Length > MetaHelper.GameConfigMeta.UserNicknameMaxLength)
|
|
{
|
|
return ServerErrorCode.UserNicknameAllowedMin2ToMax12;
|
|
}
|
|
|
|
// 금지어 여부를 체크 한다.
|
|
if (StringRuleHelper.isBanWord(nickName, true) == true)
|
|
{
|
|
Log.getLogger().info($"User Nickname is Ban !!! : nickname:{nickName}");
|
|
return ServerErrorCode.UserNicknameBan;
|
|
}
|
|
|
|
return ServerErrorCode.Success;
|
|
}
|
|
|
|
public async Task<List<DynamoDbDocBase>> getDocs4UgcNpcCreation()
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
|
|
var to_create_docs = new List<DynamoDbDocBase>();
|
|
|
|
var user_attribute = owner.getEntityAttribute<UserAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
var reserved_ugc_npc_for_creation = getReservedUgcNpcForCreation();
|
|
NullReferenceCheckHelper.throwIfNull(reserved_ugc_npc_for_creation, () => $"reserved_ugc_npc_for_creation is null !!! - {owner.toBasicString()}");
|
|
|
|
var ugc_npc_attribute = reserved_ugc_npc_for_creation.getEntityAttribute<UgcNpcAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
// UgcNpc 닉네임 관련 DB 정보를 생성 한다.
|
|
{
|
|
var to_create_doc = new UgcNpcNicknameRegistryDoc(OwnerEntityType.User, user_attribute.UserGuid, ugc_npc_attribute.Nickname, ugc_npc_attribute.UgcNpcMetaGuid);
|
|
await to_create_doc.newDoc4Query();
|
|
|
|
to_create_docs.Add(to_create_doc);
|
|
}
|
|
|
|
// UgcNpc 좋아요 선택받은 개수 관련 DB 정보를 생성 한다.
|
|
{
|
|
var to_create_doc = new UgcNpcLikeSelecteeCountDoc(OwnerEntityType.UgcNpc, ugc_npc_attribute.UgcNpcMetaGuid);
|
|
await to_create_doc.newDoc4Query();
|
|
|
|
to_create_docs.Add(to_create_doc);
|
|
}
|
|
|
|
return to_create_docs;
|
|
}
|
|
|
|
public GS2C_ACK_UGC_NPC_CREATION toAckUgcNpcCreation()
|
|
{
|
|
var owner = getOwner() as Player;
|
|
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
|
|
NullReferenceCheckHelper.throwIfNull(m_reserved_ugc_npc_for_creation_nullable, () => $"m_reserved_ugc_npc_for_creation_nullable is null !!! - {owner.toBasicString()}");
|
|
|
|
var ugc_npc_attribute = m_reserved_ugc_npc_for_creation_nullable.getEntityAttribute<UgcNpcAttribute>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_attribute, () => $"ugc_npc_attribute is null !!! - {owner.toBasicString()}");
|
|
|
|
var ugc_npc_inventory_action = m_reserved_ugc_npc_for_creation_nullable.getEntityAction<UgcNpcInventoryAction>();
|
|
NullReferenceCheckHelper.throwIfNull(ugc_npc_inventory_action, () => $"ugc_npc_inventory_action is null !!! - {owner.toBasicString()}");
|
|
|
|
var ack = new GS2C_ACK_UGC_NPC_CREATION();
|
|
ack.UgcNpcMetaGuid = ugc_npc_attribute.UgcNpcMetaGuid;
|
|
ack.Nickname = ugc_npc_attribute.Nickname;
|
|
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<META_ID, Int32>(x => (Int32)x).ToList());
|
|
ack.DialogueSocialActionIds.AddRange(ugc_npc_attribute.DialogueSocialActionMetaIds.Select<META_ID, Int32>(x => (Int32)x).ToList());
|
|
ack.TattooSlotInfos.Add(ugc_npc_inventory_action.toTattooAll4Client().toMapField());
|
|
ack.HashTagMetaIds.Add(ugc_npc_attribute.HashTagMetaIds.Select<META_ID, Int32>(x => (Int32)x).ToList());
|
|
|
|
resetReserved4UgcNpcCreation();
|
|
|
|
var found_transaction_runner = owner.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if(null == found_transaction_runner)
|
|
{
|
|
var err_msg = $"Not found TransactionRunner !!! : {toBasicString()} - {owner.toBasicString()}";
|
|
Log.getLogger().warn(err_msg);
|
|
|
|
return ack;
|
|
}
|
|
|
|
ack.CommonResult = found_transaction_runner.getCommonResult();
|
|
|
|
return ack;
|
|
}
|
|
|
|
public UgcNpc? getReservedUgcNpcForCreation() => m_reserved_ugc_npc_for_creation_nullable;
|
|
}
|
|
}
|