1027 lines
41 KiB
C#
1027 lines
41 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Concurrent;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
using NeoSmart.AsyncLock;
|
|
using Axion.Collections.Concurrent;
|
|
|
|
|
|
|
|
using ServerCore; using ServerBase;
|
|
|
|
|
|
|
|
using SESSION_ID = System.Int32;
|
|
using WORLD_ID = System.UInt32;
|
|
using META_ID = System.UInt32;
|
|
using TRANSACTION_NAME = System.String;
|
|
using TRANSACTION_GUID = System.String;
|
|
using ENTITY_GUID = System.String;
|
|
using MASTER_GUID = System.String;
|
|
using SUMMENED_ENTITY_GUID = System.String;
|
|
using ACCOUNT_ID = System.String;
|
|
using OWNER_GUID = System.String;
|
|
using DB_GUID = System.String;
|
|
using USER_GUID = System.String;
|
|
using CHARACTER_GUID = System.String;
|
|
using ITEM_GUID = System.String;
|
|
|
|
|
|
|
|
namespace ServerBase;
|
|
|
|
/*===================================================================================================================================================
|
|
// Entity 정보 관리 흐름도
|
|
|
|
1. 일반적인 시퀀스
|
|
|
|
+ 읽기 => 쓰기 (메타 정보 기반)
|
|
MetaData => MetaValidChecker => Action => Attribute => optional AttributeTransactor => optional Cache => optional Database => Attribute
|
|
|
|
* 읽기 => 쓰기 (데이터베이스 기반)
|
|
Database => MetaValidChecker => Action => Attribute => optional AttributeTransactor => optional Cache => optional Database => Attribute
|
|
|
|
- kangms
|
|
//=================================================================================================================================================*/
|
|
|
|
public abstract partial class EntityBase : IActor, IInitializer, IWithTaskSerializer
|
|
{
|
|
private EntityType m_entity_type = EntityType.None;
|
|
|
|
private readonly ENTITY_GUID m_entity_guid = System.Guid.NewGuid().ToString("N");
|
|
|
|
private EntityBase? m_parent_nullable;
|
|
|
|
private TaskSerializer m_task_serializer = new();
|
|
|
|
private readonly ConcurrentDictionary<Type, EntityAttributeBase> m_entity_attributes = new();
|
|
private readonly ConcurrentDictionary<Type, EntityActionBase> m_entity_actions = new();
|
|
|
|
//private EntityHFSMBase? m_entity_hfsm_base_nullable; // TODO kangms : 추후 팀장님 수정.
|
|
|
|
private readonly ConcurrentDictionary<Type, RedisRequestPrivateBase> m_redis_private_caches = new();
|
|
|
|
private readonly ConcurrentDictionary<TransactionIdType, TransactionRunner> m_transaction_runners = new();
|
|
|
|
private MASTER_GUID m_master_guid = string.Empty; // MASTER_GUID는 변하지 않는 고유 Id로 설정 해야 한다. !!!
|
|
private readonly ConcurrentHashSet<SUMMENED_ENTITY_GUID> m_summoned_entities = new(); // SUMMENED_ENTITY_GUID는 변하지 않는 고유 Id로 설정 해야 한다. !!!
|
|
|
|
private AsyncLock m_async_lock = new();
|
|
|
|
//=========================================================================================
|
|
// Ganeral or Master로 생성 !!!
|
|
//=========================================================================================
|
|
public EntityBase(EntityType entityType)
|
|
{
|
|
m_entity_type = entityType;
|
|
}
|
|
//=========================================================================================
|
|
// Master/Summoned 관계로 생성될 경우 : Summoned로 생성 !!!
|
|
//=========================================================================================
|
|
public EntityBase(EntityType entityType, MASTER_GUID masterGuid)
|
|
{
|
|
m_entity_type = entityType;
|
|
m_master_guid = masterGuid;
|
|
}
|
|
|
|
public EntityBase(EntityType entityType, EntityBase parent)
|
|
{
|
|
m_entity_type = entityType;
|
|
m_parent_nullable = parent;
|
|
}
|
|
|
|
// 상속 받은 클래스 onInit() 호출시 맨 마지막에 호출해야 한다 !!!
|
|
public virtual async Task<Result> onInit()
|
|
{
|
|
var result = new Result();
|
|
|
|
var initial_name = string.Empty;
|
|
|
|
var entity_attribute_initializers = new Initializers();
|
|
foreach (var each in m_entity_attributes)
|
|
{
|
|
entity_attribute_initializers.appendInitializer(each.Value as IInitializer);
|
|
}
|
|
initial_name = $"Initialize for EntityAttribute : {this.getTypeName()}";
|
|
result = await entity_attribute_initializers.init(initial_name);
|
|
if(result.isFail())
|
|
{
|
|
Log.getLogger().error($"Failed to init() !!! : {result.toBasicString()} - {toBasicString()}");
|
|
return result;
|
|
}
|
|
|
|
var entity_action_initializers = new Initializers();
|
|
foreach (var each in m_entity_actions)
|
|
{
|
|
entity_action_initializers.appendInitializer(each.Value as IInitializer);
|
|
}
|
|
initial_name = $"Initialize for EntityAction : {this.getTypeName()}";
|
|
result = await entity_action_initializers.init(initial_name);
|
|
if (result.isFail())
|
|
{
|
|
Log.getLogger().error($"Failed to init() !!! : {result.toBasicString()} - {toBasicString()}");
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public virtual void onCearAll()
|
|
{
|
|
foreach(var each in m_entity_actions)
|
|
{
|
|
var action = each.Value;
|
|
action.onClear();
|
|
}
|
|
|
|
foreach (var each in m_entity_attributes)
|
|
{
|
|
var attribute = each.Value;
|
|
attribute.onClear();
|
|
}
|
|
}
|
|
|
|
public ServerErrorCode addEntityAttribute<TEntityAttribute>(TEntityAttribute entityAttribute)
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
var error_code = ServerErrorCode.Success;
|
|
var err_msg = string.Empty;
|
|
|
|
if (false == m_entity_attributes.TryAdd(entityAttribute.GetType(), entityAttribute))
|
|
{
|
|
error_code = ServerErrorCode.EntityAttribDuplicated;
|
|
err_msg = $"Failed to TryAdd() EntityAttribute !!!, duplicated EntityAttribute : {entityAttribute.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
return error_code;
|
|
}
|
|
|
|
return ServerErrorCode.Success;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 Original TEntityAttribute를 반환해 주거나
|
|
// TransactionRunner에서 관리되는 Cloned된 TEntityAttribute를 반환할 수도 있다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual TEntityAttribute? getEntityAttribute<TEntityAttribute>()
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attrib))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var root_parent = getRootParent();
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if(null != transaction_runner)
|
|
{
|
|
return transaction_runner.getEntityAttribute(found_entity_attrib) as TEntityAttribute;
|
|
}
|
|
|
|
return found_entity_attrib as TEntityAttribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 Original TEntityAttribute를 반환해 주거나
|
|
// TransactionRunner에서 관리되는 Cloned된 TEntityAttribute를 반환할 수도 있다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual Result getEntityAttribute<TEntityAttribute>(out TEntityAttribute? foundEntityAttribute)
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
foundEntityAttribute = null;
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attrib))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
foundEntityAttribute = transaction_runner.getEntityAttribute(found_entity_attrib) as TEntityAttribute;
|
|
return result;
|
|
}
|
|
|
|
foundEntityAttribute = found_entity_attrib as TEntityAttribute;
|
|
return result;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 Original EntityAttribute를 반환해 주거나
|
|
// TransactionRunner에서 관리되는 Cloned된 EntityAttribute를 반환할 수도 있다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual EntityAttributeBase? getEntityAttribute(Type entityAttributeType)
|
|
{
|
|
if (false == m_entity_attributes.TryGetValue(entityAttributeType, out var found_entity_attribute))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var root_parent = getRootParent();
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
return transaction_runner.getEntityAttribute(found_entity_attribute);
|
|
}
|
|
|
|
return found_entity_attribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 Original TEntityAttribute를 반환해 준다.
|
|
// TransactionRunner에서 관리되지 않는다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual TEntityAttribute? getOriginEntityAttribute<TEntityAttribute>()
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attrib))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return found_entity_attrib as TEntityAttribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 Original EntityAttribute를 반환해 준다.
|
|
// TransactionRunner에서 관리되지 않는다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public virtual EntityAttributeBase? getOriginEntityAttribute(Type entityAttributeType)
|
|
{
|
|
if (false == m_entity_attributes.TryGetValue(entityAttributeType, out var found_entity_attrib))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return found_entity_attrib;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 ReadOnly 설정으로 TEntityAttribute 반환해 준다.
|
|
// TransactionRunner에서 관리되는 TEntityAttribute를 반환해 줄 수도 있다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual TEntityAttribute? getEntityAttributeWithReadOnly<TEntityAttribute>()
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attrib))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var root_parent = getRootParent();
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
return transaction_runner.getEntityAttributeWithReadOnly(found_entity_attrib) as TEntityAttribute;
|
|
}
|
|
|
|
return found_entity_attrib as TEntityAttribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 ReadOnly 설정으로 TEntityAttribute 반환해 준다.
|
|
// TransactionRunner에서 관리되는 TEntityAttribute를 반환해 줄 수도 있다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
public virtual Result getEntityAttributeWithReadOnly<TEntityAttribute>(out TEntityAttribute? foundEntityAttribute)
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
foundEntityAttribute = null;
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attrib))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
foundEntityAttribute = transaction_runner.getEntityAttributeWithReadOnly(found_entity_attrib) as TEntityAttribute;
|
|
if(null == foundEntityAttribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
foundEntityAttribute = found_entity_attrib as TEntityAttribute;
|
|
return result;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 Clone 되어 있거나 or onCloned()을 호출하여 복제된 TEntityAttribute 반환해 준다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual TEntityAttribute? getEntityAttributeWithCloneOnly<TEntityAttribute>()
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
var err_msg = string.Empty;
|
|
var result = new Result();
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attribute))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return null;
|
|
}
|
|
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
var cloned_entity_attribute = transaction_runner.getEntityAttribute(found_entity_attribute) as TEntityAttribute;
|
|
if (null == cloned_entity_attribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return null;
|
|
}
|
|
|
|
return cloned_entity_attribute;
|
|
}
|
|
|
|
var origin_entity_attribute = found_entity_attribute as TEntityAttribute;
|
|
if (null == origin_entity_attribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return null;
|
|
}
|
|
|
|
return origin_entity_attribute.onCloned() as TEntityAttribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 Clone 되어 있거나 or onCloned()을 호출하여 복제된 TEntityAttribute 반환해 준다.
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual Result getEntityAttributeWithCloneOnly<TEntityAttribute>(out TEntityAttribute? clonedEntityAttribute)
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
clonedEntityAttribute = null;
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attribute))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var transaction_runner = root_parent.findTransactionRunner(TransactionIdType.PrivateContents);
|
|
if (null != transaction_runner)
|
|
{
|
|
clonedEntityAttribute = transaction_runner.getEntityAttribute(found_entity_attribute) as TEntityAttribute;
|
|
if (null == clonedEntityAttribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
var origin_entity_attribute = found_entity_attribute as TEntityAttribute;
|
|
if (null == origin_entity_attribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
clonedEntityAttribute = origin_entity_attribute.onCloned() as TEntityAttribute;
|
|
|
|
return result;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 onCloned()을 호출하여 복제된 TEntityAttribute 반환해 준다.
|
|
// TransactionRunner에 관리되지 않는다 !!!
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual TEntityAttribute? getClonedEntityAttribute<TEntityAttribute>()
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
var err_msg = string.Empty;
|
|
var result = new Result();
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attribute))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return null;
|
|
}
|
|
|
|
var origin_entity_attribute = found_entity_attribute as TEntityAttribute;
|
|
if (null == origin_entity_attribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return null;
|
|
}
|
|
|
|
var cloned_entity_attribute = origin_entity_attribute.onCloned() as TEntityAttribute;
|
|
NullReferenceCheckHelper.throwIfNull(cloned_entity_attribute, () => $"cloned_entity_attribute is null !!! - {root_parent.toBasicString()}");
|
|
|
|
var origin_doc_base = origin_entity_attribute.getOriginDocBase<TEntityAttribute>();
|
|
if(null != origin_doc_base)
|
|
{
|
|
cloned_entity_attribute.syncOriginDocBaseWithNewDoc<TEntityAttribute>(origin_doc_base);
|
|
}
|
|
|
|
return cloned_entity_attribute;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// 등록된 TEntityAttribute 정보를 onCloned()을 호출하여 복제된 TEntityAttribute 반환해 준다.
|
|
// TransactionRunner에 관리되지 않는다 !!!
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
public virtual Result getClonedEntityAttribute<TEntityAttribute>(out TEntityAttribute? clonedEntityAttribute)
|
|
where TEntityAttribute : EntityAttributeBase
|
|
{
|
|
clonedEntityAttribute = null;
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var root_parent = getRootParent();
|
|
|
|
if (false == m_entity_attributes.TryGetValue(typeof(TEntityAttribute), out var found_entity_attribute))
|
|
{
|
|
err_msg = $"Not found TEntityAttribute !!! : Type:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var origin_entity_attribute = found_entity_attribute as TEntityAttribute;
|
|
if(null == origin_entity_attribute)
|
|
{
|
|
err_msg = $"Failed to cast EntityAttribute !!! : toType:{typeof(TEntityAttribute).Name}, {this.getTypeName()} - {root_parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityAttributeCastFailed, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
clonedEntityAttribute = origin_entity_attribute.onCloned() as TEntityAttribute;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public virtual ServerErrorCode addEntityAction<TEntityAction>(TEntityAction entityAction)
|
|
where TEntityAction : EntityActionBase
|
|
{
|
|
var error_code = ServerErrorCode.Success;
|
|
var err_msg = string.Empty;
|
|
|
|
if (false == m_entity_actions.TryAdd(entityAction.GetType(), entityAction))
|
|
{
|
|
error_code = ServerErrorCode.EntityActionDuplicated;
|
|
err_msg = $"Failed to TryAdd EntityAction !!!, duplicated EntityAction : {entityAction.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return error_code;
|
|
}
|
|
|
|
return ServerErrorCode.Success;
|
|
}
|
|
|
|
public virtual TEntityAction? getEntityAction<TEntityAction>()
|
|
where TEntityAction : EntityActionBase
|
|
{
|
|
if(false == m_entity_actions.TryGetValue(typeof(TEntityAction), out var found_entity_action))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return found_entity_action as TEntityAction;
|
|
}
|
|
|
|
public virtual Result getEntityAction<TEntityAction>(out TEntityAction? foundEntityAction)
|
|
where TEntityAction : EntityActionBase
|
|
{
|
|
foundEntityAction = null;
|
|
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
var parent = getRootParent();
|
|
|
|
if (false == m_entity_actions.TryGetValue(typeof(TEntityAction), out var found_entity_action))
|
|
{
|
|
err_msg = $"Not found TEntityAction !!! : Type:{typeof(TEntityAction).Name}, {this.getTypeName()} - {parent.toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityActionNotFound, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
foundEntityAction = found_entity_action as TEntityAction;
|
|
return result;
|
|
}
|
|
|
|
public ServerErrorCode upsertRedisPrivateCache<TRedisPrivateCache>(TRedisPrivateCache privateCache)
|
|
where TRedisPrivateCache : RedisRequestPrivateBase
|
|
{
|
|
var error_code = ServerErrorCode.Success;
|
|
var err_msg = string.Empty;
|
|
|
|
if (false == m_redis_private_caches.TryAdd(privateCache.GetType(), privateCache))
|
|
{
|
|
error_code = ServerErrorCode.RedisPrivateCacheDuplicated;
|
|
err_msg = $"Failed to TryAdd RedisRequestPrivateBase !!!, duplicated RedisRequestPrivateBase : {privateCache.toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return error_code;
|
|
}
|
|
|
|
return ServerErrorCode.Success;
|
|
}
|
|
|
|
public TRedisPrivateCache? getRedisPrivateCache<TRedisPrivateCache>()
|
|
where TRedisPrivateCache : RedisRequestPrivateBase
|
|
{
|
|
if (false == m_redis_private_caches.TryGetValue(typeof(TRedisPrivateCache), out var found_redis_cache))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return found_redis_cache as TRedisPrivateCache;
|
|
}
|
|
|
|
public async Task<Result> runTransactionRunnerSafelyWithTransGuid( TRANSACTION_GUID transGuid, TransactionIdType transactionIdType, TRANSACTION_NAME transactionName
|
|
, Func<Task<Result>> fnLogic )
|
|
{
|
|
//Log.getLogger().info($"Call runTransactionRunnerSafelyWithTransGuid !!! - transName:{transactionName}, transGuid:{transGuid}");
|
|
|
|
var task_serializer = this as IWithTaskSerializer;
|
|
NullReferenceCheckHelper.throwIfNull(task_serializer, () => $"task_serializer is null !!! - transName:{transactionName}, {toBasicString()}");
|
|
|
|
var result = new Result();
|
|
|
|
result = await TransactionRunnerHelper.runTransactionRunnerWithLogic( this, transactionIdType, transactionName, fnLogic );
|
|
if(result.getErrorCode() == ServerErrorCode.TransactionRunnerAlreadyRunning)
|
|
{
|
|
var fn_transaction_runner = async delegate ()
|
|
{
|
|
var failed_run_count_by_already_running = 0;
|
|
|
|
while (true)
|
|
{
|
|
await Task.Delay(10);
|
|
|
|
//Log.getLogger().info($"Start fn_transaction_runner async delegate !!! - transName:{transactionName}, transGuid:{transGuid}, {toBasicString()}");
|
|
|
|
result = await TransactionRunnerHelper.runTransactionRunnerWithLogic( this, transGuid, transactionIdType, transactionName, fnLogic );
|
|
if (result.ErrorCode == ServerErrorCode.TransactionRunnerAlreadyRunning)
|
|
{
|
|
failed_run_count_by_already_running++;
|
|
if (30 <= failed_run_count_by_already_running)
|
|
{
|
|
failed_run_count_by_already_running = 0;
|
|
Log.getLogger().error($"continue after Failed to runTransactionRunnerWithLogic() !!! : {result.toBasicString()} - transName:{transactionName}, transGuid:{transGuid}, {toBasicString()}");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
var NotifyPlayer = this as IInitNotifyPacket;
|
|
if(NotifyPlayer != null)
|
|
{
|
|
NotifyPlayer.InitNotifyMessage();
|
|
}
|
|
|
|
Log.getLogger().info($"End fn_transaction_runner async delegate !!! : {result.toBasicString()} - transName:{transactionName}, transGuid:{transGuid}, {toBasicString()}");
|
|
return result;
|
|
}
|
|
};
|
|
|
|
await task_serializer.getTaskSerializer().postLogicFuncWithTransId(transGuid, fn_transaction_runner, transactionName);
|
|
}
|
|
else
|
|
{
|
|
var NotifyPlayer = this as IInitNotifyPacket;
|
|
if (NotifyPlayer != null)
|
|
{
|
|
NotifyPlayer.InitNotifyMessage();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<Result> runTransactionRunnerSafely( TransactionIdType transactionIdType, TRANSACTION_NAME transactionName
|
|
, Func<Task<Result>> fnLogic )
|
|
{
|
|
//Log.getLogger().info($"Call runTransactionRunnerSafely !!! - transName:{transactionName}");
|
|
|
|
var task_serializer = this as IWithTaskSerializer;
|
|
NullReferenceCheckHelper.throwIfNull(task_serializer, () => $"task_serializer is null !!! - transName:{transactionName}, {toBasicString()}");
|
|
|
|
var result = new Result();
|
|
|
|
result = await TransactionRunnerHelper.runTransactionRunnerWithLogic( this, transactionIdType, transactionName, fnLogic );
|
|
if(result.getErrorCode() == ServerErrorCode.TransactionRunnerAlreadyRunning)
|
|
{
|
|
var trans_guid = System.Guid.NewGuid().ToString("N");
|
|
|
|
var fn_transaction_runner = async delegate ()
|
|
{
|
|
var failed_run_count_by_already_running = 0;
|
|
|
|
while (true)
|
|
{
|
|
await Task.Delay(10);
|
|
|
|
//Log.getLogger().info($"Start fn_transaction_runner async delegate !!! - transName:{transactionName}, transGuid:{trans_guid}, {toBasicString()}");
|
|
|
|
result = await TransactionRunnerHelper.runTransactionRunnerWithLogic( this, trans_guid, transactionIdType, transactionName, fnLogic );
|
|
if (result.ErrorCode == ServerErrorCode.TransactionRunnerAlreadyRunning)
|
|
{
|
|
failed_run_count_by_already_running++;
|
|
if(30 <= failed_run_count_by_already_running)
|
|
{
|
|
failed_run_count_by_already_running = 0;
|
|
Log.getLogger().error($"continue after Failed to runTransactionRunnerWithLogic() !!! : {result.toBasicString()} - transName:{transactionName}, transGuid:{trans_guid}, {toBasicString()}");
|
|
}
|
|
continue;
|
|
}
|
|
|
|
var NotifyPlayer = this as IInitNotifyPacket;
|
|
if(NotifyPlayer != null)
|
|
{
|
|
NotifyPlayer.InitNotifyMessage();
|
|
}
|
|
|
|
Log.getLogger().info($"End fn_transaction_runner async delegate !!! : {result.toBasicString()} - transName:{transactionName}, transGuid:{trans_guid}, {toBasicString()}");
|
|
return result;
|
|
}
|
|
};
|
|
|
|
await task_serializer.getTaskSerializer().postLogicFuncWithTransId(trans_guid, fn_transaction_runner, transactionName);
|
|
}
|
|
else
|
|
{
|
|
var NotifyPlayer = this as IInitNotifyPacket;
|
|
if (NotifyPlayer != null)
|
|
{
|
|
NotifyPlayer.InitNotifyMessage();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool isTransacting(TransactionIdType transactionIdType)
|
|
{
|
|
var has_runner = findTransactionRunner(transactionIdType);
|
|
if(null == has_runner)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public Result bindTransactionRunner(TransactionRunner runner)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = string.Empty;
|
|
|
|
if(false == isValidEntityGuid())
|
|
{
|
|
err_msg = $"Failed to isValidEntityGuid() !!! : {runner.toBasicString()} - {toBasicString()}";
|
|
result.setFail(ServerErrorCode.EntityGuidInvalid, err_msg);
|
|
Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
var transaction_id_type = runner.getTransactionIdType();
|
|
if(false == m_transaction_runners.TryAdd(transaction_id_type, runner))
|
|
{
|
|
err_msg = $"Failed to TryAdd() !!!, already running Transaction !!! : {runner.toBasicString()} - {toBasicString()}";
|
|
result.setFail(ServerErrorCode.TransactionRunnerAlreadyRunning, err_msg);
|
|
//Log.getLogger().error(result.toBasicString());
|
|
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public bool unbindTransactionRunner(TransactionRunner runner)
|
|
{
|
|
var err_msg = string.Empty;
|
|
|
|
var transaction_id_type = runner.getTransactionIdType();
|
|
if (false == m_transaction_runners.TryRemove(transaction_id_type, out _))
|
|
{
|
|
err_msg = $"Failed to TryRemove() !!!, not found TransactionRunner : {runner.toBasicString()} - {toBasicString()}";
|
|
Log.getLogger().error(err_msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool unbindTransactionRunnerByTransactionIdType(TransactionIdType idType)
|
|
{
|
|
var transaction_runner = findTransactionRunner(idType);
|
|
NullReferenceCheckHelper.throwIfNull(transaction_runner, () => $"transaction_runner is null !!! : TransactionIdType:{idType} - {toBasicString()}");
|
|
|
|
if(false == unbindTransactionRunner(transaction_runner))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
transaction_runner.clearTransaction();
|
|
|
|
return true;
|
|
}
|
|
|
|
public TransactionRunner? findTransactionRunner(TransactionIdType transactionIdType)
|
|
{
|
|
TransactionRunner? found_transaction_runner = null;
|
|
|
|
var err_msg = string.Empty;
|
|
|
|
if (true == hasMasterGuid())
|
|
{
|
|
var master_guid = getMasterGuid();
|
|
var found_master_entity = onGetMasterEntity();
|
|
if(null == found_master_entity)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
found_transaction_runner = found_master_entity.findTransactionRunner(transactionIdType);
|
|
}
|
|
else
|
|
{
|
|
if (false == m_transaction_runners.TryGetValue(transactionIdType, out found_transaction_runner))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return found_transaction_runner;
|
|
}
|
|
|
|
public virtual (Result, META_ID) onGetMetaIdOfEntityAlertTriggerType(EntityAlertTriggerType triggerType)
|
|
{
|
|
var result = new Result();
|
|
var err_msg = $"NotImplementedException !!! : EntityBase.onGetMetaIdOfEntityAlertTriggerType() : triggerType:{triggerType} - {toBasicString()}";
|
|
result.setFail(ServerErrorCode.FunctionNotImplemented, err_msg);
|
|
|
|
return (result, 0);
|
|
}
|
|
|
|
public bool hasSummenedEntityGuid(SUMMENED_ENTITY_GUID toCheckGuid)
|
|
{
|
|
return m_summoned_entities.Contains(toCheckGuid);
|
|
}
|
|
|
|
public bool attachSummenedEntityGuid(SUMMENED_ENTITY_GUID summenedEntityGuid)
|
|
{
|
|
return m_summoned_entities.TryAdd(summenedEntityGuid, out _);
|
|
}
|
|
public bool detachSummenedEntityGuid(SUMMENED_ENTITY_GUID summenedEntityGuid)
|
|
{
|
|
return m_summoned_entities.TryRemove(summenedEntityGuid, out _);
|
|
}
|
|
|
|
public virtual Type onGetAvailableItemAttributeType()
|
|
{
|
|
var err_msg = $"NotImplementedException !!! : EntityBase.onGetAvailableItemAttributeType() - {toBasicString()}";
|
|
throw new NotImplementedException(err_msg);
|
|
}
|
|
|
|
public virtual EntityBase? onGetMasterEntity()
|
|
{
|
|
var err_msg = $"NotImplementedException !!! : EntityBase.onGetMasterEntity() - {toBasicString()}";
|
|
throw new NotImplementedException(err_msg);
|
|
}
|
|
|
|
public bool attachMasterGuid(MASTER_GUID masterGuid)
|
|
{
|
|
if(true == hasMasterGuid())
|
|
{
|
|
if(m_master_guid != masterGuid)
|
|
{
|
|
Log.getLogger().error($"Not matched MasterGuid !!! : currMasterGuid:{m_master_guid} == reqMasterGuid:{masterGuid} - {toBasicString()}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_master_guid = masterGuid;
|
|
return true;
|
|
}
|
|
|
|
public void detachMasterGuid()
|
|
{
|
|
m_master_guid = string.Empty;
|
|
}
|
|
|
|
public bool hasRootParent()
|
|
{
|
|
if (null == m_parent_nullable)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return m_parent_nullable.hasRootParent();
|
|
}
|
|
|
|
public EntityBase getRootParent()
|
|
{
|
|
if( null == m_parent_nullable )
|
|
{
|
|
return this;
|
|
}
|
|
|
|
return m_parent_nullable.getRootParent();
|
|
}
|
|
|
|
public bool isValidEntityGuid()
|
|
{
|
|
if(true == getEntityGuid().isNullOrWhiteSpace())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#region OwnerEntityType 및 OwnerEntityType의 Guid 처리를 위한 함수들
|
|
|
|
//=================================================================================================
|
|
// 엔티티에 OwnerEntityType의 Guid 설정 여부를 반환해 준다.
|
|
//=================================================================================================
|
|
public bool isValidGuidOfOwnerEntityType()
|
|
{
|
|
if (true == onGetGuidOfOwnerEntityType().isNullOrWhiteSpace())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
// 엔티티에 OwnerEntityType 설정 여부를 반환해 준다.
|
|
//=================================================================================================
|
|
public bool isValidOwnerEntityType()
|
|
{
|
|
if (onGetOwnerEntityType() == OwnerEntityType.None)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
// 엔티티에 OwnerEntityType 및 OwnerEntityType의 Guid 설정 여부를 반환해 준다.
|
|
//=================================================================================================
|
|
public bool isValidOwnerEntityTypeWithGuid()
|
|
{
|
|
if( false == isValidOwnerEntityType()
|
|
|| false == isValidGuidOfOwnerEntityType() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=================================================================================================
|
|
// 엔티티에 설정된 OwnerEntityType에 해당하는 OWNER_GUID를 반환해 준다.
|
|
//=================================================================================================
|
|
public virtual OWNER_GUID onGetGuidOfOwnerEntityType()
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
//=================================================================================================
|
|
// 엔티티에 설정된 OwnerEntityType을 반환해 준다.
|
|
//=================================================================================================
|
|
public virtual OwnerEntityType onGetOwnerEntityType()
|
|
{
|
|
return OwnerEntityType.None;
|
|
}
|
|
|
|
#endregion OwnerEntityType 및 OwnerEntityType의 Guid 처리를 위한 함수들
|
|
|
|
|
|
#region DB와 관련된 함수들
|
|
|
|
//=================================================================================================
|
|
// 엔티티의 Db에 저장할 DB_GUID를 반환해 준다.
|
|
//=================================================================================================
|
|
public virtual DB_GUID onGetDbGuid()
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
#endregion DB와 관련된 함수들
|
|
|
|
|
|
#region 로그와 관련 함수들
|
|
|
|
//=========================================================================================
|
|
// 개발 로그 상태 관련 출력용 가상 함수
|
|
// 함수 내부에서 TEntityAttribute에 접속할 경우 getOriginEntityAttribute<TEntityAttribute>() or getOriginEntityAttribute() 함수를 사용해야 한다.
|
|
//=========================================================================================
|
|
public virtual string toStateString()
|
|
{
|
|
return $"State:Empty";
|
|
}
|
|
|
|
//=========================================================================================
|
|
// 개발 로그 세부 출력용 가상 함수
|
|
// 함수 내부에서 TEntityAttribute에 접속할 경우 getOriginEntityAttribute<TEntityAttribute>() or getOriginEntityAttribute() 함수를 사용해야 한다.
|
|
//=========================================================================================
|
|
public virtual string toSummaryString()
|
|
{
|
|
return $"MasterGuid:{getMasterGuid()}, SummonedCount:{getSummenedEntityGuids().Count}";
|
|
}
|
|
//=========================================================================================
|
|
// 개발 로그 기본 출력용 가상 함수
|
|
// 함수 내부에서 TEntityAttribute에 접속할 경우 getOriginEntityAttribute<TEntityAttribute>() or getOriginEntityAttribute() 함수를 사용해야 한다.
|
|
//=========================================================================================
|
|
public virtual string toBasicString()
|
|
{
|
|
return $"EntityType:{getEntityType()}, ClassName:{this.getTypeName()}";
|
|
}
|
|
|
|
public virtual string toBasicStringWithMaster()
|
|
{
|
|
if(true == hasMasterGuid())
|
|
{
|
|
var found_master = onGetMasterEntity();
|
|
var master_basic_string = found_master != null ? found_master.toBasicString() : "Not found Master !!!";
|
|
return $"{toBasicString()}, MasterGuid:{getMasterGuid()}, SummonedCount:{getSummenedEntityGuids().Count} - {master_basic_string}";
|
|
}
|
|
else
|
|
{
|
|
return $"{toBasicString()}";
|
|
}
|
|
}
|
|
|
|
#endregion 로그와 관련 함수들
|
|
}
|