魔兽3.3.5版本背包模块代码拆分解析(从之前的文章中拆分出来)

文章目录

    • 一、涉及到的文件
    • 二、先看成员变量(单个背包的存储数据)
    • 三、再看玩家背包和DB的交互
      • 1)逐行解析db表
      • 2)sql语句变更记录(记录一些sql语句对数据存储的影响)
      • 3)额外提一嘴物品在mysql的存储
      • 4)预编译背包的物品和db交互的sql语句
    • 四、解析成员函数
      • 1)构造函数
      • 2)析构函数
      • 3)实体添加到世界
      • 4)背包添加物品
      • 5)获得对应id的物品数量
      • 6)根据物品标签获得对应物品数量
      • 7)根据物品id获取对应背包内物品的槽位
    • 五、背包总结
    • 六、总结
      • 1)建立人物碰撞体代码讲解开始
      • 2)源码枚举
    • 七、额外补充(物品的创建过程)
    • 八、代码

不想写在 魔兽服务器学习-笔记之中了,之前的文章篇幅太长,代码在最后的模块

一、涉及到的文件

Object.h
Item.h
Bag.h

这里的背包继承了物品类,而物品类又继承了对象Object类

  • 注意点
    因为这里避免看的越多,所以这里从Bag.h看起,因为从Object.h看起来的话,根本看不完

二、先看成员变量(单个背包的存储数据)

#define MAX_BAG_SIZE 36 
        // Bag Storage space
        Item* m_bagslot[MAX_BAG_SIZE];

可以看到这里是存了指针数组,上限是36(因为c with class的关系,而且对于游戏来说内存很重要)

三、再看玩家背包和DB的交互

  • 背包类和DB交互的代码
        // DB operations
        // overwrite virtual Item::SaveToDB
        void SaveToDB(CharacterDatabaseTransaction trans) override;
        // overwrite virtual Item::LoadFromDB
        bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry) override;
        // overwrite virtual Item::DeleteFromDB
        void DeleteFromDB(CharacterDatabaseTransaction trans) override;
  • 玩家背包数据的db存储sql语句
DROP TABLE IF EXISTS `character_inventory`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `character_inventory` (
  `guid` int unsigned NOT NULL DEFAULT '0' COMMENT 'Global Unique Identifier',
  `bag` int unsigned NOT NULL DEFAULT '0',
  `slot` tinyint unsigned NOT NULL DEFAULT '0',
  `item` int unsigned NOT NULL DEFAULT '0' COMMENT 'Item Global Unique Identifier',
  PRIMARY KEY (`item`),
  UNIQUE KEY `guid` (`guid`,`bag`,`slot`),
  KEY `idx_guid` (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Player System';
/*!40101 SET character_set_client = @saved_cs_client */;

①guid 人物id
②slot 槽位id
③item 道具id
④bag 背包索引(魔兽可携带多个背包)
表格使用的存储引擎为 InnoDB,字符集为 utf8mb4,排序规则为 utf8mb4_unicode_ci。整个表格用于存储玩家系统中与角色背包相关的数据。

1)逐行解析db表

①PRIMARY KEY (item):将 item 列定义为主键,确保每个物品在表格中是唯一的。
②UNIQUE KEY guid (guid,bag,slot):创建一个唯一键,由 guid、bag 和 slot 三列组成,确保每个背包槽位中的物品在表格中是唯一的。
③KEY idx_guid (guid):创建一个索引,以 guid 列作为索引,用于快速检索与特定角色相关的背包信息。

2)sql语句变更记录(记录一些sql语句对数据存储的影响)

①改变数据表的存储格式

ALTER TABLE `character_inventory` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  • ALTER TABLE:用于修改数据库表的命令。
  • character_inventory:表名,指定要修改的表为 character_inventory。
  • CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci:用于指定要将表的字符集转换为 utf8mb4,并使用 utf8mb4_unicode_ci 校对规则。

解释:
这个 ALTER TABLE 语句的目的是将 character_inventory 表的字符集从现有的字符集转换为 utf8mb4。 utf8mb4 字符集支持存储更广泛的字符,包括一些特殊的 Unicode 字符,如表情符号。校对规则 utf8mb4_unicode_ci 则用于指定字符的排序和比较规则。

②删除对应列

ALTER TABLE `character_inventory` DROP COLUMN `item_template`;
  • ALTER TABLE:用于修改数据库表的命令。
  • character_inventory:表名,指定要修改的表为 character_inventory。
  • DROP COLUMN:指定要删除列的操作。
  • item_template:要删除的列名,即 character_inventory 表中的 item_template 列。

解释:
这个 ALTER TABLE 语句的目的是从 character_inventory 表中删除名为 item_template 的列。执行该语句后,表中的数据将不再包含 item_template 列的值,并且该列将从表结构中删除。

3)额外提一嘴物品在mysql的存储

CREATE TABLE `item_instance` (
  `guid` int unsigned NOT NULL DEFAULT '0',
  `itemEntry` mediumint unsigned NOT NULL DEFAULT '0',
  `owner_guid` int unsigned NOT NULL DEFAULT '0',
  `creatorGuid` int unsigned NOT NULL DEFAULT '0',
  `giftCreatorGuid` int unsigned NOT NULL DEFAULT '0',
  `count` int unsigned NOT NULL DEFAULT '1',
  `duration` int NOT NULL DEFAULT '0',
  `charges` tinytext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
  `flags` mediumint unsigned NOT NULL DEFAULT '0',
  `enchantments` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
  `randomPropertyId` smallint NOT NULL DEFAULT '0',
  `durability` smallint unsigned NOT NULL DEFAULT '0',
  `playedTime` int unsigned NOT NULL DEFAULT '0',
  `text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
  PRIMARY KEY (`guid`),
  KEY `idx_owner_guid` (`owner_guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Item System';
/*!40101 SET character_set_client = @saved_cs_client */;

目的:这个SQL语句的目的是创建一个名为 item_instance 的表,用于存储物品实例的相关信息。
guid:物品唯一id
owner_guid:物品归属obj的id

4)预编译背包的物品和db交互的sql语句

①删除背包物品(全服唯一的物品id)CHAR_DEL_CHAR_INVENTORY_BY_ITEM

    PrepareStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM, 
    "DELETE FROM character_inventory WHERE item = ?", CONNECTION_ASYNC);

②删除数据内对应背包id、对应格子、对应人物的数据信息

    PrepareStatement(CHAR_DEL_CHAR_INVENTORY_BY_BAG_SLOT, 
    "DELETE FROM character_inventory WHERE bag = ? AND slot = ? AND guid = ?", CONNECTION_ASYNC);

因为之前可以看到玩家背包表有做唯一索引

UNIQUE KEY `guid` (`guid`,`bag`,`slot`)

③查询背包里面单个物品id对应的持续时间、槽位、背包id、槽位id、标志位flags等信息

    PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, 
    "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot,item, itemEntry 
    FROM character_inventory ci   #给character_inventory起别名ci
    JOIN item_instance ii         #联表查询item_instance,给item_instance起别名ii
    ON ci.item = ii.guid WHERE ci.guid = ? #查询单个物品在
    ORDER BY bag, slot", CONNECTION_ASYNC);

④给character_inventory 执行插入操作

PrepareStatement(CHAR_REP_INVENTORY_ITEM,
 "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);

四、解析成员函数

1)构造函数

Bag::Bag(): Item()
{
    m_objectType |= TYPEMASK_CONTAINER;       //标志是物品是背包类型
    m_objectTypeId = TYPEID_CONTAINER;        //标志这个obj是背包

    m_valuesCount = CONTAINER_END;

    memset(m_bagslot, 0, sizeof(Item*) * MAX_BAG_SIZE);
}

2)析构函数

Bag::~Bag()
{
    for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
        if (Item* item = m_bagslot[i])
        {
            if (item->IsInWorld())  //背包在地图上,那就需要从地图实例里面去除
            {
                TC_LOG_FATAL("entities.player.items", "Item {} (slot {}, bag slot {}) in bag {} (slot {}, bag slot {}, m_bagslot {}) is to be deleted but is still in world.",
                    item->GetEntry(), (uint32)item->GetSlot(), (uint32)item->GetBagSlot(),
                    GetEntry(), (uint32)GetSlot(), (uint32)GetBagSlot(), (uint32)i);
                item->RemoveFromWorld();
            }
            delete m_bagslot[i];
        }
}

3)实体添加到世界

//把实例添加到世界
void Bag::AddToWorld()
{
    Item::AddToWorld();

    for (uint32 i = 0; i < GetBagSize(); ++i)
        if (m_bagslot[i])
            m_bagslot[i]->AddToWorld();
}

//从世界里面去除实例
void Bag::RemoveFromWorld()
{
    for (uint32 i = 0; i < GetBagSize(); ++i)
        if (m_bagslot[i])
            m_bagslot[i]->RemoveFromWorld();

    Item::RemoveFromWorld();
}

4)背包添加物品

bool Bag::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owner)
{
//读取物品的配置信息,例如堆叠数量、过期时间、耐久度
    ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemid);

    if (!itemProto || itemProto->ContainerSlots > MAX_BAG_SIZE)
        return false;

    Object::_Create(guidlow, 0, HighGuid::Container);

	//标志位字段设置物品id
    SetEntry(itemid);
    //OBJECT_FIELD_SCALE_X 是用来控制游戏中物体的缩放比例的字段
    SetObjectScale(1.0f);

    if (owner)
    {
    //如果这个背包添加道具是添加到玩家身上,在对应标志位填玩家id
        SetGuidValue(ITEM_FIELD_OWNER, owner->GetGUID());
        SetGuidValue(ITEM_FIELD_CONTAINED, owner->GetGUID());
    }

//ITEM_FIELD_DURABILITY:物品的当前耐久度。
//ITEM_FIELD_MAXDURABILITY:物品的最大耐久度
    SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
    SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);
    //ITEM_FIELD_STACK_COUNT:物品的堆叠数量
    SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);

    // Setting the number of Slots the Container has
    //根据配置设置背包槽位数
    SetUInt32Value(CONTAINER_FIELD_NUM_SLOTS, itemProto->ContainerSlots);

    // Cleaning 20 slots
    for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
    {
    //将背包对象的每个槽位的GUID值设置为空,以确保背包对象创建时的初始状态是空的,没有任何物品。
        SetGuidValue(CONTAINER_FIELD_SLOT_1 + (i*2), ObjectGuid::Empty);
        m_bagslot[i] = nullptr;
    }

    return true;
}

5)获得对应id的物品数量

  • 备注
    eItem->GetTemplate()->GemProperties用于计算背包宝石的数量
  • 代码
uint32 Bag::GetItemCount(uint32 item, Item* eItem) const
{
    Item* pItem;
    uint32 count = 0;
    for (uint32 i=0; i < GetBagSize(); ++i)
    {
        pItem = m_bagslot[i];
        if (pItem && pItem != eItem && pItem->GetEntry() == item)
            count += pItem->GetCount();
    }

    if (eItem && eItem->GetTemplate()->GemProperties)
    {
        for (uint32 i=0; i < GetBagSize(); ++i)
        {
            pItem = m_bagslot[i];
            if (pItem && pItem != eItem && pItem->GetTemplate()->Socket[0].Color)
                count += pItem->GetGemCountWithID(item);
        }
    }

    return count;
}

6)根据物品标签获得对应物品数量

  • 备注
    uint32 limitCategory表示物品的标签

  • 代码

uint32 Bag::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem) const
{
    uint32 count = 0;
    for (uint32 i = 0; i < GetBagSize(); ++i)
        if (Item* pItem = m_bagslot[i])
            if (pItem != skipItem)
                if (ItemTemplate const* pProto = pItem->GetTemplate())
                    if (pProto->ItemLimitCategory == limitCategory)
                        count += m_bagslot[i]->GetCount();

    return count;
}

7)根据物品id获取对应背包内物品的槽位

uint8 Bag::GetSlotByItemGUID(ObjectGuid guid) const
{
    for (uint32 i = 0; i < GetBagSize(); ++i)
        if (m_bagslot[i] != 0)
            if (m_bagslot[i]->GetGUID() == guid)
                return i;

    return NULL_SLOT;
}

五、背包总结

魔兽3.3.5版本背包模块代码拆分解析(从之前的文章中拆分出来)_第1张图片
①玩家身上有150个物品格子,而在这个格子里面,有的物品是背包,那么这个背包就可以存储最多36个物品(这也是为什么Bag继承Item的原因,因为可以根据Item找到对应的Bag)
②从下面的枚举可以看出,玩家身上固定第19到23是背包的槽位,1到18是身上装备槽位

enum PlayerSlots
{
    // first slot for item stored (in any way in player m_items data)
    PLAYER_SLOT_START           = 0,
    // last+1 slot for item stored (in any way in player m_items data)
    PLAYER_SLOT_END             = 150,
    PLAYER_SLOTS_COUNT          = (PLAYER_SLOT_END - PLAYER_SLOT_START)
};

class player{
.....
        Item* m_items[PLAYER_SLOTS_COUNT];
....
}

---------------------
背包枚举:
enum EquipmentSlots : uint8                                 // 19 slots
{
    EQUIPMENT_SLOT_START        = 0,
    EQUIPMENT_SLOT_HEAD         = 0,
    EQUIPMENT_SLOT_NECK         = 1,
    EQUIPMENT_SLOT_SHOULDERS    = 2,
    EQUIPMENT_SLOT_BODY         = 3,
    EQUIPMENT_SLOT_CHEST        = 4,
    EQUIPMENT_SLOT_WAIST        = 5,
    EQUIPMENT_SLOT_LEGS         = 6,
    EQUIPMENT_SLOT_FEET         = 7,
    EQUIPMENT_SLOT_WRISTS       = 8,
    EQUIPMENT_SLOT_HANDS        = 9,
    EQUIPMENT_SLOT_FINGER1      = 10,
    EQUIPMENT_SLOT_FINGER2      = 11,
    EQUIPMENT_SLOT_TRINKET1     = 12,
    EQUIPMENT_SLOT_TRINKET2     = 13,
    EQUIPMENT_SLOT_BACK         = 14,
    EQUIPMENT_SLOT_MAINHAND     = 15,
    EQUIPMENT_SLOT_OFFHAND      = 16,
    EQUIPMENT_SLOT_RANGED       = 17,
    EQUIPMENT_SLOT_TABARD       = 18,
    EQUIPMENT_SLOT_END          = 19
};

enum InventorySlots : uint8                                 // 4 slots
{
    INVENTORY_SLOT_BAG_START    = 19,
    INVENTORY_SLOT_BAG_END      = 23
};

enum InventoryPackSlots : uint8                             // 16 slots
{
    INVENTORY_SLOT_ITEM_START   = 23,
    INVENTORY_SLOT_ITEM_END     = 39
};

enum BankItemSlots                                          // 28 slots
{
    BANK_SLOT_ITEM_START        = 39,
    BANK_SLOT_ITEM_END          = 67
};

enum BankBagSlots                                           // 7 slots
{
    BANK_SLOT_BAG_START         = 67,
    BANK_SLOT_BAG_END           = 74
};

enum BuyBackSlots                                           // 12 slots
{
    // stored in m_buybackitems
    BUYBACK_SLOT_START          = 74,
    BUYBACK_SLOT_END            = 86
};

enum KeyRingSlots : uint8                                   // 32 slots
{
    KEYRING_SLOT_START          = 86,
    KEYRING_SLOT_END            = 118
};

enum CurrencyTokenSlots                                     // 32 slots
{
    CURRENCYTOKEN_SLOT_START    = 118,
    CURRENCYTOKEN_SLOT_END      = 150
};


六、总结

1)建立人物碰撞体代码讲解开始

  • 代码讲解
    遍历身上装备道具,通知客户端创建对应地图实体
  • 对应数据
    也是存在之前说的道具数组里面
        Item* m_items[PLAYER_SLOTS_COUNT];
  • 代码
void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
{
    if (target == this)
    {
    	//遍历身上装备的装备枚举,到EQUIPMENT_SLOT_END
        for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
        {
            if (m_items[i] == nullptr)
                continue;
			//通知客户端创建对应实体
            m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
        }

        for (uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; ++i)
        {
            if (m_items[i] == nullptr)
                continue;

            m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
        }
        for (uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
        {
            if (m_items[i] == nullptr)
                continue;

            m_items[i]->BuildCreateUpdateBlockForPlayer(data, target);
        }
    }

    Unit::BuildCreateUpdateBlockForPlayer(data, target);
}

2)源码枚举

enum EquipmentSlots : uint8                                 // 19 slots
{
    EQUIPMENT_SLOT_START        = 0,
    EQUIPMENT_SLOT_HEAD         = 0,
    EQUIPMENT_SLOT_NECK         = 1,
    EQUIPMENT_SLOT_SHOULDERS    = 2,
    EQUIPMENT_SLOT_BODY         = 3,
    EQUIPMENT_SLOT_CHEST        = 4,
    EQUIPMENT_SLOT_WAIST        = 5,
    EQUIPMENT_SLOT_LEGS         = 6,
    EQUIPMENT_SLOT_FEET         = 7,
    EQUIPMENT_SLOT_WRISTS       = 8,
    EQUIPMENT_SLOT_HANDS        = 9,
    EQUIPMENT_SLOT_FINGER1      = 10,
    EQUIPMENT_SLOT_FINGER2      = 11,
    EQUIPMENT_SLOT_TRINKET1     = 12,
    EQUIPMENT_SLOT_TRINKET2     = 13,
    EQUIPMENT_SLOT_BACK         = 14,
    EQUIPMENT_SLOT_MAINHAND     = 15,
    EQUIPMENT_SLOT_OFFHAND      = 16,
    EQUIPMENT_SLOT_RANGED       = 17,
    EQUIPMENT_SLOT_TABARD       = 18,
    EQUIPMENT_SLOT_END          = 19
};
--------------------------------------
EQUIPMENT_SLOT_HEAD:头部
EQUIPMENT_SLOT_NECK:颈部
EQUIPMENT_SLOT_SHOULDERS:肩部
EQUIPMENT_SLOT_BODY:身体
EQUIPMENT_SLOT_CHEST:胸部
EQUIPMENT_SLOT_WAIST:腰部
EQUIPMENT_SLOT_LEGS:腿部
EQUIPMENT_SLOT_FEET:脚部
EQUIPMENT_SLOT_WRISTS:手腕
EQUIPMENT_SLOT_HANDS:手部
EQUIPMENT_SLOT_FINGER1:手指1
EQUIPMENT_SLOT_FINGER2:手指2
EQUIPMENT_SLOT_TRINKET1:饰品1
EQUIPMENT_SLOT_TRINKET2:饰品2
EQUIPMENT_SLOT_BACK:背部
EQUIPMENT_SLOT_MAINHAND:主手
EQUIPMENT_SLOT_OFFHAND:副手
EQUIPMENT_SLOT_RANGED:远程
EQUIPMENT_SLOT_TABARD:战袍

七、额外补充(物品的创建过程)

  • 缺点
    他这个id其实创建不好,组合起来会有重复情况
    不如
    ①32位给事件戳,time(nullptr)返回32位时间戳能用到2038年
    ②5位给服务器类型,可以32种服务器类型
    ③5位给服务器id,同种类型可以扩到8个
    ⑥8位给obj类型,可以扩展到64种obj类型
    ⑦14位给同一秒级产生的id,上限是16384
#include 
#include 

// 定义位数分配
#define TIMESTAMP_BITS 32
#define SERVER_TYPE_BITS 5
#define SERVER_ID_BITS 5
#define OBJ_TYPE_BITS 8
#define SEQ_ID_BITS 14

// 定义位偏移
#define SERVER_TYPE_SHIFT (TIMESTAMP_BITS)
#define SERVER_ID_SHIFT (SERVER_TYPE_SHIFT + SERVER_TYPE_BITS)
#define OBJ_TYPE_SHIFT (SERVER_ID_SHIFT + SERVER_ID_BITS)
#define SEQ_ID_SHIFT (OBJ_TYPE_SHIFT + OBJ_TYPE_BITS)

// 定义掩码
#define TIMESTAMP_MASK ((1ULL << TIMESTAMP_BITS) - 1)
#define SERVER_TYPE_MASK ((1ULL << SERVER_TYPE_BITS) - 1)
#define SERVER_ID_MASK ((1ULL << SERVER_ID_BITS) - 1)
#define OBJ_TYPE_MASK ((1ULL << OBJ_TYPE_BITS) - 1)
#define SEQ_ID_MASK ((1ULL << SEQ_ID_BITS) - 1)

// 宏定义生成ID
#define GENERATE_ID(serverType, serverID, objType, seqID) \
    (((time(nullptr) & TIMESTAMP_MASK)) | \
     (((serverType) & SERVER_TYPE_MASK) << SERVER_TYPE_SHIFT) | \
     (((serverID) & SERVER_ID_MASK) << SERVER_ID_SHIFT) | \
     (((objType) & OBJ_TYPE_MASK) << OBJ_TYPE_SHIFT) | \
     (((seqID) & SEQ_ID_MASK) << SEQ_ID_SHIFT))

int main() {
    // 模拟服务器类型、服务器ID、Obj类型和秒级产生的ID
    uint64_t serverType = 7;  // 5位示例值
    uint64_t serverID = 5;    // 5位示例值
    uint64_t objType = 42;    // 8位示例值
    uint64_t seqID = 1234;    // 14位示例值

    // 生成ID并输出
    uint64_t generatedID = GENERATE_ID(serverType, serverID, objType, seqID);
    std::cout << "Generated ID: " << generatedID << std::endl;

    return 0;
}

  • 步骤

①根据物品id去物品表找到原型,然后根据这个配置读取持续时间、堆叠次数等信息

ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);

②如果获得物品大于堆叠次数,那么物品数量就是最大堆叠数量

        if (count > proto->GetMaxStackSize())
            count = proto->GetMaxStackSize();

③构造这个item对象,根据类型是不是背包,若是背包则构建背包(从前面代码可以看到包裹bag继承了item这个类)

Item* item = NewItemOrBag(proto);
 -----------------
 inline Item* NewItemOrBag(ItemTemplate const* proto)
{
    return (proto->InventoryType == INVTYPE_BAG) ? new Bag : new Item;
}

④根据单例传入的自增id和物品原型id自增填充Item这个对象的内容
而实际调用的是Item::Create函数:

bool Item::Create(ObjectGuid::LowType guidlow
, uint32 itemId, Player const* owner)

<1>这里的guildlow返回的是item这个道具id的Mananger对应的自增id
<2>接着调用内部的_create函数

guidhigh:  0x4000
entry:     0
guildlow:  32位的自增id

魔兽3.3.5版本背包模块代码拆分解析(从之前的文章中拆分出来)_第2张图片
调用了ObjectGuid的构造函数
在这里插入图片描述
也就是说:
item_Id是由如下的不同组合而成

1)高48位的obj类型
2)24位到47位表示entry
3)低位32位表示对应mgr的自增id
        if (item->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), itemEntry, player))
        {
            item->SetCount(count);
            return item;
        }
        else
            delete item;
------------------------
bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemId, Player const* owner)
{
    Object::_Create(guidlow, 0, HighGuid::Item);

    SetEntry(itemId);
    SetObjectScale(1.0f);

    if (owner)
    {
        SetGuidValue(ITEM_FIELD_OWNER, owner->GetGUID());
        SetGuidValue(ITEM_FIELD_CONTAINED, owner->GetGUID());
    }

    ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(itemId);
    if (!itemProto)
        return false;

    SetUInt32Value(ITEM_FIELD_STACK_COUNT, 1);
    SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability);
    SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability);

    for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
        SetSpellCharges(i, itemProto->Spells[i].SpellCharges);

    SetUInt32Value(ITEM_FIELD_DURATION, itemProto->Duration);
    SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, 0);
    return true;
}
  • 代码
Item* Item::CreateItem(uint32 itemEntry, uint32 count, Player const* player /*= nullptr*/)
{
    if (count < 1)
        return nullptr;                                        //don't create item at zero count

    ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
    if (proto)
    {
        if (count > proto->GetMaxStackSize())
            count = proto->GetMaxStackSize();

        ASSERT_NODEBUGINFO(count != 0 && "pProto->Stackable == 0 but checked at loading already");

        Item* item = NewItemOrBag(proto);
        if (item->Create(sObjectMgr->GetGenerator<HighGuid::Item>().Generate(), itemEntry, player))
        {
            item->SetCount(count);
            return item;
        }
        else
            delete item;
    }
    else
        ABORT();
    return nullptr;
}

八、代码

class TC_GAME_API Bag : public Item
{
    public:
        Bag();
        ~Bag();

        void AddToWorld() override;
        void RemoveFromWorld() override;

        bool Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owner) override;

        void StoreItem(uint8 slot, Item* pItem, bool update);
        void RemoveItem(uint8 slot, bool update);

        Item* GetItemByPos(uint8 slot) const;
        uint32 GetItemCount(uint32 item, Item* eItem = nullptr) const;
        uint32 GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipItem = nullptr) const;

        uint8 GetSlotByItemGUID(ObjectGuid guid) const;
        bool IsEmpty() const;
        uint32 GetFreeSlots() const;
        uint32 GetBagSize() const { return GetUInt32Value(CONTAINER_FIELD_NUM_SLOTS); }

        // DB operations
        // overwrite virtual Item::SaveToDB
        void SaveToDB(CharacterDatabaseTransaction trans) override;
        // overwrite virtual Item::LoadFromDB
        bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry) override;
        // overwrite virtual Item::DeleteFromDB
        void DeleteFromDB(CharacterDatabaseTransaction trans) override;

        void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override;

        std::string GetDebugInfo() const override;

    protected:

        // Bag Storage space
        Item* m_bagslot[MAX_BAG_SIZE];
};

你可能感兴趣的:(服务器,笔记,学习)