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

洱海月 v0-07-0300 封包解析:ID 799 GCStallBuy(摆摊购买成功通知)

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

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

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

×

GCStallBuy 是摆摊购买成功后的结果通知包。当前 Game.exe 与当前 Lua 服务端都能确认,这条包固定长度 35 字节,成功成交后会同时发给买家和摊主,用来同步成交对象、目标槽位、摊位序列号、摊位币种、成交价与税后实收金额。失败分支不会发送这条包,而是改走 GCStallError。

包信息

项目 内容
封包 ID 799
包名 GCStallBuy
方向 Server -> Client
对应请求 CGStallBuy
对应请求 ID 792
包长模式 固定长度
包长 35
作用 通知摆摊购买成功,并同步买家或摊主界面的成交结果

包体结构

偏移 长度 字段 类型 含义
0x00 0x04 stall_obj_id int32 摊主角色 obj_id,客户端据此区分“我买了别人摊位”还是“我的摊位被买走”
0x04 0x08 item_guid bytes[8] 道具 GUID;购买道具时有效,内部结构为 world(1) + server(1) + mask(2) + series(4)
0x0C 0x08 pet_guid bytes[8] 珍兽 GUID;购买珍兽时有效
0x14 0x04 serial uint32 摊位该槽位成交后的最新序列号,用于客户端刷新缓存一致性
0x18 0x01 to_index uint8 成交后在目标容器中的落点槽位
0x19 0x01 to_type uint8 目标容器类型;当前现役值仅见 1=背包道具、3=珍兽栏
0x1A 0x01 is_yuanbao_stall uint8 是否为元宝摊位,0=金币摊位,1=元宝摊位
0x1B 0x04 cost uint32 买家本次成交实际支付金额
0x1F 0x04 profit uint32 摊主本次成交税后实收金额

当前实际行为

  • 当前 Game.exe 的 GetPacketID() 固定返回 799。
  • 当前 Game.exe 的 GetPacketSize() 固定返回 35。
  • 当前 Game.exe 的 Read() / Write() 顺序与当前 Lua packet.GCStallBuy 一致,字段顺序固定为:
    • stall_obj_id
    • item_guid
    • pet_guid
    • serial
    • to_index
    • to_type
    • is_yuanbao_stall
    • cost
    • profit
  • 当前 msgagent 仅在普通场景 scene_type == 0 时转入 /home/ubuntu/Game2/services/scene/scenecore.lua 的 char_stall_buy()。
  • 当前 Lua 只在成交成功时构造 GCStallBuy;库存变化、背包空间不足、金额不足、摊位副本过期等失败分支都改发 GCStallError。
  • 当前 Lua 的成交成功分支会把同一份 GCStallBuy 同时发给买家和摊主:
    • 买道具成功时写 to_type = 1
    • 买珍兽成功时写 to_type = 3
    • cost = need_money
    • profit = floor(need_money * (1 - trade_tax / 100))
    • is_yuanbao_stall = stall_box:get_is_yuanbao_stall() and 1 or 0
  • 当前 Game.exe 的 Execute() 明确只处理 to_type == 1 或 to_type == 3,并且 is_yuanbao_stall 只接受 0/1 两种值;其他值不会进入现役更新分支。
  • 当 stall_obj_id 等于客户端当前打开的他人摊位 obj_id 时,客户端把这条包视为“我购买别人摊位成功”:
    • to_type == 1 时把摊位中的道具移动到自己背包 to_index
    • to_type == 3 时把摊位中的珍兽移动到自己珍兽栏 to_index
    • 同时清空对方摊位对应格、价格和旧序列号
  • 当 stall_obj_id 等于自己角色的 ServerID 时,客户端把这条包视为“我的摊位物品被别人买走”:
    • to_type == 1 时从自己的摊位和背包里移除已售道具
    • to_type == 3 时从自己的摊位和珍兽列表里移除已售珍兽
    • 同样会刷新摊位槽位价格与最新序列号
  • cost 与 profit 在当前客户端主要用于成交提示文案,实际扣钱和加钱动作已经在服务端完成。

老源码可作为参考:旧工程残留 Common/Packets/GCStallBuy.h/.cpp 里只有 stall_obj_id / item_guid / pet_guid / serial / to_index / to_type 六段字段,总长 26 字节。当前 Game.exe 已明确扩展出 is_yuanbao_stall / cost / profit 三段尾部字段,因此旧头文件不能直接视为当前版本最终结构。

服务端落点

  • 协议定义:
    • /home/ubuntu/Game2/services/game/packet.lua
  • 请求入口:
    • /home/ubuntu/Game2/services/msgagent.lua
  • 主场景处理:
    • /home/ubuntu/Game2/services/scene/scenecore.lua
  • 热更副本:
    • /home/ubuntu/Game2/services/scripts/scene.lua

关键代码

当前 Lua 协议定义

packet.GCStallBuy = {
    xy_id = packet.XYID_GC_STALL_BUY,
    bis = function(self, buffer)
        local stream = bistream.new()
        stream:attach(buffer)
        self.m_objID = stream:readint()
        self.item_guid = packet.ItemGUID.new()
        self.item_guid:bis(stream)
        self.pet_guid = packet.PetGUID.new()
        self.pet_guid:bis(stream)
        self.serial = stream:readuint()
        self.to_index = stream:readuchar()
        self.to_type = stream:readuchar()
        self.is_yuanbao_stall = stream:readuchar()
        self.cost = stream:readuint()
        self.profit = stream:readuint()
    end,
    bos = function(self)
        local stream = bostream.new()
        stream:writeint(self.m_objID)
        packet.ItemGUID.bos(self.item_guid, stream)
        packet.PetGUID.bos(self.pet_guid, stream)
        stream:writeuint(self.serial)
        stream:writeuchar(self.to_index)
        stream:writeuchar(self.to_type)
        stream:writeuchar(self.is_yuanbao_stall or 0)
        stream:writeuint(self.cost)
        stream:writeuint(self.profit)
        return stream:get()
    end
}

当前 Lua 发送逻辑

msg.to_type = 1
msg.item_guid = sb.item_guid
msg.pet_guid = sb.pet_guid
msg.serial = stall_box:get_serial_by_index(index)
msg.to_index = dest_index
msg.is_yuanbao_stall = stall_box:get_is_yuanbao_stall() and 1 or 0
msg.cost = need_money
msg.profit = profit
self:send2client(obj, msg)
self:send2client(stall_human, msg)

当前 Game.exe 结论

GetPacketID()   = 799;
GetPacketSize() = 35;

Read(stream) {
    stall_obj_id      = read_i32();
    item_guid         = read_bytes(8);
    pet_guid          = read_bytes(8);
    serial            = read_u32();
    to_index          = read_u8();
    to_type           = read_u8();
    is_yuanbao_stall  = read_u8();
    cost              = read_u32();
    profit            = read_u32();
}

Execute() {
    if (stall_obj_id == current_open_other_stall_obj_id) {
        // 购买他人摊位成功,更新自己的背包或珍兽栏
    } else if (stall_obj_id == my_server_id) {
        // 自己摊位的货物被卖出,更新自己的摊位与库存
    }
    return 2;
}

结论

GCStallBuy(799) 在 Temp_Release_v0-07-0300 中是摆摊购买成功后的固定 35 字节结果包,不是旧 26 字节头文件可以直接覆盖的历史结构。当前版本最终应以当前 Game.exe 和当前 Lua 为准:to_type 现役只处理 1=背包道具、3=珍兽栏,新增尾部字段 is_yuanbao_stall / cost / profit 分别负责同步摊位币种、买家成交金额和摊主税后实收。当前服务端已经按实际摊位类型写入 is_yuanbao_stall,因此客户端的金币摊位与元宝摊位提示分支也能与现状保持一致。

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

本版积分规则

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

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

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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