using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using ServerCore; using ServerBase; using SESSION_ID = System.Int32; using WORLD_ID = System.UInt32; using META_ID = System.UInt32; using ENTITY_GUID = System.String; using ACCOUNT_ID = System.String; using OWNER_GUID = System.String; using USER_GUID = System.String; using CHARACTER_GUID = System.String; using ITEM_GUID = System.String; using ServerCommon.BusinessLogDomain; using ServerControlCenter; namespace ServerCommon; public abstract class ItemAttributeBase : EntityAttributeBase, ICopyEntityAttributeFromDoc, IMergeWithEntityAttribute, IWithCommonResultFiller { [JsonProperty] public ITEM_GUID ItemGuid { get; set; } = string.Empty; [JsonProperty] public META_ID ItemMetaId { get; set; } = 0; [JsonProperty] public UInt16 ItemStackCount { get { return m_item_stack_count; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.ItemStackCount, value - m_item_stack_count, value); } m_item_stack_count = value; } } private UInt16 m_item_stack_count = 0; [JsonProperty] public UInt16 Level { get { return m_level; } set { var recorder = getEntityRecorder(); if (null != recorder) { recorder.applyDeltaCounter(EntityDeltaType.ItemLevel, value - m_level, value); } m_level = value; } } private UInt16 m_level = 0; public List Attributes { get; set; } = new(); // EquipedIvenType.None 일경우 EquipedPos = 0 이다 !!! - kangms [JsonProperty] public InvenEquipType EquipedIvenType { get; set; } = InvenEquipType.None; [JsonProperty] public UInt16 EquipedPos { get; set; } = 0; //========================================================================================= // 기본 생성자 //========================================================================================= public ItemAttributeBase(EntityBase owner, EntityBase entityOfOwnerEntityType) : base(owner, entityOfOwnerEntityType, true) { } public override void newEntityAttribute() { ItemGuid = System.Guid.NewGuid().ToString("N"); base.newEntityAttribute(); } public void newEntityAttributeWithItemGuid(string itemGuid) { ItemGuid = itemGuid; base.newEntityAttribute(); } public override void onClear() { ItemGuid = string.Empty; ItemMetaId = 0; ItemStackCount = 0; Level = 0; Attributes.Clear(); EquipedIvenType = InvenEquipType.None; EquipedPos = 0; getAttributeState().reset(); } public override async Task<(Result, DynamoDbDocBase?)> toDocBase(bool isForQuery = true) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {owner.toBasicString()}"); var result = new Result(); //===================================================================================== // Attribute => Try Pending Item Doc (New Doc) // Origin Doc Timestamps => Try Pending Item Doc (New Doc) //===================================================================================== var try_pending_item_doc = getTryPendingDocBase() as ItemDoc; if(null == try_pending_item_doc) { var to_copy_doc = onCreateDocBase() as ItemDoc; NullReferenceCheckHelper.throwIfNull(to_copy_doc, () => $"to_copy_doc is null !!! - {owner.toBasicString()}, {root_parent.toBasicString()}"); var origin_doc = getOriginDocBase(); if(null != origin_doc) { to_copy_doc.copyTimestampsFromOriginDocBase(origin_doc); } try_pending_item_doc = to_copy_doc; setTryPendingDocBase(try_pending_item_doc); } var to_copy_doc_item_attrib = try_pending_item_doc.getAttrib(); ArgumentNullReferenceCheckHelper.throwIfNull(to_copy_doc_item_attrib, () => $"to_copy_doc_item_attrib is null !!! - {owner.toBasicString()}, {root_parent.toBasicString()}"); to_copy_doc_item_attrib.ItemGuid = ItemGuid; to_copy_doc_item_attrib.ItemMetaId = ItemMetaId; to_copy_doc_item_attrib.ItemStackCount = ItemStackCount; to_copy_doc_item_attrib.Level = Level; to_copy_doc_item_attrib.Attributes = Attributes.Select(x => x).ToList(); to_copy_doc_item_attrib.EquipedIvenType = EquipedIvenType; to_copy_doc_item_attrib.EquipedPos = EquipedPos; if (false == isForQuery) { return (result, try_pending_item_doc); } //===================================================================================== // Doc QueryType 반영 //===================================================================================== (result, var to_query_doc) = await applyDoc4Query(try_pending_item_doc); if (result.isFail()) { return (result, null); } return (result, to_query_doc); } public async Task<(Result, ItemDoc?)> changeOwnerAndCreateItemDoc(OwnerEntityType toSetOwnerEntityType, OWNER_GUID toSetOwerGuid) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {owner.toBasicString()}"); var result = new Result(); var item_doc = new ItemDoc(toSetOwnerEntityType, toSetOwerGuid, ItemGuid); result = await ServerBase.DataCopyHelper.copyDocFromEntityAttributes(item_doc, new List() { this }); if (result.isFail()) { var err_msg = $"Failed to copyDocFromEntityAttributes() !!!, to:{item_doc.getTypeName()}, from:{this.getTypeName()} - {owner.toBasicString()}, {root_parent.toBasicString()}"; result.setFail(ServerErrorCode.EntityAttributeCopyToDynamoDbDocFailed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } return (result, item_doc); } public void onFillCommonResult(EntityCommonResult commonResult, EntityAttributeBase origin, QueryBatchBase? queryBatch = null) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var before = origin as ItemAttributeBase; NullReferenceCheckHelper.throwIfNull(before, () => $"before is null !!! - {owner.toBasicString()}"); var after = this as ItemAttributeBase; NullReferenceCheckHelper.throwIfNull(after, () => $"after is null !!! - {owner.toBasicString()}"); var recorder = after.getEntityRecorder(); NullReferenceCheckHelper.throwIfNull(recorder, () => $"recorder is null !!! - {owner.toBasicString()}"); var item_result = commonResult.Item; NullReferenceCheckHelper.throwIfNull(item_result, () => $"item_result is null !!! - {owner.toBasicString()}"); var exp_result = commonResult.Exp; NullReferenceCheckHelper.throwIfNull(exp_result, () => $"exp_result is null !!! - {owner.toBasicString()}"); var item_guid = after.ItemGuid; var item_meta_id = after.ItemMetaId; //===================================================================================== // 아이템 개수가 남아있는 경우에만 UpdatedItems 목록에 추가 - 클라이언트 요청 반영 - kangms //===================================================================================== if (after.ItemStackCount > 0) { item_result.UpdatedItems.TryAdd(item_guid, after.toItemData4Client()); } var target_types = EnumHelper.getValuesBeginEndBetweenWord("Item_"); foreach (var type in target_types) { var found_delta_counter = recorder.findDeltaCounter(type); if (null != found_delta_counter) { (var item_log_result, var item_log_info) = after.toItemLogInfo(); if (item_log_result.isFail()) { var err_msg = $"Failed to toItemLogInfo() !!! : {item_log_result.toBasicString()} - {owner.toBasicString()}"; Log.getLogger().error(err_msg); continue; } NullReferenceCheckHelper.throwIfNull(item_log_info, () => $"item_log_info is null !!! - {owner.toBasicString()}"); //===================================================================================== // 아이템 갯수 변화량 //===================================================================================== if (EntityDeltaType.ItemStackCount == type) { var delta_count = (Int32)found_delta_counter.getDeltaCount(); item_result.DeltaPerItems.TryAdd(item_guid, delta_count); if (false == item_result.DeltaPerMeta.TryGetValue(item_meta_id, out var found_item_delta)) { found_item_delta = new ItemDeltaAmount(); found_item_delta.DeltaType = (delta_count >= 0 ? AmountDeltaType.Acquire : AmountDeltaType.Consume); found_item_delta.Delta = new ItemAmount(); found_item_delta.Delta.MetaId = item_meta_id; item_result.DeltaPerMeta.Add(item_meta_id, found_item_delta); } else { found_item_delta.DeltaType = AmountDeltaType.Merge; } found_item_delta.Delta.Amount += delta_count; appendOrWriteBusinessLog4ItemStackCount(item_log_info, delta_count, after.ItemStackCount, queryBatch); } //===================================================================================== // 아이템 레벨 변화량 //===================================================================================== else if (EntityDeltaType.ItemLevel == type) { var delta_level = (Int32)found_delta_counter.getDeltaCount(); var level_exp_type = (Int32)LevelExpType.Item; if (false == exp_result.LevelExps.TryGetValue(level_exp_type, out var found_level_by_meta_id)) { found_level_by_meta_id = new LevelExpById(); exp_result.LevelExps.Add(level_exp_type, found_level_by_meta_id); } if (false == found_level_by_meta_id.LevelExpsByGuid.TryGetValue(item_guid, out var found_level)) { found_level = new LevelExp(); found_level_by_meta_id.LevelExpsByGuid.Add(item_guid, found_level); } found_level.Level = after.Level; if (false == exp_result.LevelExpDeltas.TryGetValue(level_exp_type, out var found_exp_delta_amount_by_meta_id)) { found_exp_delta_amount_by_meta_id = new LevelExpDeltaAmountById(); exp_result.LevelExpDeltas.Add(level_exp_type, found_exp_delta_amount_by_meta_id); } if (false == found_exp_delta_amount_by_meta_id.DeltasByGuid.TryGetValue(item_guid, out var found_exp_delta_amount)) { found_exp_delta_amount = new LevelExpDeltaAmount(); found_exp_delta_amount_by_meta_id.DeltasByGuid.Add(item_guid, found_exp_delta_amount); } found_exp_delta_amount.ExpDeltaType = AmountDeltaType.Merge; found_exp_delta_amount.LevelAmount = delta_level; appendOrWriteBusinessLog4ItemLevel(item_log_info, delta_level, after.Level, queryBatch); } } } } private void appendOrWriteBusinessLog4ItemStackCount( ItemLogInfo itemLogInfo , int deltaCount, int updatedCount , QueryBatchBase? queryBatch = null ) { var owner = getOwner(); var delta_type = CountDeltaType.None; if (deltaCount > 0) { delta_type = CountDeltaType.Acquire; } else if (deltaCount < 0) { delta_type = CountDeltaType.Consume; } itemLogInfo.CountDeltaType = delta_type; itemLogInfo.DeltaCount = deltaCount; itemLogInfo.StackCount = updatedCount; var item_delta_log = new ItemBusinessLog(itemLogInfo); if (null != queryBatch) { queryBatch.appendBusinessLog(item_delta_log); } else { var log_actor = owner as IWithLogActor; NullReferenceCheckHelper.throwIfNull(log_actor, () => $"log_actor is null !!! - {owner.toBasicString()}"); BusinessLogger.collectLog(log_actor, item_delta_log); } } private void appendOrWriteBusinessLog4ItemLevel( ItemLogInfo itemLogInfo, int deltaLevel, int updatedLevel , QueryBatchBase? queryBatch = null ) { var owner = getOwner(); var delta_type = CountDeltaType.None; if (deltaLevel > 0) { delta_type = CountDeltaType.Acquire; } else if (deltaLevel < 0) { delta_type = CountDeltaType.Consume; } itemLogInfo.CountDeltaType = delta_type; itemLogInfo.DeltaLevel = deltaLevel; itemLogInfo.Level = updatedLevel; var item_delta_log = new ItemBusinessLog(itemLogInfo); if (null != queryBatch) { queryBatch.appendBusinessLog(item_delta_log); } else { var log_actor = owner as IWithLogActor; NullReferenceCheckHelper.throwIfNull(log_actor, () => $"log_actor is null !!! - {owner.toBasicString()}"); BusinessLogger.collectLog(log_actor, item_delta_log); } } public virtual Result onMerge(EntityAttributeBase otherEntityAttribute) { var owner = getOwner(); NullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {owner.toBasicString()}"); ArgumentNullReferenceCheckHelper.throwIfNull(otherEntityAttribute, () => $"otherEntityAttribute is null !!! - {root_parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; //===================================================================================== // OtherAttribute => Attribute //===================================================================================== var other_item_attribute = otherEntityAttribute as ItemAttributeBase; if (null == other_item_attribute) { err_msg = $"Failed to cast ItemAttributeBase !!!, other_item_attribute is null - {owner.toBasicString()}, {root_parent.toBasicString()}"; result.setFail(ServerErrorCode.ClassTypeCastIsNull, err_msg); Log.getLogger().error(result.toBasicString()); return result; } ItemGuid = other_item_attribute.ItemGuid; ItemMetaId = other_item_attribute.ItemMetaId; ItemStackCount = other_item_attribute.ItemStackCount; Level = other_item_attribute.Level; Attributes = other_item_attribute.Attributes.Select(x => x).ToList(); EquipedIvenType = other_item_attribute.EquipedIvenType; EquipedPos = other_item_attribute.EquipedPos; //===================================================================================== // Attribute Try Pending Item Doc => Origin Doc //===================================================================================== var try_pending_item_doc = other_item_attribute.getTryPendingDocBase() as ItemDoc; if(null != try_pending_item_doc) { other_item_attribute.resetTryPendingDocBase(); syncOriginDocBaseWithNewDoc(try_pending_item_doc); } var origin_doc_base = getOriginDocBase(); if (null == origin_doc_base) { // DB 에 저장되어 있지 않는 경우 OriginDoc은 null 이다 !!! return result; } var item_attrib = origin_doc_base.getAttrib(); NullReferenceCheckHelper.throwIfNull(item_attrib, () => $"item_attrib is null !!! - {owner.toBasicString()}, {root_parent.toBasicString()}"); item_attrib.ItemGuid = ItemGuid; item_attrib.ItemMetaId = ItemMetaId; item_attrib.ItemStackCount = ItemStackCount; item_attrib.Level = Level; item_attrib.Attributes = Attributes.Select(x => x).ToList(); ; item_attrib.EquipedIvenType = EquipedIvenType; item_attrib.EquipedPos = EquipedPos; return result; } public virtual bool copyEntityAttributeFromDoc(DynamoDbDocBase docBase) { var owner = getOwner(); ArgumentNullReferenceCheckHelper.throwIfNull(owner, () => $"owner is null !!!"); var root_parent = owner.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {owner.toBasicString()}"); var err_msg = string.Empty; var to_cast_string = typeof(ItemDoc).Name; var item_doc = docBase as ItemDoc; if (null == item_doc) { err_msg = $"item_doc is null !!!, in copyEntityAttributeFromDoc() : docName:{to_cast_string} - {owner.toBasicString()}, {root_parent.toBasicString()}"; Log.getLogger().error(err_msg); return false; } //===================================================================================== // New Doc => Origin Doc //===================================================================================== syncOriginDocBaseWithNewDoc(item_doc); //===================================================================================== // Origin Doc => Attribute //===================================================================================== var item_attrib = item_doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(item_attrib, () => $"item_attrib is null !!! - {owner.toBasicString()}, {root_parent.toBasicString()}"); ItemGuid = item_attrib.ItemGuid; ItemMetaId = item_attrib.ItemMetaId; ItemStackCount = item_attrib.ItemStackCount; Level = item_attrib.Level; Attributes = item_attrib.Attributes.Select(x => x).ToList(); EquipedIvenType = item_attrib.EquipedIvenType; EquipedPos = item_attrib.EquipedPos; return true; } } public class ItemAttributeBaseTransactor : EntityAttributeTransactorBase where TItemAttributeBase : ItemAttributeBase { public ItemAttributeBaseTransactor(EntityBase owner) : base(owner) { } }