Files
caliverse_server/ServerBase/Entity/EntityBase.cs
2025-05-01 07:20:41 +09:00

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
}