using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics.Eventing.Reader; using System.Runtime.CompilerServices; using Newtonsoft.Json; using Pipelines.Sockets.Unofficial.Buffers; using Amazon.DynamoDBv2.Model; using ServerCore; using ServerBase; 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 ServerCommon; //=========================================================================================== // Meta 관련 각종 지원 함수 // // author : kangms // //=========================================================================================== public static partial class MetaHelper { public static bool isTestAccount(this string accountId) { var err_msg = string.Empty; // 1. TestUserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. if (true == MetaData.Instance.m_test_user_create_meta_datas.ContainsKey(accountId)) { return true; } return false; } public static async Task<(Result, MetaAssets.TestUserCreateMetaData?)> getTestUserCreateDataByAccountId(string accountId) { await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; // 1. TestUserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. if (false == MetaData.Instance.m_test_user_create_meta_datas.TryGetValue(accountId, out var found_test_user_create_data)) { err_msg = $"Not found TestUserCreateData !!! : accountId:{accountId}"; Log.getLogger().warn(err_msg); // 1.1. TestUserCreateData Meta 파일에서 기본 계정 정보가 존재하는 지 체크 한다. if (false == MetaData.Instance.m_test_user_create_meta_datas.TryGetValue(MetaHelper.GameConfigMeta.DefaultTestAccountId, out found_test_user_create_data)) { err_msg = $"Not found TestUserCreateData !!! : DefaultTestId:{MetaHelper.GameConfigMeta.DefaultTestAccountId} - accountId:{accountId}"; result.setFail(ServerErrorCode.MetaDataNotFoundByTestUserId, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } } return (result, found_test_user_create_data); } public static async Task<(Result, MetaAssets.UserCreateMetaData?)> getUserCreateDataByAccountId(META_ID userCreateMetaId, string accountId) { await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; // 1. UserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. if (false == MetaData.Instance.m_user_create_meta_datas.TryGetValue((int)userCreateMetaId, out var found_user_create_data)) { err_msg = $"Not found UserCreateData !!! : metaId:{userCreateMetaId} - accountId:{accountId}"; Log.getLogger().error(err_msg); return (result, null); } return (result, found_user_create_data); } public static async Task<(Result, AccountBaseDoc?)> fillupAccountBaseDocForAccountCreate(UserBase userBase, string accountId) { await Task.CompletedTask; var result = new Result(); var err_msg = string.Empty; // 1. AccountAttribute 기본 생성 정보를 설정 한다. var account_attribute = userBase.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {userBase.toBasicString()}"); account_attribute.AccountId = accountId; account_attribute.AccountCreationType = (accountId.isContainsBotId() == false ? AccountCreationType.Normal : AccountCreationType.Bot); account_attribute.newUserGuid(); // LoginCache 및 Game 시스템에서 유저 식별을 할 수 있도록 여기서 UserGuid를 미리 발급해 둔다 !!! - kangms account_attribute.LanguageType = LanguageType.Ko; account_attribute.newEntityAttribute(); // 2. AccountBaseDoc를 생성 한다. (result, var new_account_base_doc) = await account_attribute.toDocBase(); if (result.isFail()) { return (result, null); } NullReferenceCheckHelper.throwIfNull(new_account_base_doc, () => $"new_account_base_doc is null !!! - {userBase.toBasicString()}"); var account_base_attrib = new_account_base_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(account_base_attrib, () => $"account_base_attrib is null - {userBase.toBasicString()}"); account_base_attrib.CreatedDateTime = DateTimeHelper.Current; return (result, new_account_base_doc as AccountBaseDoc); } public static async Task<(Result, AccountBaseDoc?)> fillupAccountBaseDocByTestUserCreateData(UserBase userBase, string accountId) { var result = new Result(); var err_msg = string.Empty; AccountBaseDoc? new_account_base_doc; try { // 1. TestUserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. (result, var found_test_user_create_data) = await getTestUserCreateDataByAccountId(accountId); if (result.isFail() || null == found_test_user_create_data) { err_msg = $"Not found TestUserCreateData for Account Create !!! : {result.toBasicString()} - accountId:{accountId}"; Log.getLogger().error(err_msg); return (result, null); } // 1. AccountAttribute 기본 생성 정보를 설정 한다. var account_attribute = userBase.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {userBase.toBasicString()}"); account_attribute.AccountId = accountId; account_attribute.AccountCreationType = (accountId.isContainsBotId() == false ? AccountCreationType.Test : AccountCreationType.Bot); account_attribute.newUserGuid();// LoginCache 및 Game 시스템에서 유저 식별을 할 수 있도록 여기서 UserGuid를 미리 발급해 둔다 !!! - kangms account_attribute.newEntityAttribute(); if (false == account_attribute.copyEntityAttributeFromMeta(found_test_user_create_data)) { err_msg = $"Failed to copyEntityAttributeFromMeta() !!! : to:{account_attribute.getTypeName()}, from:{found_test_user_create_data.getTypeName()} - accountId:{accountId}"; result.setFail(ServerErrorCode.MetaDataCopyToEntityAttributeFailed, err_msg); Log.getLogger().error(err_msg); return (result, null); } // 2. AccountBaseDoc를 생성 한다. (result, var maked_doc) = await account_attribute.toDocBase(); if (result.isFail()) { return (result, null); } new_account_base_doc = maked_doc as AccountBaseDoc; NullReferenceCheckHelper.throwIfNull(new_account_base_doc, () => $"new_account_base_doc is null - {userBase.toBasicString()}"); var account_base_attrib = new_account_base_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(account_base_attrib, () => $"account_base_attrib is null - {userBase.toBasicString()}"); account_base_attrib.CreatedDateTime = DateTimeHelper.Current; // 3. AccountBaseDoc를 추가로 구성한다. // - TestUserCreateData => AccountBaseDoc 에 선택적으로 복사한다. result = await DataCopyHelper.copyDocFromMetas(new_account_base_doc, new List() { found_test_user_create_data }); if (result.isFail()) { err_msg = $"Failed to copyDocFromMetas() !!! : accountId:{accountId}"; Log.getLogger().error(err_msg); return (result, null); } } catch (Exception e) { err_msg = $"Failed to fillupAccountBaseDocByTestUserCreateData() !!! : Exception:{e} - accountId:{accountId}"; result.setFail(ServerErrorCode.DotNetException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, new_account_base_doc); } public static async Task<(Result, UserBaseDoc?)> fillupUserBaseDocBy(UserBase userBase, AccountBaseDoc accountBaseDoc) { ArgumentNullReferenceCheckHelper.throwIfNull(userBase, () => $"userBase is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(accountBaseDoc, () => $"accountBaseDoc is null !!! - {userBase.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var owner = userBase; var account_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {userBase.toBasicString()}"); var account_id = account_attribute.AccountId; var user_attribute = owner.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(user_attribute, () => $"user_attribute is null !!! - {userBase.toBasicString()}"); user_attribute.GameLoginDateTime = owner.getLoginStartTime(); user_attribute.newEntityAttribute(); UserBaseDoc? user_base_doc_nullable; try { if (false == user_attribute.copyEntityAttributeFromAccountBaseDoc(accountBaseDoc)) { err_msg = $"Failed to copyEntityAttributeFromAccountBaseDoc() !!! : {accountBaseDoc.toBasicString()}"; result.setFail(ServerErrorCode.DynamoDbDocCopyToEntityAttributeFailed, err_msg); Log.getLogger().error(err_msg); return (result, null); } (result, var user_doc_base) = await user_attribute.toDocBase(); if (result.isFail()) { err_msg = $"Failed to toDocBase() !!! : {result.toBasicString()} - {user_attribute.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } user_base_doc_nullable = user_doc_base as UserBaseDoc; NullReferenceCheckHelper.throwIfNull(user_base_doc_nullable, () => $"user_base_doc_nullable is null - {userBase.toBasicString()}"); } catch (Exception e) { err_msg = $"Failed to fillupUserBaseDocBy() !!! : Exception:{e} - userGuid:{user_attribute.UserGuid}, accountId:{account_id}"; result.setFail(ServerErrorCode.DotNetException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, user_base_doc_nullable); } public static async Task<(Result, CharacterBaseDoc?)> fillupCharacterBaseDocByTestUserCreateData(UserAttribute userAttribute, CharacterAttribute characterAttribute) { ArgumentNullReferenceCheckHelper.throwIfNull(userAttribute, () => $"userAttribute is null !!!"); var owner = userAttribute.getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(characterAttribute, () => $"characterAttribute is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var account_id = userAttribute.AccountId; var user_guid = userAttribute.UserGuid; var new_character_base_doc = new CharacterBaseDoc(); try { // 1. TestUserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. (result, var found_test_user_create_data) = await getTestUserCreateDataByAccountId(account_id); if (result.isFail()) { err_msg = $"Not found TestUserCreateData for Character Create !!! : {result.toBasicString()} - accountId:{account_id}"; Log.getLogger().error(err_msg); return (result, null); } NullReferenceCheckHelper.throwIfNull(found_test_user_create_data, () => $"found_test_user_create_data is null !!! - {owner.toBasicString()}"); // 2. CharacterBaseDoc 를 구성 한다. if (false == characterAttribute.copyEntityAttributeFromMeta(found_test_user_create_data)) { err_msg = $"Failed to copyEntityAttributeFromMeta() !!!, to:{characterAttribute.getTypeName()}, from:{found_test_user_create_data.getTypeName()}"; result.setFail(ServerErrorCode.MetaDataCopyToEntityAttributeFailed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } characterAttribute.newEntityAttribute(); (result, var character_doc) = await characterAttribute.toDocBase(); if (result.isFail()) { return (result, null); } new_character_base_doc = character_doc as CharacterBaseDoc; NullReferenceCheckHelper.throwIfNull(new_character_base_doc, () => $"new_character_base_doc is null !!! - {owner.toBasicString()}"); } catch (Exception e) { err_msg = $"Failed to fillupCharacterBaseDocByTestUserCreateData() !!! : Exception:{e} - accountId:{account_id}, userGuid:{user_guid}"; result.setFail(ServerErrorCode.DotNetException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, new_character_base_doc); } public static async Task<(Result, CharacterBaseDoc?)> fillupCharacterBaseDocByUserCreateData( META_ID userCreateMetaId , UserAttribute userAttribute, CharacterAttribute characterAttribute) { ArgumentNullReferenceCheckHelper.throwIfNull(userAttribute, () => $"userAttribute is null !!!"); var owner = userAttribute.getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); ArgumentNullReferenceCheckHelper.throwIfNull(characterAttribute, () => $"characterAttribute is null !!! - {owner.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var account_id = userAttribute.AccountId; var user_guid = userAttribute.UserGuid; var new_character_base_doc = new CharacterBaseDoc(); try { // 1. UserCreateData Meta 파일에서 특정 계정 정보가 존재하는 지 체크 한다. (result, var found_user_create_data) = await getUserCreateDataByAccountId(userCreateMetaId, account_id); if (result.isFail()) { err_msg = $"Not found serCreateData for Character Create !!! : {result.toBasicString()} - accountId:{account_id}"; Log.getLogger().error(err_msg); return (result, null); } NullReferenceCheckHelper.throwIfNull(found_user_create_data, () => $"found_user_create_data is null !!! - {owner.toBasicString()}"); // 2. CharacterBaseDoc 를 구성 한다. if (false == characterAttribute.copyEntityAttributeFromMeta(found_user_create_data)) { err_msg = $"Failed to copyEntityAttributeFromMeta() !!!, to:{characterAttribute.getTypeName()}, from:{found_user_create_data.getTypeName()}"; result.setFail(ServerErrorCode.MetaDataCopyToEntityAttributeFailed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } characterAttribute.newEntityAttribute(); (result, var character_doc) = await characterAttribute.toDocBase(); if (result.isFail()) { return (result, null); } new_character_base_doc = character_doc as CharacterBaseDoc; NullReferenceCheckHelper.throwIfNull(new_character_base_doc, () => $"new_character_base_doc is null !!! - {owner.toBasicString()}"); } catch (Exception e) { err_msg = $"Failed to fillupCharacterBaseDocByUserCreateData() !!! : Exception:{e} - accountId:{account_id}, userGuid:{user_guid}"; result.setFail(ServerErrorCode.DotNetException, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, new_character_base_doc); } public static ClothInfo toClothInfo(this MetaAssets.TestUserCreateMetaData testUserCreateData, string userId) { var err_msg = string.Empty; var cloth_info = new ClothInfo(); if (testUserCreateData.WearingItems == null) { return cloth_info; } foreach (var item_meta_id in testUserCreateData.WearingItems) { MetaData.Instance._ItemTable.TryGetValue(item_meta_id, out var found_item_meta); if (null == found_item_meta) { err_msg = $"Not found Cloth Item in ItemData !!! : ItemMetaId:{item_meta_id} - userId:{userId}"; Log.getLogger().error(err_msg); continue; } var meta_id = (uint)found_item_meta.ItemId; var error_code = found_item_meta.fillupClothInfo(ref cloth_info, userId); if (ServerErrorCode.Success != error_code) { err_msg = $"Failed to fillup ClothInfo !!! : errorCode:{error_code}, ItemMetaId:{item_meta_id} - TestUserCreateMetaId:{testUserCreateData.MetaId}, userId:{userId}"; Log.getLogger().error(err_msg); continue; } } return cloth_info; } public static ServerErrorCode fillupClothInfo(this MetaAssets.ItemMetaData metaData, ref ClothInfo clothInfo, string userId) { ArgumentNullReferenceCheckHelper.throwIfNull(clothInfo, () => $"clothInfo is null !!!"); var err_msg = string.Empty; if (null == clothInfo) { err_msg = $"Function param is null, ClothInfo !!! : ItemMetaId:{metaData.ItemId} - userId:{userId}"; Log.getLogger().error(err_msg); return ServerErrorCode.FunctionParamNull; } var meta_id = (uint)metaData.ItemId; if (false == metaData.TypeLarge.isClothType()) { err_msg = $"Not EItemLargeType of Cloth !!! : EItemLargeType.Cloth == {metaData.TypeLarge}, ItemMetaId:{meta_id} - userId:{userId}"; Log.getLogger().error(err_msg); return ServerErrorCode.ItemClothInvalidLargeType; } if (false == metaData.TypeSmall.isClothType()) { err_msg = $"Not EItemSmallType of Cloth !!! : EItemSmallType:{metaData.TypeSmall}, ItemMetaId:{meta_id} - userId:{userId}"; Log.getLogger().error(err_msg); return ServerErrorCode.ItemClothInvalidSmallType; } clothInfo.setClothInfo(metaData, userId); return ServerErrorCode.Success; } public static bool isClothType(this MetaAssets.ItemMetaData itemMeta) { if (true == itemMeta.TypeLarge.isClothType() && true == itemMeta.TypeSmall.isClothType()) { return true; } return false; } public static bool isClothType(this MetaAssets.EItemLargeType itemLargeType) { if (MetaAssets.EItemLargeType.CLOTH == itemLargeType) { return true; } return false; } public static bool isClothType(this MetaAssets.EItemSmallType itemSmallType) { switch (itemSmallType) { case MetaAssets.EItemSmallType.CAP: case MetaAssets.EItemSmallType.MASK: case MetaAssets.EItemSmallType.GLASSES: case MetaAssets.EItemSmallType.EARRING: case MetaAssets.EItemSmallType.NECKLACE: case MetaAssets.EItemSmallType.SHIRT: case MetaAssets.EItemSmallType.DRESS: case MetaAssets.EItemSmallType.PANTS: case MetaAssets.EItemSmallType.SOCKS: case MetaAssets.EItemSmallType.ANKLET: case MetaAssets.EItemSmallType.GLOVES: case MetaAssets.EItemSmallType.BRACELET: case MetaAssets.EItemSmallType.RING: case MetaAssets.EItemSmallType.SHOES: case MetaAssets.EItemSmallType.OUTER: case MetaAssets.EItemSmallType.BACKPACK: case MetaAssets.EItemSmallType.BAG: case MetaAssets.EItemSmallType.SHOULDERBAG: break; default: return false; } return true; } public static bool setClothInfo(this ClothInfo clothInfo, MetaAssets.ItemMetaData itemMeta, string userId) { ArgumentNullReferenceCheckHelper.throwIfNull(itemMeta, () => $"itemMeta is null !!!"); var meta_id = (uint)itemMeta.ItemId; var err_msg = string.Empty; switch (itemMeta.TypeSmall) { case MetaAssets.EItemSmallType.CAP: clothInfo.ClothHeadwear = meta_id; break; case MetaAssets.EItemSmallType.MASK: case MetaAssets.EItemSmallType.GLASSES: clothInfo.ClothMask = meta_id; break; case MetaAssets.EItemSmallType.EARRING: clothInfo.ClothEarrings = meta_id; break; case MetaAssets.EItemSmallType.NECKLACE: clothInfo.ClothNeckless = meta_id; break; case MetaAssets.EItemSmallType.SHIRT: case MetaAssets.EItemSmallType.DRESS: clothInfo.ClothTops = meta_id; break; case MetaAssets.EItemSmallType.PANTS: clothInfo.ClothBottoms = meta_id; break; case MetaAssets.EItemSmallType.SOCKS: case MetaAssets.EItemSmallType.ANKLET: clothInfo.ClothSocks = meta_id; break; case MetaAssets.EItemSmallType.GLOVES: case MetaAssets.EItemSmallType.BRACELET: case MetaAssets.EItemSmallType.RING: clothInfo.ClothGloves = meta_id; break; case MetaAssets.EItemSmallType.SHOES: clothInfo.ClothShoes = meta_id; break; case MetaAssets.EItemSmallType.OUTER: clothInfo.ClothOuter = meta_id; break; case MetaAssets.EItemSmallType.BACKPACK: case MetaAssets.EItemSmallType.BAG: case MetaAssets.EItemSmallType.SHOULDERBAG: clothInfo.ClothBag = meta_id; break; default: err_msg = $"No EItemSmallType in ClothInfo !!! : EItemSmallType:{itemMeta.TypeSmall}, ItemMetaId:{meta_id} - userId:{userId}"; Log.getLogger().warn(err_msg); return false; } return true; } public static bool isTattooType(this MetaAssets.ItemMetaData itemMeta) { if (true == itemMeta.TypeLarge.isTattooType()) { return true; } return false; } public static bool isTattooType(this MetaAssets.EItemLargeType itemLargeType) { if (itemLargeType == MetaAssets.EItemLargeType.TATTOO) { return true; } return false; } public static CurrencyType toCurrencyType(this EntityDeltaType deltaType) { return deltaType switch { EntityDeltaType.MoneyGold => CurrencyType.Gold, EntityDeltaType.MoneySaphire => CurrencyType.Sapphire, EntityDeltaType.MoneyCalium => CurrencyType.Calium, EntityDeltaType.MoneyBeam => CurrencyType.Beam, EntityDeltaType.MoneyRuby => CurrencyType.Ruby, _ => CurrencyType.None }; } public static CurrencyType intToCurrencyType(int currencyType) { return currencyType switch { 1 => CurrencyType.Gold, 2 => CurrencyType.Sapphire, 3 => CurrencyType.Calium, 4 => CurrencyType.Beam, 5 => CurrencyType.Ruby, _ => CurrencyType.None }; } public static bool isBalanceWithinMaxCurrency(this CurrencyType currencyType, double currencyValue) { var err_msg = string.Empty; // 해당 CurrencyType 으로 등록되어 있지 않아도 실패 처리 한다. !!! if (MetaData.Instance._CurrencyMetaTableByCurrencyType.TryGetValue(currencyType, out var currencyMetaData) == false) { err_msg = $"Not found CurrencyMetaData !!! : currencyType:{currencyType}"; Log.getLogger().error(err_msg); return false; } // 제한 크기를 초과하면 실패 처리 한다. !!! if(currencyMetaData.MaxCount < currencyValue) { return false; } return true; } public static List toMailItems(List gachaMetaDatas) { ArgumentNullReferenceCheckHelper.throwIfNull(gachaMetaDatas, () => $"gachaMetaDatas is null !!!"); var mail_items = new List(); foreach(var gachaMetaData in gachaMetaDatas) { NullReferenceCheckHelper.throwIfNull(gachaMetaData, () => $"gachaMetaData is null !!!"); NullReferenceCheckHelper.throwIfNull(gachaMetaData.Reward, () => $"gachaMetaData.Reward is null !!!"); if (gachaMetaData.Reward.Item is MetaAssets.ItemReward reward_item) { var mail_item = new MailItem(); mail_item.ItemId = (META_ID)reward_item.Id; mail_item.Count = reward_item.Count; mail_items.Add(mail_item); } else { object? reward_type = null; if (gachaMetaData.Reward.Currency is MetaAssets.CurrencyReward reward_currency) { reward_type = reward_currency; } Log.getLogger().error($"Not supported RewardType in Mail !!! : {reward_type?.getTypeName() ?? string.Empty} - gachaMetaId:{gachaMetaData.Id}"); } } return mail_items; } }