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 m_entity_attributes = new(); private readonly ConcurrentDictionary m_entity_actions = new(); //private EntityHFSMBase? m_entity_hfsm_base_nullable; // TODO kangms : 추후 팀장님 수정. private readonly ConcurrentDictionary m_redis_private_caches = new(); private readonly ConcurrentDictionary m_transaction_runners = new(); private MASTER_GUID m_master_guid = string.Empty; // MASTER_GUID는 변하지 않는 고유 Id로 설정 해야 한다. !!! private readonly ConcurrentHashSet 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 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 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() 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(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() 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() 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(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() 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(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() 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(); if(null != origin_doc_base) { cloned_entity_attribute.syncOriginDocBaseWithNewDoc(origin_doc_base); } return cloned_entity_attribute; } /////////////////////////////////////////////////////////////////////////////////////////// // 등록된 TEntityAttribute 정보를 onCloned()을 호출하여 복제된 TEntityAttribute 반환해 준다. // TransactionRunner에 관리되지 않는다 !!! /////////////////////////////////////////////////////////////////////////////////////////// public virtual Result getClonedEntityAttribute(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 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() 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(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 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() 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 runTransactionRunnerSafelyWithTransGuid( TRANSACTION_GUID transGuid, TransactionIdType transactionIdType, TRANSACTION_NAME transactionName , Func> 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 runTransactionRunnerSafely( TransactionIdType transactionIdType, TRANSACTION_NAME transactionName , Func> 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() or getOriginEntityAttribute() 함수를 사용해야 한다. //========================================================================================= public virtual string toStateString() { return $"State:Empty"; } //========================================================================================= // 개발 로그 세부 출력용 가상 함수 // 함수 내부에서 TEntityAttribute에 접속할 경우 getOriginEntityAttribute() or getOriginEntityAttribute() 함수를 사용해야 한다. //========================================================================================= public virtual string toSummaryString() { return $"MasterGuid:{getMasterGuid()}, SummonedCount:{getSummenedEntityGuids().Count}"; } //========================================================================================= // 개발 로그 기본 출력용 가상 함수 // 함수 내부에서 TEntityAttribute에 접속할 경우 getOriginEntityAttribute() 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 로그와 관련 함수들 }