找回密码
 registerss
搜索
查看: 25|回复: 0

洱海月 v0-07-0300 封包解析:ID 163 GCBoxItemList(场景物品箱内容列表)

[复制链接]
  • 打卡等级:本地老炮
  • 打卡总天数:603
  • 打卡月天数:1
  • 打卡总奖励:600
  • 最近打卡:2026-10-03 06:32:28
Waylee 发表于 2026-4-13 16:51 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?registerss

×

包信息

项目 内容
封包 ID 163
包名 GCBoxItemList
方向 GC 服务端 -> 客户端
包长 动态
直接发送 场景服
主要关联交互 GCNewItemBox / CGPickBoxItem / GCPickResult
主要作用 打开场景物品箱或采集点后,把箱内物品列表一次性下发给客户端

当前 Game.exe 可直接确认:

  • GetPacketID():sub_1405ECD90,返回 163
  • GetPacketSize():sub_1405EEC10,固定头 7 字节,再逐项累加 ItemInfo 长度
  • Read():sub_1405FFF10
  • Write():sub_1405FFFB0
  • Execute():sub_1405FFF00 -> sub_140333950

包体结构

偏移 长度 字段 类型 含义
0x00 0x04 m_objID uint32 场景中 itembox 对象 ID
0x04 0x01 size uint8 本次下发的物品条目数,读写时都会限制最大 10
0x05 0x02 item_box_type int16 物品箱类型;当前 Lua 仅确认 -1 掉落箱、0 生长点箱
0x07 动态 item_list ItemInfo[size] 按顺序序列化的物品条目列表

当前 Game.exe 与 Lua 的读写顺序完全一致:

read(m_objID, 4);
read(size, 1);
read(item_box_type, 2);
for (i = 0; i < size; ++i)
    read(ItemInfo[i]);
stream:writeuint(self.m_objID)
stream:writeuchar(self.size)
stream:writeshort(self.item_box_type)
for i = 1, self.size do
    packet.ItemInfo.bos(self.item_list[i], stream)
end

需要单独注意两点:

  • size 在客户端 Read() 和服务端 bos() 中都会被裁到 10,不是纯粹的信任字段。
  • item_list 不是固定长度尾部。GetPacketSize() 先取固定头 7 字节,再通过 sub_1405B3BA0() 逐项累计每个 ItemInfo 的实际序列化长度,因此整包长度取决于具体物品类型。

当前实际行为

  • 当前 Lua 发送点有两条主链:
    • /home/ubuntu/Game2/services/scene/scenecore.lua 的 char_open_box_item():玩家主动打开场景物品箱时下发列表。
    • /home/ubuntu/Game2/services/scene/ability/generic_gather.lua 的 on_proc_success():采集成功后,如果目标是 itembox 且内部有物品,则直接下发列表。
  • 两条主链都会从 itembox 的容器里读取原始物品数据,逐项执行 item:copy_raw_data() 后塞进 msg.item_list。
  • 当前 itembox 容器在 /home/ubuntu/Game2/services/scene/obj/itembox.lua 初始化时固定 init(10, {}, define.CONTAINER_INDEX.ITEM_BOX),因此 size <= 10 不只是网络层限制,也符合当前场景箱体容器容量。
  • item_box_type 当前服务端枚举只有两种:
    • define.ITEMBOX_TYPE.ITYPE_DROPBOX = -1
    • define.ITEMBOX_TYPE.ITYPE_GROWPOINT = 0
  • 客户端 Execute() 的实际行为不是简单缓存包体,而是:
    • 通过 m_objID 找到场景里的箱体对象
    • 清理当前物品箱展示
    • 按 size 遍历 item_list
    • 为每个条目解析物品模板、复制 ItemInfo 原始数据并挂到界面槽位
    • 在当前交互对象仍然有效时刷新开箱界面状态

这说明 GCBoxItemList 的定位是“展示箱内可拾取内容”,而不是“执行拾取结果”;真正拾取单个条目的成功或失败,由 GCPickResult 承担。

服务端落点

  • /home/ubuntu/Game2/services/game/packet.lua
    • 定义 packet.GCBoxItemList
    • bis() / bos() 都按 m_objID -> size -> item_box_type -> item_list 顺序处理
  • /home/ubuntu/Game2/services/scene/scenecore.lua
    • char_open_box_item() 构造并发送 GCBoxItemList
  • /home/ubuntu/Game2/services/scene/ability/generic_gather.lua
    • on_proc_success() 在采集型箱体上直接发送 GCBoxItemList
  • /home/ubuntu/Game2/services/scene/obj/itembox.lua
    • 初始化 itembox 容器容量为 10
    • 提供 get_item_box_type() 作为 item_box_type 来源
  • /home/ubuntu/Game2/lualib/define.lua
    • 定义 ITEMBOX_TYPE 当前已确认枚举值

关键代码

packet.GCBoxItemList = {
    xy_id = packet.XYID_GC_ITEM_BOX_LIST,
    bis = function(self, buffer)
        local stream = bistream.new()
        stream:attach(buffer)
        self.m_objID = stream:readuint()
        self.size = stream:readuchar()
        self.size = self.size > 10 and 10 or self.size
        self.item_box_type = stream:readshort()
        self.item_list = {}
        for i = 1, self.size do
            local item = packet.ItemInfo.new()
            item:bis(stream)
            self.item_list[i] = item
        end
    end,
    bos = function(self)
        local stream = bostream.new()
        stream:writeuint(self.m_objID)
        stream:writeuchar(self.size)
        self.size = self.size > 10 and 10 or self.size
        stream:writeshort(self.item_box_type)
        for i = 1, self.size do
            packet.ItemInfo.bos(self.item_list[i], stream)
        end
        return stream:get()
    end
}
local msg = packet_def.GCBoxItemList.new()
local container = obj_box:get_container()
local list = {}
local item_list = container:get_item_data()
for i = 0, container:get_size() do
    local item = item_list[i]
    if item then
        table.insert(list, item:copy_raw_data())
    end
end
msg.m_objID = box_item.m_objID
msg.size = #list
msg.item_list = list
msg.item_box_type = obj_box:get_item_box_type()
self:send2client(me, msg)
__int64 __fastcall sub_1405EEC10(__int64 a1)
{
  unsigned int total = 7;
  for (int i = 0; i < *(unsigned __int8 *)(a1 + 32); ++i)
    total += sub_1405B3BA0(a1 + 36 + 188LL * i);
  return total;
}

结论

GCBoxItemList 是当前 Temp_Release_v0-07-0300 里用于“打开场景物品箱内容列表”的动态回包。它的固定头很小,只有 m_objID + size + item_box_type 共 7 字节,真正的大头在后面的 ItemInfo[size] 变长数组。

从当前 Lua 和 Game.exe 的一致证据看,这条包的实际语义已经很明确:

  • 它只负责告诉客户端“这个箱子里现在有哪些物品”
  • 它不负责单个物品的拾取结算
  • 它当前最多承载 10 个条目
  • item_box_type 在当前服务端只稳定出现掉落箱 -1 和生长点箱 0

因此,GCBoxItemList 最准确的理解不是“通用物品列表包”,而是“绑定具体场景 itembox 对象的开箱内容同步包”。

您需要登录后才可以回帖 登录 | registerss

本版积分规则

QQ|手机版|雪舞知识库 ( 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 )|天天打卡

GMT+8, 2026-10-4 11:26 , Processed in 0.090104 second(s), 25 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表