using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ServerCore; using ServerBase; using MetaAssets; 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 Google.Protobuf.WellKnownTypes; namespace ServerCommon; public abstract class InventoryBase : SlotsWrapperBase>, IWithInventoryAccessor where TSlotType : notnull { public InventoryBase(EntityType entityType, EntityBase parent) : base(entityType, parent) { } public virtual Result onTryTakeInEntityBase(TSlotType entityGuid, EntityBase toTakeInEntityBase) { var err_msg = string.Empty; var result = tryIsEquipableOrEquipWithEntityBase(entityGuid, toTakeInEntityBase); if (result.isFail()) { err_msg = $"Failed to onTryIsEquipableOrEquipWithEntityBase() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } return result; } public virtual Result onTryTakeOutEnityBase(ENTITY_GUID entityGuid, out EntityBase? takenOutEntityBase) { var err_msg = string.Empty; takenOutEntityBase = null; var result = tryUnequipableOrUnequipWithEntityBase(entityGuid, out var taken_out_entity_base); if (result.isFail()) { err_msg = $"Failed to onTryUnequipableOrUnequipWithEntityBase() !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return result; } takenOutEntityBase = taken_out_entity_base; return result; } public (Result, List, UInt16) findItemAttributeBaseAllByMetaId(META_ID itemMetaId, UInt32 toCheckItemCount = UInt32.MaxValue) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"paren is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var err_msg = string.Empty; var result = new Result(); var target_item_attributes = new List(); if (0 >= itemMetaId) { err_msg = $"Invalid ItemMetaId !!! : itemMetaId:{itemMetaId} > 0 - {parent.toBasicString()}"; result.setFail(ServerErrorCode.MetaIdInvalid, err_msg); Log.getLogger().error(result.toBasicString()); return (result, target_item_attributes, 0); } // 1. 아이템 메타 정보를 얻는다. if (false == MetaData.Instance._ItemTable.TryGetValue((int)itemMetaId, out var itemMetaData)) { err_msg = $"Not found ItemMeta !!! : itemMetaId:{itemMetaId} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, target_item_attributes, 0); } // 2. 동일한 종류의 현재 Origin ItemAttributeBase 목록을 얻는다. var found_origin_item_attributes = getHasSlotsWithEntityBases().Where(x => { var item_attribute_base = x.Value.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute_base, () => $"item_attribute_base is null !!! - {parent.toBasicString()}"); if ( item_attribute_base.ItemMetaId == itemMetaId && item_attribute_base.ItemStackCount > 0) { return true; } return false; }).Select(x => { var item_attribute_base = x.Value.getOriginEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute_base, () => $"item_attribute_base is null !!! - {parent.toBasicString()}"); return (item_attribute_base.ItemGuid, item_attribute_base); }).ToDictionary(); // 3. 동일한 종류의 예약된 Cloned ItemAttributeBase 목록을 얻는다. var found_cloned_item_attributes = new Dictionary(); var found_transaction_runner = parent.findTransactionRunner(TransactionIdType.PrivateContents); if (null != found_transaction_runner) { var entity_attribute_transactors = found_transaction_runner.getEntityAttributeTransactorAll(parent.onGetAvailableItemAttributeType()); found_cloned_item_attributes = entity_attribute_transactors.Where(x => { var item_attribute_base = x.Value.getClonedEntityAttribute() as ItemAttributeBase; NullReferenceCheckHelper.throwIfNull(item_attribute_base, () => $"item_attribute_base is null !!! - {parent.toBasicString()}"); if ( item_attribute_base.ItemMetaId == itemMetaId && item_attribute_base.ItemStackCount >= 0 ) { return true; } return false; }).Select(x => { var item_attribute = x.Value.getClonedEntityAttribute() as ItemAttributeBase; NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); return (item_attribute.ItemGuid, item_attribute); }).ToDictionary(); } // 4. Cloned 정보가 있거나, Origin 정보만 있는 경우 대상 아이템으로 추가한다 !!! var all_keys = found_origin_item_attributes.Keys.Concat(found_cloned_item_attributes.Keys).Distinct(); foreach (var key in all_keys) { // 4.1. Cloned 정보가 있는데, 삭제 대상일 경우는 Origin & Cloned 모두 무시 한다. if(true == found_cloned_item_attributes.TryGetValue(key, out var found_cloned_attribute)) { var stack_count = found_cloned_attribute.ItemStackCount; if (0 >= stack_count) { // 4.1.1. Cloned 정보가 삭제 대상일 경우 추가하지 않는다. (삭제될거니까...) continue; } else { // 4.1.2. Cloned 정보의 아이템 개수가 존재 한다면 추가 한다. target_item_attributes.Add(found_cloned_attribute); } } // 4.2. Origin 정보만 있는 경우 추가 한다. else if (found_origin_item_attributes.ContainsKey(key) && false == found_cloned_item_attributes.ContainsKey(key)) { var only_origin_item_attribute = found_origin_item_attributes[key]; target_item_attributes.Add(only_origin_item_attribute); } } // 5. 조건에 총족된 대상 아이템들의 StackCount 합산 개수를 얻는다. var item_has_count = target_item_attributes.Sum(x => { return x.ItemStackCount; }); return (result, target_item_attributes, (UInt16)item_has_count); } public EntityBase? findEntityBase(TSlotType toFindEntityBaseKey) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var found_entity_base = inven_data.findEntityBaseInSlotType(toFindEntityBaseKey); if (null == found_entity_base) { return null; } return found_entity_base; } public override void onWriteLog() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var has_items = getHasSlotsWithEntityBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); foreach (var each in has_items) { var slot_type = each.Key; META_ID item_meta_id = 0; var item_guid = string.Empty; var item_stack_count = 0; var item_max_stack_count = 0; var item_base = each.Value as ItemBase; if (null != item_base) { var item_attibute_base = item_base.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attibute_base, () => $"item_attibute_base is null !!!"); item_meta_id = (META_ID)item_attibute_base.ItemMetaId; item_guid = item_attibute_base.ItemGuid; item_stack_count = item_attibute_base.ItemStackCount; if (true == MetaData.Instance._ItemTable.TryGetValue((int)item_meta_id, out var found_item_meta)) { item_max_stack_count = found_item_meta.StackMaxCount; } } var err_msg = $"SlotType:{slot_type}, ItemMetaId:{item_meta_id}, StackCount:{item_stack_count}, MaxStackCount:{item_max_stack_count}, ItemGuid:{item_guid}"; Log.getLogger().info(err_msg); } } public ConcurrentDictionary getHasSlotsWithEntityBases() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var slots = inven_data.getSlots() as ConcurrentDictionary; NullReferenceCheckHelper.throwIfNull(slots, () => $"slots is null !!! - {parent.toBasicString()}"); return slots; } public List getHasEntityBases() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var slots_with_entity_bases = getHasSlotsWithEntityBases(); NullReferenceCheckHelper.throwIfNull(slots_with_entity_bases, () => $"slots_with_entity_bases is null !!! - {parent.toBasicString()}"); return slots_with_entity_bases.Select(x => x.Value).ToList(); } // IWithInventoryAccessor.clearItemAll() public void clearItemAll() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"paren is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var slots = inven_data.getSlots() as ConcurrentDictionary; NullReferenceCheckHelper.throwIfNull(slots, () => $"slots is null !!! - {parent.toBasicString()}"); slots.Clear(); } // IWithInventoryAccessor.getHasItemBases() public List getHasItemBases() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); return getHasSlotsWithEntityBases().Select(x => { var item_base = x.Value as ItemBase; NullReferenceCheckHelper.throwIfNull(item_base, () => $"item_base is null !!! - {parent.toBasicString()}"); return item_base; }).ToList(); } // IWithInventoryAccessor.tryEquipWithItemBase() public ServerErrorCode tryEquipWithItemBase(string slotType, ItemBase itemBase) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var err_msg = string.Empty; if(false == slotType.fillupEnumTypeAndValueStringToEnum("None", out var target_slot_type) || null == target_slot_type) { err_msg = $"Failed to fillupEnumTypeAndValueStringToEnum() !!!, SlotType of Target Inventory : targetSlotType:{slotType} - {parent.toBasicString()}"; Log.getLogger().error(err_msg); return ServerErrorCode.StringConvertToEnumFailed; } return inven_data.onTryEquipWithEntityBase(target_slot_type, itemBase); } // IWithInventoryAccessor.tryUnequipWithItemBase() public ServerErrorCode tryUnequipWithItemBase(string slotType, out ItemBase? itemBase) { itemBase = null; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inven_data = getData(); NullReferenceCheckHelper.throwIfNull(inven_data, () => $"inven_data is null !!! - {parent.toBasicString()}"); var err_msg = string.Empty; if (false == slotType.fillupEnumTypeAndValueStringToEnum("None", out var target_slot_type) || null == target_slot_type) { err_msg = $"Failed to fillupEnumTypeAndValueStringToEnum() !!!, SlotType of Target Inventory : targetSlotType:{slotType} - {parent.toBasicString()}"; Log.getLogger().error(err_msg); return ServerErrorCode.StringConvertToEnumFailed; } var result_code = inven_data.onTryUnequipWithEntityBase(target_slot_type, out EntityBase? entity_base); if (ServerErrorCode.Success == result_code) { itemBase = entity_base as ItemBase; } return result_code; } }