353 lines
14 KiB
C#
353 lines
14 KiB
C#
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<TSlotType> : SlotsWrapperBase<SlotsWithEntityBase<TSlotType>>, 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<ItemAttributeBase>, 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<ItemAttributeBase>();
|
|
|
|
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<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_base = x.Value.getOriginEntityAttribute<ItemAttributeBase>();
|
|
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<ITEM_GUID, ItemAttributeBase>();
|
|
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<ItemAttributeBase>();
|
|
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<TSlotType, EntityBase> 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<TSlotType, EntityBase>;
|
|
NullReferenceCheckHelper.throwIfNull(slots, () => $"slots is null !!! - {parent.toBasicString()}");
|
|
|
|
return slots;
|
|
}
|
|
|
|
public List<EntityBase> 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<TSlotType, EntityBase>;
|
|
NullReferenceCheckHelper.throwIfNull(slots, () => $"slots is null !!! - {parent.toBasicString()}");
|
|
|
|
slots.Clear();
|
|
}
|
|
|
|
// IWithInventoryAccessor.getHasItemBases()
|
|
public List<ItemBase> 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<TSlotType>("None", out var target_slot_type) || null == target_slot_type)
|
|
{
|
|
err_msg = $"Failed to fillupEnumTypeAndValueStringToEnum<TSlotType>() !!!, 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<TSlotType>("None", out var target_slot_type) || null == target_slot_type)
|
|
{
|
|
err_msg = $"Failed to fillupEnumTypeAndValueStringToEnum<TSlotType>() !!!, 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;
|
|
}
|
|
}
|