using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Amazon.Runtime.Internal.Transform; using ServerCore; using ServerBase; using ServerCommon; using SESSION_ID = System.Int32; using WORLD_META_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 Amazon.S3.Model; using MetaAssets; using Nettention.Proud; namespace GameServer { public class BagTab { private readonly BagTabType m_bag_tab_type; private InventoryBase m_owner; private readonly string m_usable_inven_bag_name; public UInt16 TakeInItemCount { get; private set; } = 0; public BagTab(InventoryBase owner, BagTabType bagTabType) { m_owner = owner; m_bag_tab_type = bagTabType; m_usable_inven_bag_name = bagTabType.toUsableInvenBagName(); } public bool tryCheckTakableInItem(Int16 toIncCount, Int16 reservedCount = 0) { if( getSlotMaxCount() < (TakeInItemCount + reservedCount + toIncCount) ) { return false; } return true; } public bool tryTakableInItem() { if (getSlotMaxCount() <= TakeInItemCount) { return false; } TakeInItemCount += 1; return true; } public bool tryCheckTakableOutItem(Int16 toDecCount, Int16 reservedCount = 0) { if ( 0 > (TakeInItemCount + reservedCount - toDecCount) ) { return false; } return true; } public bool tryTakableOutItem() { if (0 >= TakeInItemCount) { return false; } TakeInItemCount -= 1; return true; } public bool onMergeTakeInItemCount(Int16 reservedCount) { var inventory_base = getInventoryBase(); NullReferenceCheckHelper.throwIfNull(inventory_base, () => $"inventory_base is null !!!"); var root_parent = inventory_base.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {inventory_base.toBasicString()}"); var to_update_count = TakeInItemCount + reservedCount; if (to_update_count < 0 || InventoryRuleHelper.getBagTapMaxSlotCountByBagTabType(root_parent.getEntityType(), getBagTabType()) < to_update_count) { return false; } TakeInItemCount = (UInt16)to_update_count; return true; } public BagTabType getBagTabType() => m_bag_tab_type; public Int32 getSlotMaxCount() { var inventory_base = getInventoryBase(); NullReferenceCheckHelper.throwIfNull(inventory_base, () => $"inventory_base is null !!!"); var root_parent = inventory_base.getRootParent(); NullReferenceCheckHelper.throwIfNull(root_parent, () => $"root_parent is null !!! - {inventory_base.toBasicString()}"); return InventoryRuleHelper.getBagTapMaxSlotCountByBagTabType(root_parent.getEntityType(), getBagTabType()); } public void resetItemCount() { TakeInItemCount = 0; } public InventoryBase getInventoryBase() => m_owner; public string toBasicString() { return $"{this.getTypeName()}, UsableInvenBagName:{m_usable_inven_bag_name}"; } } public class BagInven : InventoryBase { // BagTabType에 장착되어 아이템 있는 개수 private ConcurrentDictionary m_bag_tab_take_in_counts = new(); public BagInven(EntityBase owner) : base(EntityType.BagInven, owner) { base.onInitSlots(ServerCommon.Constant.BagSlotDefaultMaxCount); } public override async Task onInit() { await Task.CompletedTask; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var result = new Result(); var bag_tab_types = EnumHelper.getValuesWithoutScopeAll(); foreach (var bag_tab in bag_tab_types) { m_bag_tab_take_in_counts.TryAdd(bag_tab, new BagTab(this, bag_tab)); } return result; } public void resetItemCountInTabs() { foreach(var each in m_bag_tab_take_in_counts) { var bag_tab = each.Value; bag_tab.resetItemCount(); } } public async Task<(Result, List)> tryTakableInItemBase(META_ID toTakeInItemMetaId, UInt16 toTakeInCount) { await Task.CompletedTask; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var bag_item_data = getData(); NullReferenceCheckHelper.throwIfNull(bag_item_data, () => $"bag_item_data is null !!! - {parent.toBasicString()}"); var result = new Result(); var reserved_items = new List(); var err_msg = string.Empty; // 1. 아이템 메타 정보를 얻는다. if (false == MetaData.Instance._ItemTable.TryGetValue((int)toTakeInItemMetaId, out var itemMetaData)) { err_msg = $"Not found ItemMeta !!! : itemMetaId:{toTakeInItemMetaId} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, reserved_items); } if(true == itemMetaData.IsUiOnly) { err_msg = $"Returned without creation Item !!!, because UI-only item : itemMetaId:{toTakeInItemMetaId} - {parent.toBasicString()}"; Log.getLogger().debug(err_msg); return (result, reserved_items); } // 2. 동일한 종류의 ItemAttributeBase 목록과 보유 개수를 얻는다. (result, var target_item_attributes, var item_has_count) = findItemAttributeBaseAllByMetaId(toTakeInItemMetaId, toTakeInCount); if (result.isFail()) { return (result, reserved_items); } // 3. 해당 아이템의 최대 Stack 개수를 초과했는지 체크 한다. var item_total_count = item_has_count + toTakeInCount; if (item_total_count > itemMetaData.MaxCount) { err_msg = $"Item max count exceed !!! : totalCount:{item_total_count} <= maxCount:{itemMetaData.MaxCount}, itemMetaId:{toTakeInItemMetaId}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemMaxCountExceed, err_msg); Log.getLogger().error(result.toBasicString()); return (result, reserved_items); } // 4. 아이템 Stack 개수가 큰 순서로 정렬 시킨다. var remain_count = toTakeInCount; target_item_attributes.OrderBy(x => { return x.ItemStackCount; }); // 5. 동일한 종류의 아이템에 최대 Stack 개수이내로 추가 한다. foreach (var item_attribute in target_item_attributes) { if (remain_count <= 0) { break; } var max_count = itemMetaData.StackMaxCount; var to_modify_item = item_attribute.getOwner() as Item; NullReferenceCheckHelper.throwIfNull(to_modify_item, () => $"to_modify_item is null !!! - {parent.toBasicString()}"); var to_modify_item_attribute = to_modify_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(to_modify_item_attribute, () => $"to_modify_item_attribute is null !!! - {parent.toBasicString()}"); var has_count = to_modify_item_attribute.ItemStackCount; if (has_count >= itemMetaData.StackMaxCount) { continue; } var to_add_count = 0; // 남은 아이템 개수도 추가 가능한지 체크 한다 !!! if (has_count + remain_count > max_count) { to_add_count = max_count - has_count; } else { to_add_count = remain_count; } to_modify_item_attribute.ItemStackCount += (ushort)to_add_count; remain_count -= (ushort)to_add_count; to_modify_item_attribute.modifiedEntityAttribute(); reserved_items.Add(to_modify_item); } // 6. 추가해야 하는 아이템 개수가 있다면 if(0 < remain_count) { // 6.1. 아이템 생성하고 추가 한다. var server_logic = GameServerApp.getServerLogic(); NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {parent.toBasicString()}"); var inventory_rule = server_logic.findRule(); NullReferenceCheckHelper.throwIfNull(inventory_rule, () => $"inventory_rule is null !!! - {parent.toBasicString()}"); var inventory_action = parent.getEntityAction(); NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!! - {parent.toBasicString()}"); var to_create_count = remain_count; while (to_create_count > 0) { var new_item = inventory_action.onAllocItem(); if(null == new_item) { err_msg = $"Failed to alloc Item by ItemMeta !!! : itemMetaId:{toTakeInItemMetaId}, count:{to_create_count} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemAllocFailed, err_msg); return (result, reserved_items); } result = await new_item.createByItemMeta(toTakeInItemMetaId, to_create_count); if (result.isFail()) { err_msg = $"Failed to create Item !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return (result, reserved_items); } var item_item_meta = new_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_item_meta, () => $"item_item_meta is null !!! - {parent.toBasicString()}"); var found_bag_rules = inventory_rule.getBagRulesByItemLargeType(item_item_meta.TypeLarge); NullReferenceCheckHelper.throwIfNull(found_bag_rules, () => $"found_bag_rules is null !!! - {parent.toBasicString()}"); foreach (var bag_rule in found_bag_rules) { result = tryIncItemCountInBagTap(new_item, bag_rule); if (result.isFail()) { err_msg = $"Failed to tryIncItemCountInBagTap() !!! : {result.toBasicString()} - {new_item.toBasicString()}, {parent.toBasicString()}"; Log.getLogger().error(err_msg); return (result, reserved_items); } break; } to_create_count -= new_item.getItemStackCount(); var item_attribute = new_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {new_item.toBasicString()}, {parent.toBasicString()}"); result = onTryTakeInEntityBase(item_attribute.ItemGuid, new_item); if (result.isFail()) { return (result, reserved_items); } reserved_items.Add(new_item); } } return (result, reserved_items); } public async Task<(Result, Item?)> tryTakableInItemBase(ItemDoc doc, bool isWriteToDb = false) { var result = new Result(); var err_msg = string.Empty; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var server_logic = GameServerApp.getServerLogic(); NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {parent.toBasicString()}"); var inventory_rule = server_logic.findRule(); NullReferenceCheckHelper.throwIfNull(inventory_rule, () => $"item_attribute is null !!! - {parent.toBasicString()}"); var item_doc_attrib = doc.getAttrib(); NullReferenceCheckHelper.throwIfNull(item_doc_attrib, () => $"item_doc_attrib is null !!! - {parent.toBasicString()}"); var found_duplicated_item = findEntityBase(item_doc_attrib.ItemGuid); if(null != found_duplicated_item) { err_msg = $"Found duplicated Item from ItemDoc !!! : duplicatedItem:{found_duplicated_item.toBasicString()} - {doc.toBasicString()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemDocLoadDuplicatedItem, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var inventory_action = parent.getEntityAction(); NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!! - {parent.toBasicString()}"); var new_item = inventory_action.onAllocItem(); if (null == new_item) { err_msg = $"Failed to alloc Item by ItemDoc !!! : itemGuid:{item_doc_attrib.ItemGuid}, itemMetaId:{item_doc_attrib.ItemMetaId} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemAllocFailed, err_msg); return (result, null); } result = await new_item.createByItemDoc(doc, isWriteToDb); if (result.isFail()) { err_msg = $"Failed to create Item !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } var item_item_meta = new_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_item_meta, () => $"item_item_meta is null !!! - {parent.toBasicString()}"); var found_bag_rules = inventory_rule.getBagRulesByItemLargeType(item_item_meta.TypeLarge); NullReferenceCheckHelper.throwIfNull(found_bag_rules, () => $"found_bag_rules is null !!! - {parent.toBasicString()}"); foreach (var bag_rule in found_bag_rules) { result = tryIncItemCountInBagTap(new_item, bag_rule); if (result.isFail()) { err_msg = $"Failed to tryIncItemCountInBagTap() !!! : {result.toBasicString()} - {new_item.toBasicString()}, {parent.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } break; } var item_attribute = new_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); result = onTryTakeInEntityBase(item_attribute.ItemGuid, new_item); if(result.isFail()) { return (result, null); } return (result, new_item); } private Result tryIncItemCountInBagTap(Item toTakeInItem, BagRule bagRule) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); NullReferenceCheckHelper.throwIfNull(toTakeInItem, () => $"toTakeInItem is null !!! - {parent.toBasicString()}"); NullReferenceCheckHelper.throwIfNull(bagRule, () => $"bagRule is null !!! - {parent.toBasicString()}"); var item_meta = toTakeInItem.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var target_bag_type = bagRule.InvenBagType; var usable_bag_tab_type = target_bag_type.toUsableBagTabType().toNFTTabOffset(item_meta); // Transaction 활성화 상태일 경우 var transaction_runner = parent.findTransactionRunner(TransactionIdType.PrivateContents); if (null != transaction_runner) { if (true == m_bag_tab_take_in_counts.TryGetValue(usable_bag_tab_type, out var found_tab)) { // 슬롯에 예약된 아이템 개수까지 확인하여 보관 가능 여부를 체크 한다. var reserved_count = 0; var found_reserved_slot = transaction_runner.findReservedSlot(parent, getEntityType(), usable_bag_tab_type.convertEnumToEnumTypeAndValueString()); if (null != found_reserved_slot) { reserved_count += found_reserved_slot.getReservedCount(); } var inc_slot_count = 1; if (false == found_tab.tryCheckTakableInItem((Int16)inc_slot_count, (Int16)reserved_count)) { err_msg = $"Failed to tryCheckTakableInItem() !!!, Bag is reserved item full : incSlotCount:{inc_slot_count}, reservedSlotCount:{reserved_count}" + $" - {toTakeInItem.toBasicString()}, usableInvenBagName:{usable_bag_tab_type.toUsableInvenBagName()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagIsReservedItemFull, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } // 예약 슬롯 개수를 증가 시킨다. result = transaction_runner.tryIncReserveSlotCount(parent, getEntityType(), usable_bag_tab_type.convertEnumToEnumTypeAndValueString()); if (result.isFail()) { return result; } err_msg = $"Succss tryEquipToReserveSlot() : targetSlotType:{usable_bag_tab_type.convertEnumToEnumTypeAndValueString()} - {toBasicString()}, {parent.toBasicString()}"; Log.getLogger().info(err_msg); return result; } if (false == m_bag_tab_take_in_counts.TryGetValue(usable_bag_tab_type, out var found_bag_tab)) { found_bag_tab = new BagTab(this, usable_bag_tab_type); m_bag_tab_take_in_counts.TryAdd(usable_bag_tab_type, found_bag_tab); } if(false == found_bag_tab.tryTakableInItem()) { err_msg = $"Failed to tryTakableInItem() !!!, Bag is item full - {toTakeInItem.toBasicString()}, {found_bag_tab.toBasicString()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagIsReservedItemFull, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public async Task<(Result, List)> tryTakableOutItemBase(META_ID toUnequipMetaId, UInt32 toTakeOutCount = UInt32.MaxValue) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var result = new Result(); var err_msg = string.Empty; var reserved_items = new List(); // 1. 아이템 메타 정보를 얻는다. if (false == MetaData.Instance._ItemTable.TryGetValue((int)toUnequipMetaId, out var itemMetaData)) { err_msg = $"Not found ItemMeta !!! : itemMetaId:{toUnequipMetaId} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, reserved_items); } // 2. 동일한 종류의 ItemAttributeBase 목록과 보유 개수를 얻는다. (result, var target_item_attributes, var item_has_count) = findItemAttributeBaseAllByMetaId(toUnequipMetaId, toTakeOutCount); if (result.isFail()) { return (result, reserved_items); } // 3. 보유하고 있는 아이템 개수를 체크 한다. if ( UInt32.MaxValue != toTakeOutCount && item_has_count < toTakeOutCount) { err_msg = $"Not enough Item Count !!! : totalCount:{item_has_count} > reqCount:{toTakeOutCount}, itemMetaId:{toUnequipMetaId}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemCountNotEnough, err_msg); Log.getLogger().error(result.toBasicString()); return (result, reserved_items); } var to_take_out_items = new Dictionary(); var remain_count = toTakeOutCount; if (UInt32.MaxValue == toTakeOutCount) { remain_count = (UInt16)target_item_attributes.Sum(x => x.ItemStackCount); } // 4. 아이템 개수가 많은 순서로 정렬 시킨다. target_item_attributes.OrderBy(x => (x.ItemStackCount)); // 5. 동일한 종류의 아이템에서 Takeout할 아이템 개수만큼 차감한다. foreach (var item_attribute in target_item_attributes) { if (remain_count <= 0) { break; } var to_modify_item = item_attribute.getOwner() as Item; NullReferenceCheckHelper.throwIfNull(to_modify_item, () => $"to_modify_item is null !!! - {parent.toBasicString()}"); var to_modify_item_attribute = to_modify_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(to_modify_item_attribute, () => $"to_modify_item_attribute is null !!! - {parent.toBasicString()}"); UInt16 to_take_out_count = 0; // 5.1. Takeout 가능 개수를 계산 한다. if (item_attribute.ItemStackCount > remain_count) { to_take_out_count = (UInt16)remain_count; remain_count = 0; } else { to_take_out_count = item_attribute.ItemStackCount; remain_count -= to_take_out_count; } to_take_out_items.Add(item_attribute, to_take_out_count); } foreach(var each in to_take_out_items) { var item_attribute = each.Key as ItemAttributeBase; NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); (result, var take_out_item) = await tryTakableOutItemBase(item_attribute.ItemGuid, each.Value); if(result.isFail()) { return (result, reserved_items); } NullReferenceCheckHelper.throwIfNull(take_out_item, () => $"take_out_item is null !!! - {parent.toBasicString()}"); reserved_items.Add(take_out_item); } return (result, reserved_items); } public async Task<(Result, Item?)> tryTakableOutItemBase(ITEM_GUID toUnequipItemGuid, UInt16 toTakeOutCount) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var result = new Result(); var err_msg = string.Empty; var server_logic = GameServerApp.getServerLogic(); NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {parent.toBasicString()}"); var inventory_rule = server_logic.findRule(); NullReferenceCheckHelper.throwIfNull(inventory_rule, () => $"inventory_rule is null !!! - {parent.toBasicString()}"); // 1. 가방에서 아이템을 찾는다. var found_item = findEntityBase(toUnequipItemGuid) as Item; if(null == found_item) { err_msg = $"Not found Item !!!, findEntityBase() : itemGuid:{toUnequipItemGuid} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } var found_item_meta = found_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(found_item_meta, () => $"found_item_meta is null !!! - {parent.toBasicString()}"); // 2. 아이템 개수의 차감 가능 여부를 체크하고 // 아이템 개수 차감 => 아이템 삭제 => BagTab 개수 차감을 처리 한다. var item_attribute = found_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); if (item_attribute.ItemStackCount < toTakeOutCount) { err_msg = $"Not enough ItemStackCount !!! : hasCount:{item_attribute.ItemStackCount} > reqCount:{toTakeOutCount}, itemGuid:{toUnequipItemGuid} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemStackCountNotEnough, err_msg); Log.getLogger().error(result.toBasicString()); return (result, null); } else if(item_attribute.ItemStackCount > toTakeOutCount) { item_attribute.ItemStackCount -= toTakeOutCount; item_attribute.modifiedEntityAttribute(); } else { result = onTryTakeOutEnityBase(toUnequipItemGuid, out _); if(result.isFail()) { return (result, null); } item_attribute.ItemStackCount = 0; item_attribute.deleteEntityAttribute(); } if (0 == item_attribute.ItemStackCount) { var found_bag_rules = inventory_rule.getBagRulesByItemLargeType(found_item_meta.TypeLarge); NullReferenceCheckHelper.throwIfNull(found_bag_rules, () => $"found_bag_rules is null !!! - {parent.toBasicString()}"); foreach (var bag_rule in found_bag_rules) { result = tryDecItemCountInBagTap(found_item, bag_rule); if (result.isFail()) { err_msg = $"Failed to tryDecItemCountInBagTap() !!! : {result.toBasicString()} - {found_item.toBasicString()}, {parent.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } break; } } return await Task.FromResult((result, found_item)); } private Result tryDecItemCountInBagTap(Item toRemoveItem, BagRule bagRule) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); NullReferenceCheckHelper.throwIfNull(toRemoveItem, () => $"toRemoveItem is null !!! - {parent.toBasicString()}"); NullReferenceCheckHelper.throwIfNull(bagRule, () => $"bagRule is null !!! - {parent.toBasicString()}"); var item_meta = toRemoveItem.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var target_bag_type = bagRule.InvenBagType; var usable_bag_tab_type = target_bag_type.toUsableBagTabType().toNFTTabOffset(item_meta); // Transaction 활성화 상태일 경우 var transaction_runner = parent.findTransactionRunner(TransactionIdType.PrivateContents); if (null != transaction_runner) { if (true == m_bag_tab_take_in_counts.TryGetValue(usable_bag_tab_type, out var found_tab)) { // 슬롯에 예약된 아이템 개수까지 확인하여 차감 가능 여부를 체크 한다. var reserved_count = 0; var found_reserved_slot = transaction_runner.findReservedSlot(parent, getEntityType(), usable_bag_tab_type.convertEnumToEnumTypeAndValueString()); if (null != found_reserved_slot) { reserved_count += found_reserved_slot.getReservedCount(); } var dec_slot_count = 1; if (false == found_tab.tryCheckTakableOutItem((Int16)dec_slot_count, (Int16)reserved_count)) { err_msg = $"Failed to tryCheckTakableOutItem() !!!, Bag is reserved item empty - decSlotCount:{dec_slot_count}, reservedSlotCount:{reserved_count} - {toRemoveItem.toBasicString()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagIsReservedItemEmpty, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } // 예약 슬롯 개수를 감소 시킨다. result = transaction_runner.tryDecToReserveSlotCount(parent, getEntityType(), usable_bag_tab_type.convertEnumToEnumTypeAndValueString()); if (result.isFail()) { return result; } err_msg = $"Success tryUnequipToReserveSlot() : targetSlotType:{usable_bag_tab_type.convertEnumToEnumTypeAndValueString()} - {toBasicString()}, {parent.toBasicString()}"; Log.getLogger().info(err_msg); return result; } if (false == m_bag_tab_take_in_counts.TryGetValue(usable_bag_tab_type, out var found_bag_tab)) { found_bag_tab = new BagTab(this, usable_bag_tab_type); m_bag_tab_take_in_counts.TryAdd(usable_bag_tab_type, found_bag_tab); } if (false == found_bag_tab.tryTakableOutItem()) { err_msg = $"Failed to tryTakableOutItem() !!!, Bag is item empty - {toRemoveItem.toBasicString()}, {found_bag_tab.toBasicString()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagIsItemEmpty, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public async Task<(Result, Item?, Item?)> tryDivideItem(ITEM_GUID toDivideitemGuid, UInt16 toDivideCount = 1) { var result = new Result(); var err_msg = string.Empty; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); (result, var divided_item) = await tryTakableOutItemBase(toDivideitemGuid, toDivideCount); if (result.isFail()) { return (result, null, null); } NullReferenceCheckHelper.throwIfNull(divided_item, () => $"take_out_item is null !!! - {parent.toBasicString()}"); var divided_item_meta = divided_item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(divided_item_meta, () => $"server_logic is null !!! - {parent.toBasicString()}"); (result, var new_item) = await tryCreateItemByMeta((META_ID)divided_item_meta.ItemId, toDivideCount); if (result.isFail()) { return (result, null, null); } NullReferenceCheckHelper.throwIfNull(divided_item, () => $"take_out_item is null !!! - {parent.toBasicString()}"); return (result, divided_item, new_item); } async Task<(Result, Item?)> tryCreateItemByMeta(META_ID toCreateItemMetaId, UInt16 toCreateCount) { var result = new Result(); var err_msg = string.Empty; var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var inventory_action = parent.getEntityAction(); NullReferenceCheckHelper.throwIfNull(inventory_action, () => $"inventory_action is null !!! - {parent.toBasicString()}"); var new_item = inventory_action.onAllocItem(); if (null == new_item) { err_msg = $"Failed to alloc Item by ItemMeta !!! : itemMetaId:{toCreateItemMetaId}, count:{toCreateCount} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemAllocFailed, err_msg); return (result, null); } result = await new_item.createByItemMeta(toCreateItemMetaId, toCreateCount); if (result.isFail()) { err_msg = $"Failed to create Item !!! : {result.toBasicString()}"; Log.getLogger().error(err_msg); return (result, null); } return (result, new_item); } public Result onMergeTakeInItem(Item takeInItem) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); NullReferenceCheckHelper.throwIfNull(takeInItem, () => $"takeInItem is null !!! - {parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var item_attribute = takeInItem.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); var error_code = getData().onTryEquipWithEntityBase(item_attribute.ItemGuid, takeInItem); if (error_code.isFail()) { err_msg = $"Failed to onTryEquipWithEntityBase() !!! : {error_code.toBasicString()} - {parent.toBasicString()}"; result.setFail(error_code, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public Result onMergeTakeOutItem(Item takeOutItem) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); NullReferenceCheckHelper.throwIfNull(takeOutItem, () => $"takeOutItem is null !!! - {parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; var item_attribute = takeOutItem.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(item_attribute, () => $"item_attribute is null !!! - {parent.toBasicString()}"); var error_code = getData().onTryUnequipWithEntityBase(item_attribute.ItemGuid, out var unequiped_item); if (error_code.isFail()) { err_msg = $"Failed to onTryUnequipWithEntityBase() !!! : {error_code.toBasicString()} - {parent.toBasicString()}"; result.setFail(error_code, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return result; } public bool hasItemByMetaId(META_ID itemMetaId, UInt32 toCheckCount = 1) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var found_items = new List(); var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); var found_item_count = 0; foreach (var item_base in has_items) { var item = item_base as Item; NullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {parent.toBasicString()}"); var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); if (item_meta.ItemId != itemMetaId) { continue; } found_item_count += item.getItemStackCount(); if (toCheckCount <= found_item_count) { return true; } } return false; } public ConcurrentDictionary getItemsCountByMetaIds(List itemMetaIds) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var found_items = new ConcurrentDictionary(); var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); foreach (var item_base in has_items) { var item = item_base as Item; NullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {parent.toBasicString()}"); var item_meta_id = item.getItemMetaId(); UInt16 item_stack_count = item.getItemStackCount(); foreach (var find_item_meta_id in itemMetaIds) { if(find_item_meta_id == item_meta_id) { if(found_items.TryGetValue(item_meta_id, out UInt16 item_count) == false) { found_items.TryAdd(item_meta_id, item_stack_count); break; } found_items[item_meta_id] = (ushort)(item_count + item_stack_count); break; } } } return found_items; } public Int32 getItemStackCountAllByMetaId(META_ID itemMetaId) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var item_stack_count_all = 0; var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); foreach (var item in has_items) { var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); if (item_meta.ItemId != itemMetaId) { continue; } item_stack_count_all += item.getItemStackCount(); } return item_stack_count_all; } public Int32 getItemCountAllByMetaId(META_ID itemMetaId) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var item_count_all = 0; var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); foreach (var item in has_items) { var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); if (item_meta.ItemId != itemMetaId) { continue; } item_count_all += 1; } return item_count_all; } public List getItemAllByMetaId(META_ID itemMetaId) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var found_items = new List(); var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); foreach (var item_base in has_items) { var item = item_base as Item; NullReferenceCheckHelper.throwIfNull(item, () => $"item is null !!! - {parent.toBasicString()}"); var item_meta = item.getItemMeta(); NullReferenceCheckHelper.throwIfNull(item_meta, () => $"item_meta is null !!! - {parent.toBasicString()}"); if (item_meta.ItemId != itemMetaId) { continue; } found_items.Add(item); } return found_items; } public override void onWriteLog() { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); var err_msg = string.Empty; var total_count = 0; var total_max_count = 0; foreach(var each in m_bag_tab_take_in_counts) { var bag_tab_type = each.Key; var bag_tab = each.Value; err_msg = $"BagTabType:{bag_tab_type}, TakeInCount:{bag_tab.TakeInItemCount}, SlotMaxCount:{bag_tab.getSlotMaxCount()}"; Log.getLogger().info(err_msg); total_count += bag_tab.TakeInItemCount; total_max_count += bag_tab.getSlotMaxCount(); } var has_items = getHasItemBases(); NullReferenceCheckHelper.throwIfNull(has_items, () => $"has_items is null !!! - {parent.toBasicString()}"); err_msg = $"BagTab TotalTakeInCount:{total_count}, TotalSlotMaxCount:{total_max_count}, TotalItemCount:{has_items.Count}"; Log.getLogger().info(err_msg); } public async Task tryCheckTakableInBagInven(Dictionary toCheckTakableInItemCounts) { var parent = getDirectParent(); NullReferenceCheckHelper.throwIfNull(parent, () => $"parent is null !!!"); NullReferenceCheckHelper.throwIfNull(toCheckTakableInItemCounts, () => $"toCheckTakableInItemCounts is null !!! - {parent.toBasicString()}"); var result = new Result(); var err_msg = string.Empty; bool is_found_value = (false == toCheckTakableInItemCounts.Any()) || toCheckTakableInItemCounts.Values.All(value => value <= 0); if(true == is_found_value) { return result; } var server_logic = GameServerApp.getServerLogic(); NullReferenceCheckHelper.throwIfNull(server_logic, () => $"server_logic is null !!! - {parent.toBasicString()}"); var inventory_rule = server_logic.findRule(); NullReferenceCheckHelper.throwIfNull(inventory_rule, () => $"inventory_rule is null !!! - {parent.toBasicString()}"); var is_found_target_bag_tab = false; foreach (var each in toCheckTakableInItemCounts) { var item_meta_id = each.Key; var item_count = each.Value; // 1. 아이템 메타 정보를 얻는다. if (false == MetaData.Instance._ItemTable.TryGetValue((Int32)item_meta_id, out var found_item_meta)) { err_msg = $"Not found Item Meta !!! : itemMetaId:{item_meta_id} - {parent.toBasicString()}"; result.setFail(ServerErrorCode.ItemMetaDataNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } // 2. 동일한 종류의 ItemAttributeBase 목록과 보유 개수를 얻는다. (result, var target_item_attributes, var item_has_count) = findItemAttributeBaseAllByMetaId(item_meta_id, (ushort)item_count); if (result.isFail()) { return result; } var remain_count = item_count; target_item_attributes.OrderByDescending(x => { return x.ItemStackCount; }); // 3. 동일한 종류의 아이템에 최대 Stack 개수이내로 추가 한다. foreach (var item_attribute in target_item_attributes) { if (remain_count <= 0) { break; } var max_count = found_item_meta.StackMaxCount; var to_modify_item = item_attribute.getOwner() as Item; NullReferenceCheckHelper.throwIfNull(to_modify_item, () => $"to_modify_item is null !!! - {parent.toBasicString()}"); var to_modify_item_attribute = to_modify_item.getEntityAttribute(); NullReferenceCheckHelper.throwIfNull(to_modify_item_attribute, () => $"to_modify_item_attribute is null !!! - {parent.toBasicString()}"); var has_count = to_modify_item_attribute.ItemStackCount; if (has_count >= found_item_meta.StackMaxCount) { continue; } var to_add_count = 0; if (has_count + remain_count > max_count) { to_add_count = max_count - has_count; } else { to_add_count = remain_count; } remain_count -= (short)to_add_count; is_found_target_bag_tab = true; } if (0 < remain_count) { is_found_target_bag_tab = false; var to_inc_slot_count = remain_count / found_item_meta.StackMaxCount; to_inc_slot_count += ((remain_count % found_item_meta.StackMaxCount) > 0 ? 1 : 0); var found_bag_rules = inventory_rule.getBagRulesByItemLargeType(found_item_meta.TypeLarge); NullReferenceCheckHelper.throwIfNull(found_bag_rules, () => $"found_bag_rules is null !!! - {parent.toBasicString()}"); foreach (var bag_rule in found_bag_rules) { var target_bag_type = bag_rule.InvenBagType; var usable_bag_tab_type = target_bag_type.toUsableBagTabType().toNFTTabOffset(found_item_meta); if (true == m_bag_tab_take_in_counts.TryGetValue(usable_bag_tab_type, out var found_tab)) { is_found_target_bag_tab = true; // 슬롯에 예약된 아이템 개수까지 확인하여 보관 가능 여부를 체크 한다. var reserved_count = 0; // Transaction 활성화 상태일 경우 var transaction_runner = parent.findTransactionRunner(TransactionIdType.PrivateContents); if (null != transaction_runner) { var found_reserved_slot = transaction_runner.findReservedSlot(parent, getEntityType(), usable_bag_tab_type.convertEnumToEnumTypeAndValueString()); if (null != found_reserved_slot) { reserved_count += found_reserved_slot.getReservedCount(); } } if (false == found_tab.tryCheckTakableInItem((Int16)to_inc_slot_count, (Int16)reserved_count)) { err_msg = $"Failed to tryCheckTakableInItem() !!!, Bag is reserved item full : incSlotCount:{remain_count}, reservedSlotCount:{reserved_count}" + $" - itemMetaId:{item_meta_id}, usableInvenBagName:{usable_bag_tab_type.toUsableInvenBagName()}, {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagIsReservedItemFull, err_msg); Log.getLogger().error(result.toBasicString()); return result; } } } } } if(false == is_found_target_bag_tab) { err_msg = $"Not found target BagTab !!! - {parent.toBasicString()}"; result.setFail(ServerErrorCode.BagTabTypeNotFound, err_msg); Log.getLogger().error(result.toBasicString()); return result; } return await Task.FromResult(result); } public ConcurrentDictionary getBagTabs() => m_bag_tab_take_in_counts; public override string toSummaryString() { return $"{this.getTypeName()}, {getData().toBasicString()}"; } } }