초기커밋

This commit is contained in:
2025-05-01 07:20:41 +09:00
commit 98bb2e3c5c
2747 changed files with 646947 additions and 0 deletions

View File

@@ -0,0 +1,663 @@
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<AccountAttribute>();
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<AccountBaseAttrib>();
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<AccountAttribute>();
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<AccountBaseAttrib>();
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<MetaAssets.IMetaData>() { 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<AccountAttribute>();
NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {userBase.toBasicString()}");
var account_id = account_attribute.AccountId;
var user_attribute = owner.getEntityAttribute<UserAttribute>();
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<MailItem> toMailItems(List<MetaAssets.GachaMetaData> gachaMetaDatas)
{
ArgumentNullReferenceCheckHelper.throwIfNull(gachaMetaDatas, () => $"gachaMetaDatas is null !!!");
var mail_items = new List<MailItem>();
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;
}
}