초기커밋

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,40 @@
using Amazon.S3.Model;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace ServerCommon;
public static class BeaconHelper
{
public static async Task<(Result, string)> getBeaconAppProfileUploadPresignedUrl( this S3ConnectorBase s3connector
, string bucketName
, string beaconAppProfileS3Key )
{
var result = new Result();
var err_msg = string.Empty;
(var is_success, var presigned_url) = await s3connector.getPresignedUrl( bucketName
, beaconAppProfileS3Key
, Amazon.S3.HttpVerb.PUT
, DateTime.UtcNow.AddHours(1) );
if ( false == is_success
|| presigned_url.isNullOrWhiteSpace() )
{
err_msg = $"Failed to getPresignedUrl() !!!";
result.setFail(ServerErrorCode.S3FileGetFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, presigned_url);
}
return (result, presigned_url);
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServerBase;
namespace ServerCommon;
//===================================================================================
// LogAction + LogActionType 함께 생성해주는 클래스 이다.
//===================================================================================
public partial class LogActionEx : LogAction
{
public LogActionEx(LogActionType logActionType)
:base(logActionType.ToString())
{
}
public LogActionEx(LogActionType logActionType, Guid tranId)
: base(logActionType.ToString(), tranId)
{
}
}//LogAction
//===================================================================================
// ILogInvoker + LogDomainType 함께 생성해주는 클래스 이다.
//===================================================================================
public abstract partial class ILogInvokerEx : ILogInvoker
{
public ILogInvokerEx(LogDomainType logDomainType)
: base(logDomainType.ToString())
{
}
public ILogInvokerEx(LogDomainType logDomainType, LogAction logAction)
: base(logDomainType.ToString(), logAction)
{
}
}
//==============================================================================================
// QueryBatch + LogActionType 함께 생성해주는 클래스 이다.
//==============================================================================================
public class QueryBatchEx<TQueryRunner> : QueryBatch<TQueryRunner>
where TQueryRunner : IQueryRunner, new()
{
public QueryBatchEx( IWithLogActor logActor
, LogActionType logActionType
, DynamoDbClient dbClient
, bool isUseTransact = true, string transId = ""
, UInt16 retryCount = 3, UInt16 retryDelayMSec = 1000, string eventTid = "" )
: base( logActor, logActionType.ToString(), dbClient
, isUseTransact, transId, retryCount, retryDelayMSec )
{
getQueryRunner().setQueryBatch(this);
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using ServerCore; using ServerBase;
namespace ServerCommon;
public static class ClientMessageHelper
{
public static global::Item toItemData4Client(this ItemBase item)
{
var parent = item.getRootParent();
NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!");
var item_attribute = item.getEntityAttribute<ItemAttributeBase>();
NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}");
return item_attribute.toItemData4Client();
}
public static global::Item toItemData4Client(this ItemAttributeBase itemAttributeBase)
{
var owner = itemAttributeBase.getOwner();
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
var parent = owner.getRootParent();
NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!! - {owner.toBasicString()}");
var item_4_client = new global::Item();
item_4_client.ItemGuid = itemAttributeBase.ItemGuid;
item_4_client.ItemId = (Int32)itemAttributeBase.ItemMetaId;
item_4_client.Count = itemAttributeBase.ItemStackCount;
item_4_client.Slot = itemAttributeBase.EquipedPos;
item_4_client.Level = itemAttributeBase.Level;
item_4_client.Attributeids.AddRange(itemAttributeBase.Attributes.Select<UInt16, Int32>(x => x).ToList());
(var result, DynamoDbDocBase? doc_base) = itemAttributeBase.toDocBase(false).GetAwaiter().GetResult();
if(result.isFail())
{
var err_msg = $"Failed to toDocBase() !!!, in toItemData4Client() : {result.toBasicString()} - {owner.toBasicString()}, {parent.toBasicString()}";
Log.getLogger().error(err_msg);
return item_4_client;
}
NullReferenceCheckHelper.throwIfNull(doc_base, () => $"doc_base is null !!! - {itemAttributeBase.toBasicString()}");
item_4_client.CreateTime = doc_base.getCreatedDateTime().ProcessedTime.ToTimestamp();
item_4_client.CreateTime = doc_base.getUpdatedDateTime().ProcessedTime.ToTimestamp();
return item_4_client;
}
}

View File

@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServerCore;
using ServerBase;
namespace ServerCommon;
public static class CommonResultHelper
{
public static string toBasicString(this global::Item item)
{
var data = string.Empty;
data += $"itemGuid:{item.ItemGuid}, itemMetaId:{item.ItemId}, stackCount:{item.Count}";
return data;
}
public static string toBasicString(this global::LevelExp levelExp)
{
var data = string.Empty;
data += $"Level:{levelExp.Level}, ExpInLevel:{levelExp.ExpInLevel}, ExpInTotal:{levelExp.ExpInTotal}";
return data;
}
public static string toBasicString(this global::EntityCommonResult entityCommonResult)
{
var data = string.Empty;
data = $"{entityCommonResult.Money.toBasicString()}"
+ $"{entityCommonResult.Item.toBasicString()}"
+ $"{entityCommonResult.Exp.toBasicString()}";
return data;
}
public static string toBasicString(this global::MoneyDeltaAmount moneyDelta)
{
var data = string.Empty;
data += $"deltaType:{moneyDelta.DeltaType}, amount:{moneyDelta.Amount}";
return data;
}
public static string toBasicString(this global::MoneyResult moneyResult)
{
var data = string.Empty;
foreach (var each in moneyResult.Moneys)
{
var currency_type = (CurrencyType)each.Key;
var count = each.Value;
data += $"\n";
data += $"money: currencyType:{currency_type}, count:{count}";
}
foreach (var each in moneyResult.Deltas)
{
var currency_type = (CurrencyType)each.Key;
var money_delta = each.Value;
data += $"\n";
data += $"money.delta: currencyType:{currency_type}, {money_delta.toBasicString()}";
}
return data;
}
public static string toBasicString(this global::ItemAmount itemAmount)
{
var data = string.Empty;
data += $"itemMetaId:{itemAmount.MetaId}, itemCount:{itemAmount.Amount}";
return data;
}
public static string toBasicString(this global::ItemDeltaAmount itemDelta)
{
var data = string.Empty;
data += $"deltaType:{itemDelta.DeltaType}, {itemDelta.Delta.toBasicString()}";
return data;
}
public static string toBasicString(this global::ItemResult itemResult)
{
var data = string.Empty;
foreach (var each in itemResult.UpdatedItems)
{
var item_guid = each.Key;
var item = each.Value;
data += $"\n";
data += $"item.Updated: {item.toBasicString()}";
}
foreach (var each in itemResult.NewItems)
{
var item_guid = each.Key;
var item_delta = each.Value;
data += $"\n";
data += $"item.New: {item_delta.toBasicString()}";
}
foreach (var each in itemResult.DeltaPerMeta)
{
var item_meta_id = each.Key;
var item_delta = each.Value;
data += $"\n";
data += $"item.DeltaPerMeta: {item_delta.toBasicString()}";
}
foreach (var each in itemResult.DeltaPerItems)
{
var item_guid = each.Key;
var deleta_count = each.Value;
data += $"\n";
data += $"item.DeltaPerGuid: itemGuid:{item_guid}, deltaCount:{deleta_count}";
}
return data;
}
public static string toBasicString(this global::LevelExpDeltaAmount levelExpDelta)
{
var data = string.Empty;
data += $"expDeltaType:{levelExpDelta.ExpDeltaType}, expDeltaAmount:{levelExpDelta.ExpAmount}, levelDeltaAmount:{levelExpDelta.LevelAmount}";
return data;
}
public static string toBasicString(this global::ExpResult expResult)
{
var data = string.Empty;
foreach (var each in expResult.LevelExps)
{
var level_exp_type = (LevelExpType)each.Key;
var level_exp = each.Value;
data += $"\n";
data += $"exp: {level_exp.toBasicString()}";
}
foreach (var each in expResult.LevelExpDeltas)
{
var level_exp_type = (LevelExpType)each.Key;
var level_exp_delta = each.Value;
data += $"\n";
data += $":exp.Delta: {level_exp_delta.toBasicString()}";
}
return data;
}
}

View File

@@ -0,0 +1,43 @@
using Microsoft.Extensions.Configuration;
using ServerCore;
using WORLD_META_ID = System.UInt32;
using CHANNEL_NO = System.UInt32;
namespace ServerCommon;
public static class ConfigHelper
{
public static WORLD_META_ID getWorldId(this IConfiguration config)
{
return uint.Parse(config["worldId"] ?? "0");
}
public static ushort getPort(this IConfiguration config)
{
return ushort.Parse(config["port"] ?? "0");
}
public static CHANNEL_NO getChannelNo(this IConfiguration config)
{
return uint.Parse(config["channelNo"] ?? "0");
}
public static ServerType getServerType(this IConfiguration config)
{
var value = config["config"];
if (Enum.TryParse<ServerType>(value, true, out var serverType))
{
return serverType;
}
throw new Exception($"Invalid or missing ServerType in IConfiguration:{value}");
}
}

View File

@@ -0,0 +1,245 @@
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.DynamoDBv2.Model;
using Amazon.S3.Model;
using ServerCore;
using ServerBase;
using DYNAMO_DB_TABLE_NAME = System.String;
using DYNAMO_DB_TABLE_FULL_NAME = System.String;
using USER_GUID = System.String;
namespace ServerCommon;
public static class CurrencyControlHelper
{
private static DynamoDbClient? m_db_connector = null;
//=============================================================================================
// 단위당 금전량을 quantity 만큼 계산하여 Round 보정후 반환 한다.
//=============================================================================================
public static double calculateRoundedMoneyByCurrency(this double reqUnitMoney, CurrencyType currencyType, int quantity = 1)
{
var result = new Result();
if (ServerCore.TypeHelper.NumericSignType.Positive == ServerCore.TypeHelper.checkNumericSignType<double>(reqUnitMoney))
{
reqUnitMoney *= -1;
}
var req_money = reqUnitMoney * quantity;
return roundMoneyByCurrencyType(currencyType, req_money);
}
//=============================================================================================
// 금전량을 보정 한다.
//=============================================================================================
public static double roundMoneyByCurrencyType(CurrencyType currencyType, double delta)
{
// CurrencyType.Calium 이 아닌 모든 재화는 소숫점을 올림 처리 한다 !!!
if (currencyType != CurrencyType.Calium)
{
delta = MathHelper.roundUpOrDown(delta);
}
return delta;
}
//=============================================================================================
// delta 만큼 금전을 쓴다.
//=============================================================================================
public static async Task<(Result, double)> spendMoneyByUserGuid( USER_GUID userGuid, CurrencyType currencyType, double delta )
{
var result = new Result();
if (ServerCore.TypeHelper.NumericSignType.Positive == ServerCore.TypeHelper.checkNumericSignType<double>(delta))
{
delta *= -1;
}
return await changeMoneyByUserGuid(userGuid, currencyType, delta);
}
//=============================================================================================
// delta 만큼 금전을 얻는다.
//=============================================================================================
public static async Task<(Result, double)> earnMoneyByUserGuid(USER_GUID userGuid, CurrencyType currencyType, double delta)
{
var result = new Result();
if (ServerCore.TypeHelper.NumericSignType.Negative == ServerCore.TypeHelper.checkNumericSignType<double>(delta))
{
delta *= -1;
}
if (0 < delta)
{
return await changeMoneyByUserGuid(userGuid, currencyType, delta);
}
return (result, 0);
}
public static async Task<(Result, double)> getMoneyByUserGuid(USER_GUID userGuid, CurrencyType type)
{
var result = new Result();
string err_msg;
var error_code = hasDbConnector();
if (error_code.isFail())
{
err_msg = $"Not available DynamoDB connector !!! : userGuid:{userGuid}";
result.setFail(error_code, err_msg);
return (result, 0);
}
var db_connector = getDbConnector();
(result, var found_money_doc) = await findMoneyDocByUserGuid(db_connector, userGuid);
if (null == found_money_doc)
{
err_msg = $"Not found MoneyDoc !!! : userGuid:{userGuid}";
result.setFail(ServerErrorCode.MoneyDocIsNull, err_msg);
return (result, 0);
}
var attrib = found_money_doc.getAttrib<MoneyAttrib>();
NullReferenceCheckHelper.throwIfNull(attrib, () => $"attrib is null !!!");
return (result, attrib.getCurrencyFromType(type));
}
public static async Task<(Result, double)> changeMoneyByUserGuid(USER_GUID userGuid, CurrencyType type, double delta)
{
var result = new Result();
var err_msg = string.Empty;
var error_code = hasDbConnector();
if (error_code.isFail())
{
err_msg = $"Not available DynamoDB connector !!! : userGuid:{userGuid}";
result.setFail(error_code, err_msg);
return (result, 0);
}
var db_connector = getDbConnector();
var table = db_connector.getTableByDoc<MoneyDoc>();
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<MoneyDoc>(userGuid);
if (result.isFail())
{
return (result, 0);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - userGuid:{userGuid}");
(result, var request) = makeUpdateItemRequest( table.TableName
, make_primary_key.toKeyWithAttributeValue(), MoneyAttribExtensions.getKeyNameFromType(type)
, delta);
if (result.isFail() || request == null) return (result, 0);
(result, var changed_money_doc) = await db_connector.simpleQueryDocTypesWithUpdateItemRequest<MoneyDoc>(request);
if (result.isFail() || changed_money_doc == null) return (result, 0);
var money_attrib = changed_money_doc.getAttrib<MoneyAttrib>();
NullReferenceCheckHelper.throwIfNull(money_attrib, () => $"money_attrib is null !!! - userGuid:{userGuid}");
return (result, money_attrib.getCurrencyFromType(type));
}
public static async Task<(Result, MoneyDoc?)> findMoneyDocByUserGuid(DynamoDbClient dynamoDbClient, USER_GUID userGuid)
{
var result = new Result();
var money_doc = new MoneyDoc();
money_doc.setCombinationKeyForPK(userGuid);
money_doc.onApplyPKSK();
var query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(money_doc.getPK());
return await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<MoneyDoc>(query_config);
}
private static (Result, UpdateItemRequest?) makeUpdateItemRequest( DYNAMO_DB_TABLE_FULL_NAME tableFullName
, Dictionary<string, AttributeValue> attributeValueWithPrimaryKey
, string targetAttribName
, double deltaCount )
{
var result = new Result();
var err_msg = string.Empty;
var error_code = hasDbConnector();
if (error_code.isFail())
{
err_msg = $"Not available DynamoDb connector !!!";
result.setFail(error_code, err_msg);
return (result, null);
}
var query_builder = new DynamoDbItemRequestHelper.UpdateItemRequestBuilder(tableFullName);
query_builder.withKeys(attributeValueWithPrimaryKey);
var target_doc = new MoneyDoc();
var attrib_path_json_string = target_doc.toJsonStringOfAttribs();
var target_key = JsonHelper.getJsonPropertyName<MoneyAttrib>(targetAttribName);
(var is_success, var attribute_expression) = DynamoDbClientHelper.toAttributeExpressionFromJson(attrib_path_json_string, target_key);
if (false == is_success)
{
err_msg = $"Failed to DynamoDbClientHelper.toAttributeExpressionFromJson() !!! : attribPath:{attrib_path_json_string}, targetKey:{target_key}";
result.setFail(ServerErrorCode.AttribPathMakeFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
query_builder.withExpressionAttributeNames(DynamoDbClientHelper.toExpressionAttributeNamesFromJson(attrib_path_json_string, target_key));
var update_expression = (deltaCount >= 0)
? $"SET {attribute_expression} = if_not_exists({attribute_expression}, :start) + :changeValue"
: $"SET {attribute_expression} = if_not_exists({attribute_expression}, :start) - :changeValue";
query_builder.withUpdateExpression(update_expression);
var expression_attribute_values = new Dictionary<string, AttributeValue>
{
{ ":changeValue", new AttributeValue { N = Math.Abs(deltaCount).ToString() } },
{ ":start", new AttributeValue { N = "0" } }
};
query_builder.withExpressionAttributeValues(expression_attribute_values);
query_builder.withReturnValues(ReturnValue.ALL_NEW);
return query_builder.build();
}
private static ServerErrorCode hasDbConnector()
{
if(null == m_db_connector)
{
return ServerErrorCode.DynamoDbConnectorIsNull;
}
return ServerErrorCode.Success;
}
public static DynamoDbClient getDbConnector()
{
NullReferenceCheckHelper.throwIfNull(m_db_connector, () => $"m_db_connector is null !!!");
return m_db_connector;
}
//=============================================================================================
// CurrencyControlHelper <= DynamoDbClient 설정
//=============================================================================================
public static Result setDbConnector(DynamoDbClient dbConnector)
{
var result = new Result();
m_db_connector = dbConnector;
return result;
}
}

View File

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServerCore;
using ServerBase;
using MetaAssets;
namespace ServerCommon;
public static class DataCopyHelper
{
#region : Meta => Doc
public static async Task<Result> copyDocFromMetas<TDoc>(TDoc toDoc, List<MetaAssets.IMetaData> fromMetaDatas)
where TDoc : DynamoDbDocBase
{
var result = new Result();
var err_msg = string.Empty;
var to_doc = toDoc as ICopyDocFromMeta;
if (null == to_doc)
{
err_msg = $"Failed to cast ICopyDocFromMeta !!! : {typeof(TDoc).Name}";
result.setFail(ServerErrorCode.ClassDoesNotImplementInterfaceInheritance, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
foreach (var meta_data in fromMetaDatas)
{
if (false == to_doc.copyDocFromMeta(meta_data))
{
err_msg = $"Failed to copyDocFromMeta() !!!, to:{typeof(TDoc).Name}, from:{meta_data.getTypeName()}";
result.setFail(ServerErrorCode.MetaDataCopyToDynamoDbDocFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
}
return await Task.FromResult(result);
}
#endregion
#region : Meta => EntityAttribute
public static async Task<Result> copyEntityAttributeFromMetas<TEntityAttributeType>(TEntityAttributeType toDocBase, List<MetaAssets.IMetaData> metaDatas)
where TEntityAttributeType : EntityAttributeBase
{
var result = new Result();
var err_msg = string.Empty;
var to_copy = toDocBase as ICopyEntityAttributeFromMeta;
if (null == to_copy)
{
err_msg = $"Failed to cast ICopyEntityAttributeFromMeta !!! : {typeof(TEntityAttributeType).Name} !!!";
result.setFail(ServerErrorCode.ClassDoesNotImplementInterfaceInheritance, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
foreach (var meta_data in metaDatas)
{
if (false == to_copy.copyEntityAttributeFromMeta(meta_data))
{
err_msg = $"Failed to copyEntityAttributeFromMeta() !!!, to:{typeof(TEntityAttributeType).Name}, from:{meta_data.getTypeName()}";
result.setFail(ServerErrorCode.MetaDataCopyToEntityAttributeFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
}
return await Task.FromResult(result);
}
#endregion
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCommon;
public static class EffectHelper
{
public static FarmingStateType toFarmingStateType( this PropState propState )
{
switch(propState)
{
case PropState.Activation:
return FarmingStateType.StandBy;
case PropState.Progress:
return FarmingStateType.Progress;
case PropState.Respawning:
return FarmingStateType.CoolingTime;
}
return FarmingStateType.None;
}
public static PropState toPropState(this FarmingStateType farmingStateType)
{
switch (farmingStateType)
{
case FarmingStateType.StandBy:
return PropState.Activation;
case FarmingStateType.Progress:
return PropState.Progress;
case FarmingStateType.CoolingTime:
return PropState.Respawning;
}
return PropState.None;
}
}

View File

@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using Newtonsoft.Json;
using ServerCore;
using ServerBase;
using ServerControlCenter;
using USER_GUID = System.String;
using MASTER_GUID = System.String;
using MASTER_NICKNAME = System.String;
using Microsoft.AspNetCore.Http;
using Amazon.S3.Model;
namespace ServerCommon;
public static class EntityHelper
{
public class MasterProfile
{
public MASTER_GUID MasterGuid { get; set; } = string.Empty;
public MASTER_NICKNAME MasterNickname { get; set; } = string.Empty;
}
public static (Result, MasterProfile?) getMasterProfile<TMasterEntity>(EntityBase owner)
where TMasterEntity : EntityBase
{
var result = new Result();
var err_msg = string.Empty;
var master_entity = owner as TMasterEntity;
if (null == master_entity)
{
if (true == owner.hasMasterGuid())
{
master_entity = owner.onGetMasterEntity() as TMasterEntity;
if (null == master_entity)
{
err_msg = $"Not related Master !!! : TMasterEntity:{nameof(TMasterEntity)} - {owner.toBasicString()}";
result.setFail(ServerErrorCode.MasterNotFound, err_msg);
Log.getLogger().error(err_msg);
return (result, null);
}
}
else
{
err_msg = $"Not related Master !!! : TMasterEntity:{nameof(TMasterEntity)} - {owner.toBasicString()}";
result.setFail(ServerErrorCode.MasterNotRelated, err_msg);
Log.getLogger().error(err_msg);
return (result, null);
}
}
var entity_profile = new MasterProfile();
entity_profile.MasterGuid = (master_entity as dynamic).getUserGuid(); // 나중에 가상함수로 수정 하자. - kangms
entity_profile.MasterNickname = (master_entity as dynamic).getUserNickname(); // 나중에 가상함수로 수정 하자. - kangms
return (result, entity_profile);
}
public static OwnerEntityType toOwnerEntityType(this EntityType entityType)
{
if (EntityType.Player == entityType)
{
return OwnerEntityType.User;
}
else if (EntityType.Character == entityType)
{
return OwnerEntityType.Character;
}
else if (EntityType.UgcNpc == entityType
|| EntityType.Beacon == entityType)
{
return OwnerEntityType.UgcNpc;
}
else if (EntityType.MyHome == entityType)
{
return OwnerEntityType.Myhome;
}
var err_msg = $"Invalid EntityType !!! : EntityType:{entityType}";
Log.getLogger().error(err_msg);
return OwnerEntityType.None;
}
public static async Task<(Result, bool)> isUserLoggedIn(DynamoDbClient dynamoDbClient, USER_GUID userGuid)
{
var result = new Result();
// 1. UserBaseDoc 조회
(result, var user_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<UserBaseDoc>(userGuid);
if (result.isFail())
{
return (result, false);
}
ArgumentNullException.ThrowIfNull(user_primary_key, $"user_primary_key is null !!! - userGuid:{userGuid}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(user_primary_key.PK);
(result, var user_base_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<UserBaseDoc>(query_config);
if (result.isFail())
{
return (result, false);
}
NullReferenceCheckHelper.throwIfNull(user_base_doc, () => $"user_base_doc is null !!! : userGuid:{userGuid}");
// 2. LoggedIn 체크
var user_base_attrib = user_base_doc.getAttrib<UserBaseAttrib>();
NullReferenceCheckHelper.throwIfNull(user_base_attrib, () => $"user_base_attrib is null !!!");
return (result, (user_base_attrib.GameLoginDateTime > user_base_attrib.GameLogoutDateTime));
}
public static async Task<(Result, List<ItemDoc>?)> findUserItemsWithCondition( DynamoDbClient dynamoDbClient, USER_GUID userGuid
, Func<ItemDoc, bool>? filterCondition )
{
var result = new Result();
// 1. ItemDoc 조회
(result, var user_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<ItemDoc>(userGuid);
if (result.isFail())
{
return (result, null);
}
ArgumentNullException.ThrowIfNull(user_primary_key, $"user_primary_key is null !!! - userGuid:{userGuid}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(user_primary_key.PK);
(result, var found_item_docs) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig<ItemDoc>(query_config);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_item_docs, () => $"found_item_docs is null !!! : userGuid:{userGuid}");
if (null != filterCondition)
{
// 2. ItemDoc 조건 체크 !!!
var filtered_item_docs = new List<ItemDoc>();
foreach(var item_doc in found_item_docs)
{
var is_success = filterCondition.Invoke(item_doc);
if (is_success.isFalse())
{
continue;
}
filtered_item_docs.Add(item_doc);
}
return (result, filtered_item_docs);
}
return (result, found_item_docs);
}
}

View File

@@ -0,0 +1,297 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServerCore;
using ServerBase;
using SESSION_ID = System.Int32;
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;
using NICKNAME = System.String;
namespace ServerCommon;
public class UserByAccountIdResult
{
public string UserGuid { get; set; } = null!;
public string Nickname { get; set; } = null!;
}
public class UserInfoByUserNickname
{
public USER_GUID UserGuid { get; set; } = string.Empty;
public NICKNAME UserNickname { get; set; } = string.Empty;
}
public class UgcNpcInfoByUgcNpcNickname
{
public UgcNpcAttrib UgcNpcAttrib { get; set; } = null!;
}
public static class EntitySearchHelper
{
public static async Task<(Result, UserByAccountIdResult?)> findUserByAccountId(DynamoDbClient dynamoDbClient, ACCOUNT_ID accountId, string tryUserEmailId)
{
var result = new Result();
var err_msg = string.Empty;
if(true == accountId.isNullOrWhiteSpace())
{
err_msg = $"Invalid AccountId !!! - tryUserEmailId:{tryUserEmailId}";
result.setFail(ServerErrorCode.AccountIdInvalid, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
(result, var found_account_doc) = await AccountBaseDoc.findUserGuidFromAccountId(dynamoDbClient, accountId);
if(result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_account_doc, () => $"found_account_doc is null !!! - AccountId:{accountId}, tryUserEmailId:{tryUserEmailId}");
var doc_account_base_attrib = found_account_doc.getAttrib<AccountBaseAttrib>();
NullReferenceCheckHelper.throwIfNull(doc_account_base_attrib, () => $"doc_account_base_attrib is null !!! - AccountId:{accountId}, tryUserEmailId:{tryUserEmailId}");
var user_guid = doc_account_base_attrib.UserGuid;
if (true == user_guid.isNullOrWhiteSpace())
{
err_msg = $"Account without UserGuid !!! - AccountId:{accountId}, tryUserEmailId:{tryUserEmailId}";
result.setFail(ServerErrorCode.AccountWithoutUserGuid, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
string found_nickname = string.Empty;
(result, var found_nickname_attrib) = await NicknameDoc.findNicknameFromGuid(dynamoDbClient, user_guid);
if(result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_nickname_attrib, () => $"found_nickname_attrib is null !!!, userGuid:{user_guid} - AccountId:{accountId}, tryUserEmailId:{tryUserEmailId}");
found_nickname = found_nickname_attrib.Nickname;
return (result, new UserByAccountIdResult
{
UserGuid = user_guid,
Nickname = found_nickname,
});
}
public static async Task<(Result, List<UgcNpcAttrib>?)> findUgcNpcAttribAllByUserGuid(DynamoDbClient dynamoDbClient, USER_GUID userGuid, string tryUserEmailId)
{
var result = new Result();
var err_msg = string.Empty;
if (true == userGuid.isNullOrWhiteSpace())
{
err_msg = $"Invalid UserGuid !!! - tryUserEmailId:{tryUserEmailId}";
result.setFail(ServerErrorCode.UserGuidInvalid, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<UgcNpcDoc>(userGuid);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - UserGuid:{userGuid}, tryUserEmailId:{tryUserEmailId}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK);
(result, var found_ugc_npc_docs) = await dynamoDbClient.simpleQueryDocTypesWithQueryOperationConfig<UgcNpcDoc>(query_config);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
var found_ugc_npc_attribs = new List<UgcNpcAttrib>();
foreach(var doc in found_ugc_npc_docs)
{
var ugc_npc_attrib = doc.getAttrib<UgcNpcAttrib>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_attrib, () => $"ugc_npc_attrib is null !!! - UserGuid:{userGuid}, tryUserEmailId:{tryUserEmailId}");
found_ugc_npc_attribs.Add(ugc_npc_attrib);
}
return (result, found_ugc_npc_attribs);
}
public static async Task<(Result, UserInfoByUserNickname?)> findUserInfoByUserNickname(DynamoDbClient dynamoDbClient, NICKNAME userNickname)
{
var result = new Result();
var err_msg = string.Empty;
if (true == userNickname.isNullOrWhiteSpace())
{
err_msg = $"Empty UserNickname !!!";
result.setFail(ServerErrorCode.UserNicknameEmpty, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<UserNicknameRegistryDoc>(DynamoDbClient.PK_GLOBAL, userNickname);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - userNickname:{userNickname}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK);
(result, var found_user_nickname_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<UserNicknameRegistryDoc>(query_config);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_user_nickname_doc, () => $"found_user_nickname_doc is null !!! - userNickname:{userNickname}");
var user_nickname_registry_attrib = found_user_nickname_doc.getAttrib<UserNicknameRegistryAttrib>();
NullReferenceCheckHelper.throwIfNull(user_nickname_registry_attrib, () => $"user_nickname_registry_attrib is null !!! - userNickname:{userNickname}");
var searched_result = new UserInfoByUserNickname();
searched_result.UserNickname = user_nickname_registry_attrib.Nickname;
searched_result.UserGuid = user_nickname_registry_attrib.UserGuid;
return (result, searched_result);
}
public static async Task<(Result, UgcNpcInfoByUgcNpcNickname?)> findUgcNpcInfoByUgcNpcNickname( DynamoDbClient dynamoDbClient
, OwnerEntityType ownerEntityType, OWNER_GUID ownerGuid
, NICKNAME ugcNpcNickname )
{
var result = new Result();
var err_msg = string.Empty;
if (true == ugcNpcNickname.isNullOrWhiteSpace())
{
err_msg = $"Empty UgcNpcNickname !!!";
result.setFail(ServerErrorCode.UgcNpcNicknameEmpty, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
UgcNpcNicknameRegistryDoc? found_ugc_nickname_registry_doc = null;
{
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<UgcNpcNicknameRegistryDoc>(ownerGuid, ugcNpcNickname);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK);
(result, found_ugc_nickname_registry_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcNicknameRegistryDoc>(query_config);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_ugc_nickname_registry_doc, () => $"found_ugc_nickname_registry_doc is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
}
var ugc_npc_nickname_registry_attrib = found_ugc_nickname_registry_doc.getAttrib<UgcNpcNicknameRegistryAttrib>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_nickname_registry_attrib, () => $"ugc_npc_nickname_registry_attrib is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
UgcNpcDoc? found_ugc_npc_doc = null;
{
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<UgcNpcDoc>(ownerGuid, ugc_npc_nickname_registry_attrib.UgcNpcMetaGuid);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKSK(make_primary_key.PK, make_primary_key.SK);
(result, found_ugc_npc_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<UgcNpcDoc>(query_config);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_ugc_npc_doc, () => $"found_ugc_npc_doc is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
}
var ugc_npc_attrib = found_ugc_npc_doc.getAttrib<UgcNpcAttrib>();
NullReferenceCheckHelper.throwIfNull(ugc_npc_attrib, () => $"ugc_npc_attrib is null !!! - userGuid:{ownerGuid}, userNickname:{ugcNpcNickname}");
var searched_result = new UgcNpcInfoByUgcNpcNickname();
if(null != ugc_npc_attrib)
{
searched_result.UgcNpcAttrib = ugc_npc_attrib;
}
return (result, searched_result);
}
public static async Task<(Result, MoneyAttrib?)> findUserMoneyByUserGuid(DynamoDbClient dynamoDbClient, USER_GUID userGuid)
{
var result = new Result();
var err_msg = string.Empty;
if (true == userGuid.isNullOrWhiteSpace())
{
err_msg = $"Invalid UserGuid !!!";
result.setFail(ServerErrorCode.UserGuidInvalid, err_msg);
Log.getLogger(result.toBasicString());
return (result, null);
}
(result, var make_primary_key) = await DynamoDBDocBaseHelper.makePrimaryKey<MoneyDoc>(userGuid);
if (result.isFail())
{
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(make_primary_key, () => $"make_primary_key is null !!! - userGuid:{userGuid}");
var query_config = dynamoDbClient.makeQueryConfigForReadByPKOnly(make_primary_key.PK);
(result, var found_money_doc) = await dynamoDbClient.simpleQueryDocTypeWithQueryOperationConfig<MoneyDoc>(query_config);
if (result.isFail())
{
err_msg = $"Failed to simpleQueryDocTypeWithQueryOperationConfig() !!! : {result.toBasicString()}, {make_primary_key.toBasicString()}";
Log.getLogger().error(err_msg);
return (result, null);
}
NullReferenceCheckHelper.throwIfNull(found_money_doc, () => $"found_money_doc is null !!! - userGuid:{userGuid}");
var money_attrib = found_money_doc.getAttrib<MoneyAttrib>();
if(money_attrib == null)
{
err_msg = $"money_attrib is null !!! - UserGuid:{userGuid} !!! {result.toBasicString()}, {make_primary_key.toBasicString()}";
result.setFail(ServerErrorCode.UserMoneyDocEmpty, err_msg);
Log.getLogger().error(err_msg);
return (result, null);
}
return (result, money_attrib);
}
}

View File

@@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using ServerCore;
using ServerBase;
using MetaAssets;
using ServerControlCenter;
using SESSION_ID = System.Int32;
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;
public static class InventoryRuleHelper
{
public static Int16 getBagTapMaxSlotCount()
{
return MetaHelper.GameConfigMeta.MaxItemCategorySlotNum;
}
// 임시로 관련 메타 정보를 반환 한다.
// 추후 인벤토리 메타 정보를 추가하여 처리되도록 개선하자. - kangms
public static Int16 getBagTapMaxSlotCountByBagTabType(EntityType entityType, BagTabType tabType)
{
switch (entityType)
{
case EntityType.MyHome:
return getMyHomeMaxSlotCountByBagTabType(tabType);
default:
return getNormalMaxSlotCountByBagTabType(tabType);
}
}
public static Int16 getNormalMaxSlotCountByBagTabType(BagTabType tabType)
{
switch (tabType)
{
case BagTabType._0:
return MetaHelper.GameConfigMeta.Inventory1Slot;
case BagTabType._1:
return MetaHelper.GameConfigMeta.Inventory3Slot;
case BagTabType._2:
return MetaHelper.GameConfigMeta.Inventory5Slot;
case BagTabType._3:
return MetaHelper.GameConfigMeta.Inventory7Slot;
case BagTabType._4:
return MetaHelper.GameConfigMeta.Inventory9Slot;
case BagTabType._5:
return MetaHelper.GameConfigMeta.Inventory2Slot;
case BagTabType._6:
return MetaHelper.GameConfigMeta.Inventory4Slot;
case BagTabType._7:
return MetaHelper.GameConfigMeta.Inventory6Slot;
case BagTabType._8:
return MetaHelper.GameConfigMeta.Inventory8Slot;
case BagTabType._9:
return MetaHelper.GameConfigMeta.Inventory10Slot;
default:
throw new ArgumentException($"Invalid BagTabType !!!, in getNormalMaxSlotCountByBagTabType() : BagTabType:{tabType}");
}
}
public static Int16 getMyHomeMaxSlotCountByBagTabType(BagTabType tabType)
{
switch (tabType)
{
case BagTabType._0:
return MetaHelper.GameConfigMeta.MyHomeInventory1Slot;
case BagTabType._1:
return MetaHelper.GameConfigMeta.MyHomeInventory3Slot;
case BagTabType._2:
return MetaHelper.GameConfigMeta.MyHomeInventory5Slot;
case BagTabType._3:
return MetaHelper.GameConfigMeta.MyHomeInventory7Slot;
case BagTabType._4:
return MetaHelper.GameConfigMeta.MyHomeInventory9Slot;
case BagTabType._5:
return MetaHelper.GameConfigMeta.MyHomeInventory2Slot;
case BagTabType._6:
return MetaHelper.GameConfigMeta.MyHomeInventory4Slot;
case BagTabType._7:
return MetaHelper.GameConfigMeta.MyHomeInventory6Slot;
case BagTabType._8:
return MetaHelper.GameConfigMeta.MyHomeInventory8Slot;
case BagTabType._9:
return MetaHelper.GameConfigMeta.MyHomeInventory10Slot;
default:
throw new ArgumentException($"Invalid BagTabType !!!, in getMyHomeMaxSlotCountByBagTabType() : BagTabType:{tabType}");
}
}
public static List<ITEM_GUID> findContainDuplicatedItemGuid(List<ITEM_GUID> toCheckItemGuids)
{
return toCheckItemGuids.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
}
public static InvenEquipType toInvenEquipType(this EntityType entityInvenType)
{
switch (entityInvenType)
{
case EntityType.ClothEquipInven:
return InvenEquipType.Cloth;
case EntityType.ToolEquipInven:
return InvenEquipType.Tool;
case EntityType.TattooEquipInven:
return InvenEquipType.Tattoo;
default:
Log.getLogger().error($"Invalid EntityType !!!, can't convert InvenEquipType : EntityType:{entityInvenType}");
return InvenEquipType.None;
}
}
//=========================================================================================
// 해당 가방에 장착 가능한 EItemLargeType 목록을 반환 한다.
//=========================================================================================
public static List<MetaAssets.EItemLargeType> toEquipableItemLargeTypes(this InvenBagType toEquipBagType)
{
switch (toEquipBagType)
{
case InvenBagType.Cloth:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.CLOTH };
case InvenBagType.Prop:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.PROP };
case InvenBagType.Beauty:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.BEAUTY };
case InvenBagType.Tattoo:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.TATTOO };
case InvenBagType.Etc:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.TOOL
, MetaAssets.EItemLargeType.EXPENDABLE
, MetaAssets.EItemLargeType.TICKET
, MetaAssets.EItemLargeType.RAND_BOX
, MetaAssets.EItemLargeType.SET_BOX
};
default:
Log.getLogger().error($"Invalid InvenBagType !!!, can't equip EItemLargeType : toEquipInvenBagType:{toEquipBagType}");
return new List<MetaAssets.EItemLargeType>();
}
}
public static BagTabType toUsableBagTabType(this InvenBagType bagType)
{
switch (bagType)
{
case InvenBagType.Cloth:
return BagTabType._1;
case InvenBagType.Prop:
return BagTabType._2;
case InvenBagType.Beauty:
return BagTabType._3;
case InvenBagType.Tattoo:
return BagTabType._4;
case InvenBagType.Etc:
return BagTabType._0;
default:
Log.getLogger().error($"Invalid InvenBagType !!!, can't usable BagTabType : InvenBagType:{bagType}");
return BagTabType.None;
}
}
public static InvenBagType toUsableInvenBagType(this BagTabType bagType)
{
switch (bagType)
{
case BagTabType._1:
return InvenBagType.Cloth;
case BagTabType._2:
return InvenBagType.Prop;
case BagTabType._3:
return InvenBagType.Beauty;
case BagTabType._4:
return InvenBagType.Tattoo;
case BagTabType._0:
return InvenBagType.Etc;
default:
return InvenBagType.None;
}
}
public static string toUsableInvenBagName(this BagTabType bagTabType)
{
var is_nft = false;
var inven_bag_type = InventoryRuleHelper.toUsableInvenBagType(bagTabType);
if(InvenBagType.None == inven_bag_type)
{
if(BagTabType._5 <= bagTabType && bagTabType < BagTabType.MAX)
{
is_nft = true;
bagTabType -= (BagTabType.MAX - BagTabType._5);
inven_bag_type = InventoryRuleHelper.toUsableInvenBagType(bagTabType);
}
}
var inven_bag_name = EnumHelper.convertEnumToEnumTypeAndValueString(inven_bag_type);
if (false == is_nft)
{
inven_bag_name += $"_NormalTab";
}
else
{
inven_bag_name += $"_NftTab";
}
return inven_bag_name;
}
public static BagTabType toNFTTabOffset(this BagTabType bagTabType, MetaAssets.ItemMetaData itemMeta)
{
return bagTabType + (true == itemMeta.IsNFT ? (BagTabType.MAX - BagTabType._5) : 0);
}
public static EntityDynamoDbSortKeyType toEntitySKType(this InvenEquipType equipType)
{
switch (equipType)
{
case InvenEquipType.Cloth:
return EntityDynamoDbSortKeyType.Guid;
case InvenEquipType.Tool:
return EntityDynamoDbSortKeyType.Guid;
case InvenEquipType.Tattoo:
return EntityDynamoDbSortKeyType.Guid;
default:
Log.getLogger().error($"Invalid InvenEquipType !!!, can't use EntityDynamoDbSortKeyType : InvenEquipType:{equipType}");
return EntityDynamoDbSortKeyType.None;
}
}
public static List<MetaAssets.EItemLargeType> toEquipableItemLargeTypes(this InvenEquipType equipType)
{
switch (equipType)
{
case InvenEquipType.Cloth:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.CLOTH };
case InvenEquipType.Tool:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.TOOL };
case InvenEquipType.Tattoo:
return new List<MetaAssets.EItemLargeType>() { MetaAssets.EItemLargeType.TATTOO };
default:
Log.getLogger().error($"Invalid InvenEquipType !!!, can't equip EItemLargeType : InvenEquipType:{equipType}");
return new List<MetaAssets.EItemLargeType>();
}
}
public static List<MetaAssets.EItemSmallType> toEquipableItemSmallTypesWithClothSlotType(this InvenEquipType equipType, ClothSlotType clothSlotType)
{
switch (equipType)
{
case InvenEquipType.Cloth:
return clothSlotType.toEquipableItemSmallTypes();
default:
Log.getLogger().error($"Invalid InvenEquipType !!!, can't equip ClothSlotType : InvenEquipType:{equipType}, ClothSlotType:{clothSlotType}");
return new List<MetaAssets.EItemSmallType>();
}
}
public static List<MetaAssets.EItemSmallType> toEquipableItemSmallTypes(this ClothSlotType clothSlotType)
{
var list = new List<EItemSmallType>();
foreach (var equipData in MetaData.Instance.Meta.ClothEquipTypeMetaTable.ClothEquipTypeDataList)
{
if (false == EnumHelper.tryParse<ClothSlotType>(equipData.EquipSlotType, out var slotType)) continue;
if (slotType != clothSlotType) continue;
if (false == EnumHelper.tryParse<EItemSmallType>(equipData.SmallType, out var smallType)) continue;
list.Add(smallType);
}
if (list.Count <= 0)
{
Log.getLogger().warn($"Invalid ClothSlotType !!!, can't equip ClothSlotType : ClothSlotType:{clothSlotType}");
}
return list;
}
public static List<ToolSlotType> toEquipableToolSlotTypes(this InvenEquipType equipType)
{
switch (equipType)
{
case InvenEquipType.Tool:
return new List<ToolSlotType>() { ToolSlotType._1, ToolSlotType._2, ToolSlotType._3, ToolSlotType._4 };
default:
Log.getLogger().error($"Invalid InvenEquipType !!!, can't equip ToolSlotType : InvenEquipType:{equipType}");
return new List<ToolSlotType>();
}
}
public static List<TattooSlotType> toEquipableTattooSlotTypes(this InvenEquipType equipType, EntityType entityType = EntityType.None)
{
switch (equipType)
{
case InvenEquipType.Tattoo:
{
if( EntityType.UgcNpc == entityType
|| EntityType.Beacon == entityType )
{
return new List<TattooSlotType>() { TattooSlotType._1, TattooSlotType._2, TattooSlotType._3 };
}
return new List<TattooSlotType>() { TattooSlotType._1, TattooSlotType._2, TattooSlotType._3, TattooSlotType._4 };
}
}
Log.getLogger().error($"Invalid InvenEquipType !!!, can't equip TattooSlotType : InvenEquipType:{equipType}");
return new List<TattooSlotType>();
}
public static bool isEquipableTattooSlotType(this InvenEquipType equipType, TattooSlotType slotType, EntityType entityType)
{
var equipable_slot_types = equipType.toEquipableTattooSlotTypes(entityType);
var found_slot_type = equipable_slot_types.FirstOrDefault(x => x == slotType, TattooSlotType.None);
if (TattooSlotType.None == found_slot_type)
{
var err_msg = $"Not found TattooSlotType !!! : InvenEquipType:{equipType}, TattooSlotType:{slotType} - entityType:{entityType}";
Log.getLogger().error(err_msg);
return false;
}
return true;
}
public static ClothSlotType toClothSlotType(this MetaAssets.EItemSmallType smallType)
{
if (false == MetaData.Instance.Meta.ClothEquipTypeMetaTable.ClothEquipTypeDataListbyId.TryGetValue(smallType.ToString(), out var slotTypeData))
{
Log.getLogger().warn($"Invalid EItemSmallType !!!, can't convert ClothSlotType : EItemSmallType:{smallType}");
return ClothSlotType.None;
}
if (false == EnumHelper.tryParse<ClothSlotType>(slotTypeData.EquipSlotType, out var slotType))
{
Log.getLogger().warn($"Invalid EquipSlotType !!!, can't convert ClothSlotType : EquipSlotType:{slotTypeData.EquipSlotType}");
return ClothSlotType.None;
}
return slotType;
}
}

View File

@@ -0,0 +1,234 @@
using ServerCore;
using ServerBase;
using WORLD_META_ID = System.UInt32;
namespace ServerCommon;
public static class LoadBalanceServerHelper
{
private const int READ_SERVER_DATA_LIMIT_COUNT = 5;
private const int FULL_LIMIT_RATE = 100;
public static async Task<ServerInfo?> getAuthLoginToChannelServerInfo( this IWithServerMetrics serverMetrics
, EntityBase entityUser
, UInt16 worldId = 0 )
{
if (0 >= worldId)
{
worldId = getWorldIdWenServerChange(entityUser);
}
return await getBalancedServerInfo(serverMetrics, entityUser, ServerType.Channel, ServerMoveType.Auto, worldId);
}
public static async Task<ServerInfo?> getReturnToServerInfo( this IWithServerMetrics serverMetrics
, string toReturnServerName, ServerType toReturnServerType
, EntityBase entityUser, ushort worldId = 0 )
{
var err_msg = string.Empty;
(var result, ServerInfo ? found_server_info) = await serverMetrics.getServerInfoByServerName(toReturnServerName);
if (result.isSuccess() && null != found_server_info)
{
return found_server_info;
}
found_server_info = await getBalancedServerInfo(serverMetrics, entityUser, toReturnServerType, ServerMoveType.Auto, worldId);
if(null != found_server_info)
{
return found_server_info;
}
err_msg = $"Not found ServerInfo !!!, in getReturnToServerInfo() : {result.toBasicString()} - {entityUser.toBasicString()}, {serverMetrics.toBasicString()}";
Log.getLogger().error(err_msg);
return null;
}
public static async Task<ServerInfo?> getBalancedServerInfo( this IWithServerMetrics IWithServerMetrics
, EntityBase entityUser
, ServerType toServerType, ServerMoveType serverMoveType
, UInt16 worldId = 0 )
{
string err_msg;
// 1. 모든 서버 리스트 가져오기
var loaded_servers = await getAllServersPassableEntered(IWithServerMetrics, toServerType, worldId);
if (!loaded_servers.Any())
{
err_msg = $"ServerMetrics count zero !!! toServerType:{toServerType}, worldId:{worldId} - {entityUser.toBasicString()}";
Log.getLogger().fatal(err_msg);
return null;
}
// 2. 이동 타입에 따른 체크
switch (serverMoveType)
{
case ServerMoveType.Auto:
return await selectServerInfoFromList(loaded_servers.ToList(), entityUser, MetaHelper.GameConfigMeta.AutoMoveRate);
case ServerMoveType.Force:
return await selectServerInfoFromList(loaded_servers.ToList(), entityUser, FULL_LIMIT_RATE);
case ServerMoveType.Return:
case ServerMoveType.None:
default:
return null;
}
}
private static async Task<ServerInfo?> selectServerInfoFromList(IReadOnlyList<ServerInfo> servers, EntityBase entityUser, int limitRate)
{
await Task.CompletedTask;
ServerInfo? select_server = null;
ServerInfo? language_server = null;
var check_user_count = -1;
var account_attribute = entityUser.getOriginEntityAttribute<AccountAttribute>();
if (null == account_attribute) return null;
foreach (var server in servers)
{
var current_user_count = checkCurrentUserCount(server);
// 1. max count 체크
var max_user_count = server.Capacity * limitRate / 100;
if (max_user_count <= current_user_count) continue;
// 2. current session count 체크
if (check_user_count >= current_user_count) continue;
select_server = server;
check_user_count = current_user_count;
}
return select_server ?? language_server;
}
private static async Task<IEnumerable<ServerInfo>> getAllServersPassableEntered( this IWithServerMetrics serverMetrics
, ServerType toServerType, WORLD_META_ID worldMetaId )
{
for (var i = 0; i < READ_SERVER_DATA_LIMIT_COUNT; i++)
{
// 1. 서버 정보 가져오기
(var result, var found_server_infos) = await serverMetrics.getServerInfosByServerType(toServerType, worldMetaId);
if (result.isFail())
{
continue;
}
// 2. 서버 최대 세션수 체크
// 서버의 최대 세션수보다 (현재 세션수 + 예약자 수 + 복귀자 수 + Beacon 수)가 크거나 같을때 목록에서 제외시킨다.
found_server_infos.RemoveAll(x => checkCurrentUserCount(x) >= x.Capacity);
return found_server_infos;
}
return new List<ServerInfo>();
}
private static UInt16 getWorldIdWenServerChange(EntityBase entityUser)
{
UInt16 target_world_id = 0;
// 1. 기본 Default 정보 확인
if (!MetaData.Instance._GameConfigMetaTable.TryGetValue(ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth.ToString(), out var found_value))
{
var err_msg = $"ConstantKeyType.DefaultEntryWorldIdWhenLoginToAuth not found !!! : worldId:{found_value} - {entityUser.toBasicString()}";
Log.getLogger().fatal(err_msg);
return 0;
}
// 2. LastConnectionChannel 정보 확인
var location_attribute = entityUser.getOriginEntityAttribute<LocationAttribute>();
if (null == location_attribute) return target_world_id;
target_world_id = (location_attribute.LastestChannelServerLocation.WorldMetaId == 0)
? Convert.ToUInt16(found_value)
: (UInt16)location_attribute.LastestChannelServerLocation.WorldMetaId;
return target_world_id;
}
private static int checkCurrentUserCount(ServerInfo server) => server.Sessions + server.Reservation + server.ReturnCount + server.UgcNpcCount;
/// <summary>
/// *인스턴스 룸을 생성할 인스턴스 서버 고르기*<br/>
/// 인스턴스 룸 최대 인원이 들어갈 수 있는 서버 중 수용인원이 가장 많은 서버 선택
/// </summary>
/// <param name="serverList">인스턴스 서버 목록</param>
/// <param name="roomCapacity">생성할 인스턴스 룸 최대 인원</param>
/// <returns>입력값 List&lt;ServerInfo&gt; serverList 의 인덱스, 선택된 서버가 없는 경우 -1</returns>
public static int getBestInstanceServerIndexForCreate(in List<ServerInfo> serverList, int roomCapacity)
{
int retIndex = -1;
int maxCount = 0;
foreach (var (server_info, index) in serverList.Select((value, index) => (value, index)))
{
if (maxCount < server_info.RoomCapacity + roomCapacity && server_info.RoomCapacity + roomCapacity <= server_info.Capacity)
{
retIndex = index;
maxCount = server_info.RoomCapacity + roomCapacity;
}
}
return retIndex;
}
/// <summary>
/// *게임 서버 고르기*<br/>
/// 서버 수용인원의 80% 되지 않은 서버에서 선택<br/>
/// 모든 서버가 80% 이상인 경우 서버 수용인원을 넘지 않은 서버에서 선택
/// </summary>
/// <param name="serverList">게임 서버 목록</param>
/// <returns>입력값 List&lt;ServerInfo&gt; serverList 의 인덱스, 선택된 서버가 없는 경우 -1</returns>
public static int getBestGameServerIndex(List<ServerInfo> serverList, LanguageType languageType
, ref int currentServerIndex)
{
int server_index = -1;
int limit_first = Constant.g_MaxUser * 5 / 10;
/*
foreach (var serverInfo in serverList.Select((value, index) => new { value, index }))
{
if (serverInfo.value.Language == languageType &&
limit_first > serverInfo.value.Sessions)
{
serverIndex = serverInfo.index;
break;
}
}
if (serverIndex == -1)
{
foreach (var serverInfo in serverList.Select((value, index) => new { value, index }))
{
if (limit_first > serverInfo.value.Sessions)
{
serverIndex = serverInfo.index;
break;
}
}
}
*/
if (server_index == -1)
{
if (currentServerIndex >= serverList.Count)
{
currentServerIndex = 0;
}
server_index = currentServerIndex;
++currentServerIndex;
}
return server_index;
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace ServerCommon;
public static class LocationHelper
{
public static void clear(this ChannelServerLocation location)
{
location.SpawnPos = Vector3.Zero;
location.ForwardAngle = 0.0f;
location.ServerName = string.Empty;
location.WorldMetaId = 0;
}
public static void clear(this IndunLocation location)
{
location.SpawnPos = Vector3.Zero;
location.ForwardAngle = 0.0f;
location.InstanceMetaId = 0;
location.InstanceRoomId = string.Empty;
}
public static ChannelServerLocation clone(this ChannelServerLocation location)
{
ChannelServerLocation clone = new();
clone.SpawnPos = location.SpawnPos;
clone.ForwardAngle = location.ForwardAngle;
clone.ServerName = location.ServerName;
clone.WorldMetaId = location.WorldMetaId;
return clone;
}
public static IndunLocation clone(this IndunLocation location)
{
IndunLocation clone = new();
clone.SpawnPos = location.SpawnPos;
clone.ForwardAngle = location.ForwardAngle;
clone.InstanceMetaId = location.InstanceMetaId;
clone.InstanceRoomId = location.InstanceRoomId;
return clone;
}
public static void toPos(this BaseLoaction location, Pos pos)
{
pos.X = location.SpawnPos.X;
pos.Y = location.SpawnPos.Y;
pos.Z = location.SpawnPos.Z;
pos.Angle = (int)location.ForwardAngle;
}
public static void fromPos(this BaseLoaction location, Pos pos)
{
var newPos = new Vector3(pos.X, pos.Y, pos.Z);
location.SpawnPos = newPos;
location.ForwardAngle = pos.Angle;
}
public static Vector3 toVector3(this Pos pos)
{
Vector3 new_pos;
new_pos.X = pos.X;
new_pos.Y = pos.Y;
new_pos.Z = pos.Z;
return new_pos;
}
public static Pos toPos(this Vector3 pos)
{
var new_pos = new Pos();
new_pos.X = pos.X;
new_pos.Y = pos.Y;
new_pos.Z = pos.Z;
return new_pos;
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCommon;
public static class MessgaeHelper
{
public static AvatarInfo toCharacterAppearanceProfile4Client( this CharacterAttribute.AppearanceProfile appearanceProfile )
{
var avatar_info = new AvatarInfo();
var appearance_customize = new AppearanceCustomization();
avatar_info.AppearCustomize = appearance_customize;
appearance_customize.BasicStyle = (int)appearanceProfile.BasicStyle;
appearance_customize.BodyShape = (int)appearanceProfile.BodyShape;
appearance_customize.HairStyle = (int)appearanceProfile.HairStyle;
appearance_customize.CustomValues.AddRange(appearanceProfile.CustomValues.Select( x => x ).ToList());
avatar_info.Init = (uint)((true == appearanceProfile.IsCustomCompleted) ? 0 : 1);
return avatar_info;
}
public static AppearanceCustomization toCharacterAppearanceCustomize4Client(this CharacterAttribute.AppearanceProfile appearanceProfile)
{
var appearance_customize = new AppearanceCustomization();
appearance_customize.BasicStyle = (int)appearanceProfile.BasicStyle;
appearance_customize.BodyShape = (int)appearanceProfile.BodyShape;
appearance_customize.HairStyle = (int)appearanceProfile.HairStyle;
appearance_customize.CustomValues.AddRange(appearanceProfile.CustomValues.Select(x => x).ToList());
return appearance_customize;
}
}

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;
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Newtonsoft.Json;
using FUNCTION_NAME = System.String;
namespace ServerCommon;
public static class MetaTextStringHelper
{
public delegate string META_TEXT_STRING_FUNC(object caller, params object[] parameters);
private static readonly Dictionary<FUNCTION_NAME, META_TEXT_STRING_FUNC> m_methods = new();
// 초기 입력 문자열 저장
private static string m_input_string = string.Empty;
private static StringBuilder m_result_builder = new StringBuilder();
public static bool parseAndExecute(string funcName, object caller, params object[] parameters)
{
// 등록된 함수가 있는지 확인
if (m_methods.ContainsKey(funcName))
{
string func_result = m_methods[funcName](caller, parameters); // 함수 실행
// {순번: 함수명} 패턴을 찾아 함수 결과로 대체
string pattern = $@"\{{\d+\s*:\s*{funcName}\}}";
// 패턴을 찾고 함수 결과로 대체
m_result_builder = new StringBuilder(Regex.Replace(m_result_builder.ToString(), pattern, func_result));
return true;
}
return false;
}
public static void inputString(string input)
{
m_input_string = input;
m_result_builder = new StringBuilder(input); // 입력 문자열로 초기화
}
public static string getResult()
{
return m_result_builder.ToString();
}
public static bool registerFunc(string name, META_TEXT_STRING_FUNC func)
{
if (true == m_methods.ContainsKey(name))
{
return false;
}
m_methods[name] = func;
return true;
}
// 문자열 내에 {순번: 함수명} 구조가 존재하는지 확인해주는 함수 !!!, 존재할 경우 true 반환 한다.
public static bool isContainsMetaTextStringFunctionPattern(string input)
{
// {숫자: 함수명} 구조를 찾는 정규 표현식을 설정 한다.
var pattern = @"\{\d+\s*:\s*[a-zA-Z_]\w*\}";
// 정규 표현식으로 입력 문자열에서 해당 패턴이 존재하는지 확인 한다. !!!
return Regex.IsMatch(input, pattern);
}
}

View File

@@ -0,0 +1,126 @@
using Amazon.S3.Model;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace ServerCommon;
public static class MyHomeHelper
{
public static async Task<Result> uploadMyhomeUgcInfo( this S3ConnectorBase s3connector
, string bucketName
, string myhomeUgcInfoS3Key, string myhomeUgcInfo )
{
var result = new Result();
var err_msg = string.Empty;
using (var memory_stream = myhomeUgcInfo.toStream())
{
var request = new PutObjectRequest
{
BucketName = bucketName,
Key = myhomeUgcInfoS3Key,
InputStream = memory_stream,
};
if (false == await s3connector.uploadFile(request))
{
err_msg = $"Failed to uploadFile() !!!";
result.setFail(ServerErrorCode.S3FileUploadFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
}
return result;
}
public static async Task<Result> deleteMyhomeUgcInfo( this S3ConnectorBase s3connector
, string bucketName
, string myhomeUgcInfoS3Key)
{
var result = new Result();
var err_msg = string.Empty;
var request = new DeleteObjectRequest
{
BucketName = bucketName,
Key = myhomeUgcInfoS3Key,
};
if (!await s3connector.deleteFile(request))
{
err_msg = $"Failed to deleteFile() !!!";
result.setFail(ServerErrorCode.S3FileDeleteFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
return result;
}
public static async Task<Result> deleteMyhomeUgcInfoFolderFile( this S3ConnectorBase s3connector
, string bucketName
, string myhomeGuid )
{
var result = new Result();
var err_msg = string.Empty;
var request = new ListObjectsV2Request
{
BucketName = bucketName,
Prefix = myhomeGuid,
};
if (!await s3connector.deleteFolderFile(request))
{
err_msg = $"Failed to deleteFile() !!!";
result.setFail(ServerErrorCode.S3FileDeleteFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
return result;
}
public static async Task<(Result, string)> getMyhomeUgcInfo( this S3ConnectorBase s3connector
, string bucketName
, string myhomeUgcInfoS3Key )
{
var result = new Result();
var err_msg = string.Empty;
var myhome_ugc_info = string.Empty;
var request = new GetObjectRequest
{
BucketName = bucketName,
Key = myhomeUgcInfoS3Key,
};
(var is_success, var stream) = await s3connector.getFileStream(request);
if ( false == is_success
|| null == stream )
{
err_msg = $"Failed to getFile() !!! - bucketName:{bucketName}, key:{myhomeUgcInfoS3Key}";
result.setFail(ServerErrorCode.S3FileGetFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, myhome_ugc_info);
}
myhome_ugc_info = stream.toString();
return (result, myhome_ugc_info);
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UGQDatabase.Models;
namespace ServerCommon;
public static class QuestHelper
{
public static (UInt32, UInt32) convertUgqQuestIdToQuestIdAndRevision(Int64 ugq_quest_id)
{
var quest_id = (UInt32)(ugq_quest_id & 0xFFFFFFFF);
var quest_revision = (UInt32)(ugq_quest_id >> 32);
return (quest_id, quest_revision);
}
public static Int64 convertQuestIdAndRevisionToUgqQuestId(UInt32 quest_id, UInt32 quest_revision)
{
Int64 ugq_quest_id = ((Int64)quest_revision << 32) | quest_id;
return ugq_quest_id;
}
public static UgqStateType convertQuestContentStateToUgqStateType(QuestContentState state)
{
//RevisionChanged 이건 어디다 쓰지?
switch (state)
{
case QuestContentState.None:
case QuestContentState.Editable:
case QuestContentState.Shutdown:
case QuestContentState.Standby:
return UgqStateType.Shutdown;
case QuestContentState.Test:
return UgqStateType.Test;
case QuestContentState.Live:
return UgqStateType.Live;
default:
return UgqStateType.Shutdown;
}
}
public static QuestContentState convertUgqStateTypeToQuestContentState(UgqStateType state)
{
switch (state)
{
case UgqStateType.Test:
return QuestContentState.Test;
case UgqStateType.Live:
return QuestContentState.Live;
case UgqStateType.Shutdown:
case UgqStateType.None:
return QuestContentState.None;
default:
return QuestContentState.None;
}
}
public static string convertUgqGradeToQuestCheckGrade(UgqGradeType gradeType)
{
switch (gradeType)
{
case UgqGradeType.Amature:
return QuestUgqGradeConditionType.AMATEUR.ToString();
case UgqGradeType.RisingStar:
return QuestUgqGradeConditionType.RISINGSTAR.ToString();
case UgqGradeType.Master1:
return QuestUgqGradeConditionType.MASTER1.ToString();
case UgqGradeType.Master2:
return QuestUgqGradeConditionType.MASTER2.ToString();
case UgqGradeType.Master3:
return QuestUgqGradeConditionType.MASTER3.ToString();
case UgqGradeType.None:
return QuestUgqGradeConditionType.ALL.ToString();
default:
return QuestUgqGradeConditionType.ALL.ToString();
}
}
}
public enum QuestUgqGradeConditionType
{
ALL = 1,
AMATEUR = 2,
RISINGSTAR = 3,
MASTER1 = 4,
MASTER2 = 5,
MASTER3 = 6,
}

View File

@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection.KeyManagement;
using Microsoft.AspNetCore.Identity;
using Nettention.Proud;
using Newtonsoft.Json;
using OtpNet;
using Pipelines.Sockets.Unofficial.Buffers;
using StackExchange.Redis;
using ServerCore;
using ServerBase;
using SESSION_ID = System.Int32;
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;
public static class ServerConnectionSwitchHelper
{
public static async Task<Result> registerUserOtpToTargetServer(UserBase entityUser, RedisConnector redisConnector
, string destServer)
{
var result = new Result();
var err_msg = string.Empty;
(result, var reserved_to_switch_server) = await startServerSwitch(entityUser, redisConnector, destServer);
if (result.isFail())
{
return result;
}
NullReferenceCheckHelper.throwIfNull(reserved_to_switch_server, () => $"reserved_to_switch_server is null !!!");
var account_attribute = entityUser.getEntityAttribute<AccountAttribute>();
NullReferenceCheckHelper.throwIfNull(account_attribute, () => $"account_attribute is null !!! - {entityUser.toBasicString()}");
account_attribute.OtpForServerConnect = reserved_to_switch_server.OneTimeKey;
return result;
}
public static async Task<(Result, LoginCache.ReservationToSwitchServer?)> startServerSwitch(UserBase entityUser, RedisConnector redisConnector, string destServer)
{
var result = new Result();
var err_msg = string.Empty;
try
{
var login_cache_request = new LoginCacheRequest(entityUser, redisConnector);
result = await login_cache_request.onPrepareRequest();
if (result.isFail())
{
return (result, null);
}
string key = login_cache_request.getKey();
var database = login_cache_request.getDatabase();
var value = await database.StringGetAsync(key, CommandFlags.PreferReplica);
if (value.HasValue == false)
{
err_msg = $"Not found Key !!! : Key:{key} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.RedisLoginCacheGetFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
var login_cache = JsonConvert.DeserializeObject<LoginCache>(value.ToString());
if (null == login_cache)
{
err_msg = $"Failed to JsonConvert.DeserializeObject !!! : {login_cache_request.toBasicString()} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.JsonConvertDeserializeFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
var otp = KeyGeneration.GenerateRandomKey(20);
var otp_encoding_by_base32 = Base32Encoding.ToString(otp);
login_cache.ReservedToSwitchServer = new LoginCache.ReservationToSwitchServer()
{
OneTimeKey = otp_encoding_by_base32,
DestServer = destServer
};
var json_string = login_cache.toJsonString();
if (await database.StringSetAsync(key, json_string, TimeSpan.FromMilliseconds(LoginCacheRequest.SERVER_SWITCH_CACHE_EXPIRY_TIME)) == false)
{
err_msg = $"Failed to StringSetAsync !!! : Key:{key}, JsonString:{json_string} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.RedisStringsWriteFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
Log.getLogger().info($"Server Switching Start : currServer:{login_cache.CurrentServer}, destServer:{destServer} - {entityUser.toBasicString()}");
return (result, login_cache.ReservedToSwitchServer);
}
catch (Exception e)
{
err_msg = $"Failed to startServerSwitch !!! : Exception:{e} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.TryCatchException, err_msg);
Log.getLogger().error(result.toBasicString());
return (result, null);
}
}
public static async Task<Result> endServerSwitch(EntityBase entityUser, LoginCacheRequest loginCacheRequest
, string currConnectedServer
, DateTime switchedDateTime)
{
var result = new Result();
var err_msg = string.Empty;
try
{
var database = loginCacheRequest.getDatabase();
var login_cache = loginCacheRequest.getLoginCache();
NullReferenceCheckHelper.throwIfNull(login_cache, () => $"login_cache is null !!!");
NullReferenceCheckHelper.throwIfNull(login_cache.ReservedToSwitchServer, () => $"login_cache.ReservedToSwitchServer is null !!!");
string prev_connected_server = login_cache.CurrentServer;
login_cache.LoginDateTime = switchedDateTime;
login_cache.ServerSwitchCount += 1;
login_cache.CurrentServer = login_cache.ReservedToSwitchServer.DestServer;
login_cache.ReservedToSwitchServer = null;
var key = loginCacheRequest.getKey();
var json_string = login_cache.toJsonString();
if (await database.StringSetAsync(key, json_string, TimeSpan.FromMilliseconds(LoginCacheRequest.LOGIN_CACHE_EXPIRY_TIME)) == false)
{
err_msg = $"Failed to StringSetAsync !!! : Key:{key}, JsonString:{json_string} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.RedisStringsWriteFailed, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
Log.getLogger().info($"Server Switching End : prevServer:{prev_connected_server} => currServer:{currConnectedServer} - {entityUser.toBasicString()}");
return result;
}
catch (Exception e)
{
err_msg = $"Failed to startServerSwitch !!! : Exception:{e} - {entityUser.toBasicString()}";
result.setFail(ServerErrorCode.TryCatchException, err_msg);
Log.getLogger().error(result.toBasicString());
return result;
}
}
}

View File

@@ -0,0 +1,18 @@
using ServerCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCommon;
public static class ServerHelper
{
}

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ServerCore;
using ServerBase;
using MODULE_ID = System.UInt32;
namespace ServerCommon;
public static class ServerLogicHelper
{
public static Result loadMetaDatas(this ServerLogicBase serverLogicBase)
{
var result = new Result();
var err_msg = string.Empty;
if (false == MetaData.Instance.LoadTableAll())
{
err_msg = $"Failed to LoadTableAll()!!! : {serverLogicBase.toBasicString()}";
result.setFail(ServerErrorCode.MetaDataLoadFailed, err_msg);
Log.getLogger().error(err_msg);
return result;
}
return result;
}
public static ProudNetListener getProudNetListener(this ServerLogicBase serverLogicBase)
{
var found_proud_net_listener = ServerBase.ServerLogicHelper.getListenSessionBase(serverLogicBase) as ProudNetListener;
NullReferenceCheckHelper.throwIfNull(found_proud_net_listener, () => $"found_proud_net_listener is null !!! - {serverLogicBase.toBasicString()}");
return found_proud_net_listener;
}
public static ILogActor toLogActor(this ServerLogicBase serverLogicBase)
{
var log_info = new LogicActorLog();
log_info.initLogInfo(
serverLogicBase.getServerConfig().getRegionId()
, serverLogicBase.getServerConfig().getWorldId()
, serverLogicBase.getServerType().toServerType()
, serverLogicBase.getServerName()
);
return log_info;
}
}

View File

@@ -0,0 +1,46 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
using WORLD_META_ID = System.UInt32;
namespace ServerCommon;
public static class ServerMetricsHelper
{
public static async Task<(Result, List<ServerInfo>)> getServerInfoAll(this IWithServerMetrics serverMetrics)
{
var result = new Result();
var server_metrics_request = serverMetrics.getServerMetricsCacheRequest();
NullReferenceCheckHelper.throwIfNull(server_metrics_request, () => $"server_metrics_request is null - {serverMetrics.toBasicString()}");
var handler_type = (uint)ServerMetricsCacheRequest.TriggerType.ServerMetrics_AllGetAndFill;
var world_meta_ids = new List<WORLD_META_ID>();
foreach (var worldData in MetaData.Instance._WorldMetaTable)
{
world_meta_ids.Add((WORLD_META_ID)worldData.Value.Id);
}
var with_result = await server_metrics_request.tryRunTriggerHandler(handler_type, world_meta_ids);
if (with_result.Result.isFail())
{
return (with_result.Result, new List<ServerInfo>());
}
if (with_result.isResultOnly())
{
return (result, new List<ServerInfo>());
}
var result_value_server_info = with_result as ResultValue<List<ServerInfo>>;
NullReferenceCheckHelper.throwIfNull(result_value_server_info, () => $"result_value_server_info is null - {serverMetrics.toBasicString()}");
return (result, result_value_server_info.ValueOfResult);
}
}

View File

@@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
namespace ServerCommon;
public static class StringRuleHelper
{
public static ServerErrorCode isValidNickname(string nickName)
{
ArgumentNullReferenceCheckHelper.throwIfNull(nickName, () => $"stringValue is null");
// 첫번째 글자가 숫자인지 체크 한다.
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.isBanNickname(nickName) == true)
{
Log.getLogger().info($"User Nickname is Ban !!! : nickname:{nickName}");
return ServerErrorCode.UserNicknameBan;
}
return ServerErrorCode.Success;
}
public static string convertStringFromBase64(string stringValue)
{
ArgumentNullReferenceCheckHelper.throwIfNull(stringValue, () => $"stringValue is null");
string decoded_text = string.Empty;
try
{
byte[] byteArray = Convert.FromBase64String(stringValue);
decoded_text = Encoding.UTF8.GetString(byteArray);
}
catch (Exception e)
{
Log.getLogger().error($"convertStringFromBase64 exception occured {e.StackTrace}");
}
return decoded_text;
}
public static string getString(LanguageType langType, List<OperationSystemMessage> systemMessage)
{
ArgumentNullReferenceCheckHelper.throwIfNull(systemMessage, () => $"systemMessage is null");
string txt = string.Empty;
foreach (var msg in systemMessage)
{
if ((int)msg.LanguageType == (int)langType)
{
//var base64_string = StringRuleHelper.convertStringFromBase64(msg.Text);
txt = msg.Text;
break;
}
}
return txt;
}
public static bool isBanWord(string word, bool includeNickname)
{
var lowered_word = word.ToLower();
if (MetaData.Instance._BanWordMetaTable.Any(banWord => lowered_word.Contains(banWord) == true))
{
return true;
}
return includeNickname && isBanNickname(word);
}
private static bool isBanNickname(string word)
{
var lowered_word = word.ToLower();
return MetaData.Instance._BanWordNicknameMetaTable.Any(banNickNameWord => lowered_word.Contains(banNickNameWord) == true);
}
}

View File

@@ -0,0 +1,74 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ServerCore;
using ServerBase;
using ServerCommon;
using ServerCommon.BusinessLogDomain;
using MetaAssets;
namespace ServerCommon;
public static class TransactionRunnerHelper
{
public static async Task onApplyCommonResultOfInsertedItem( this TransactionRunner transactionRunner
, ItemBase insertedItemInBag )
{
await Task.CompletedTask;
var owner = transactionRunner.getOwner();
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
ArgumentNullReferenceCheckHelper.throwIfNull(insertedItemInBag, () => $"insertedItemInBag is null !!! - {owner.toBasicString()}");
var item_attribute_base = insertedItemInBag.getEntityAttribute<ItemAttributeBase>();
NullReferenceCheckHelper.throwIfNull(item_attribute_base, () => $"item_attribute_base is null !!! - {owner.toBasicString()}");
(var result, var entity_common_result) = transactionRunner.getOrNewEntityCommonResult(item_attribute_base);
if (result.isFail())
{
Log.getLogger().fatal(result.toBasicString());
// 유저 로그 아웃 처리 필요 !!!
return;
}
NullReferenceCheckHelper.throwIfNull(entity_common_result, () => $"entity_common_result is null !!! - {owner.toBasicString()}");
var item_result = entity_common_result.Item;
NullReferenceCheckHelper.throwIfNull(item_result, () => $"item_result is null !!! - {owner.toBasicString()}");
var item_guid = item_attribute_base.ItemGuid;
item_result.NewItems.TryAdd(item_guid, item_attribute_base.toItemData4Client());
}
public static async Task onApplyCommonResultOfDeletedItem( this TransactionRunner transactionRunner
, ItemBase deletedItemInBag )
{
await Task.CompletedTask;
var owner = transactionRunner.getOwner();
NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!");
NullReferenceCheckHelper.throwIfNull(deletedItemInBag, () => $"deletedItemInBag is null !!! - {owner.toBasicString()}");
var item_attribute_base = deletedItemInBag.getEntityAttribute<ItemAttributeBase>();
NullReferenceCheckHelper.throwIfNull(item_attribute_base, () => $"item_attribute_base is null !!! - {owner.toBasicString()}");
(var result, var entity_common_result) = transactionRunner.getOrNewEntityCommonResult(item_attribute_base);
if (result.isFail())
{
Log.getLogger().fatal(result.toBasicString());
// 유저 로그 아웃 처리 필요 !!!
return;
}
NullReferenceCheckHelper.throwIfNull(entity_common_result, () => $"entity_common_result is null !!! - {owner.toBasicString()}");
var item_result = entity_common_result.Item;
NullReferenceCheckHelper.throwIfNull(item_result, () => $"item_result is null !!! - {owner.toBasicString()}");
var item_guid = item_attribute_base.ItemGuid;
item_result.DeletedItems.Add(item_guid);
}
}

View File

@@ -0,0 +1,48 @@
using ServerCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCommon;
public static class TypeHelper
{
public static EPlaceType toPlaceType(this ContentsType contentsType)
{
switch (contentsType)
{
case ContentsType.None:
return EPlaceType.NONE;
case ContentsType.MyHome:
return EPlaceType.MyHome;
case ContentsType.DressRoom:
return EPlaceType.DressRoom;
case ContentsType.Concert:
return EPlaceType.Concert;
case ContentsType.Movie:
return EPlaceType.Movie;
case ContentsType.Instance:
return EPlaceType.Instance;
case ContentsType.BeaconCreateRoom:
return EPlaceType.BeaconCreateRoom;
case ContentsType.BeaconEditRoom:
return EPlaceType.BeaconEditRoom;
case ContentsType.BeaconDraftRoom:
return EPlaceType.BeaconDraftRoom;
case ContentsType.EditRoom:
return EPlaceType.EditRoom;
case ContentsType.BeaconCustomizeRoom:
return EPlaceType.BeaconCustomizeRoom;
case ContentsType.BattleRoom:
return EPlaceType.BattleRoom;
case ContentsType.ArcadeRunning:
return EPlaceType.ArcadeRunning;
}
return EPlaceType.NONE;
}
}

View File

@@ -0,0 +1,34 @@
using Amazon.DynamoDBv2.DataModel;
using ServerCore;
using ServerBase;
namespace ServerCommon;
public class UserBlockHelper
{
public static HashSet<string> convertBlockPolicyToJavaPolicy(List<string> blockPolicy)
{
HashSet<string> policies = new();
foreach (var policy in blockPolicy)
{
switch (policy)
{
case "Access_Restrictions":
policies.Add(UserBlockPolicyType.AccessRestrictions.ToString());
break;
case "Chatting_Restrictions":
policies.Add(UserBlockPolicyType.ChattingRestrictions.ToString());
break;
default:
Log.getLogger($"BlockPolicy not match policy : {policy}");
break;
}
}
return policies;
}
}