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

洱海月 v0-07-0300 封包解析:ID 128 GCPlayerShopApply(玩家商店申请信息下发)

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

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

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

×

GCPlayerShopApply 是当前版本里用于向客户端下发“玩家商店申请信息”的结果包。现役 /home/ubuntu/Game2 主链中,这条包不是由 CGPlayerShopApply 请求链驱动,而是由 NPC 脚本直接调用 ApplyPlayerShop() 构造发送,用来告诉客户端当前开店 NPC、佣金系数、申请费用,以及玩家当前还能申请的店铺类型。

包信息

项目 内容
封包 ID 128
包名 GCPlayerShopApply
方向 Server -> Client
对应请求 无专用请求;现役由脚本直接下发
固定包长 14
作用 下发玩家商店申请面板所需的 NPC、佣金、费用与可申请店铺类型

包体结构

当前 Game.exe 与现役 Lua 协议定义都能确认,这条包固定长度为 14 字节。

偏移 长度 字段 类型 含义
0x00 0x04 npc_id int32 当前开店 NPC ID
0x04 0x04 com_factor float32 当前佣金系数
0x08 0x04 cost uint32 当前申请费用
0x0C 0x01 shop_type uint8 可申请店铺类型;当前 1=ITEM、2=PET、3=BOTH
0x0D 0x01 reserved_flag uint8 保留字节;现役 Lua 字段名仍为 unknow,当前主链默认发送 0

当前 Game.exe 的 GetPacketSize() 返回固定值:

14

当前实际行为

  • 当前 Game.exe 中,GCPlayerShopApply::GetPacketID() 返回 128,GetPacketSize() 返回 14。
  • 当前 Game.exe 的 Read() / Write() 顺序可直接确认:
    • npc_id
    • com_factor
    • cost
    • shop_type
    • reserved_flag
  • 当前 Game.exe 的 Execute() 会先按 npc_id 查询目标 NPC,再把包内的申请参数写入客户端玩家商店上下文,并触发内部事件 209。
  • 现役 Lua 发送链不走 request:CGPlayerShopApply();真正的发送入口是 script_base:ApplyPlayerShop(selfId, targetId)。
  • 当前脚本发送前会先做两类校验:
    • 角色等级必须达到 30
    • 角色不能同时已经拥有道具店和珍兽店
  • 校验失败时,当前主链发送的是 GCPlayerShopError,不会发送 GCPlayerShopApply。
  • 校验通过时,当前脚本会计算:
    • com_factor = playershopmanager:get_com_factor()
    • cost = 300000 * com_factor * 2 * 1.03
  • 当前 shop_type 的发送规则为:
    • 尚未拥有任意店铺时发送 TYPE_BOTH(3)
    • 只缺道具店时发送 TYPE_ITEM(1)
    • 只缺珍兽店时发送 TYPE_PET(2)
  • 当前代码库里检出的实际触发脚本只有两处,均为洛阳 NPC:
    • oluoyang_qiaofusheng.lua
    • oluoyang_suzhe.lua
  • 当前主链没有检出其他地方对 reserved_flag 的赋值,因此该字节在现役版本里应视为保留位。

服务端落点

  • 协议定义:
    • /home/ubuntu/Game2/services/game/packet.lua
  • 发送逻辑:
    • /home/ubuntu/Game2/lualib/script_base.lua
  • 当前触发脚本:
    • /home/ubuntu/Game2/services/scripts/obj/luoyang/oluoyang_qiaofusheng.lua
    • /home/ubuntu/Game2/services/scripts/obj/luoyang/oluoyang_suzhe.lua

关键代码

当前 Lua 协议定义

packet.GCPlayerShopApply = {
    xy_id = packet.XYID_GC_PLAYER_SHOP_APPLY,
    TYPE = {
        TYPE_ERR = 0,
        TYPE_ITEM = 1,
        TYPE_PET = 2,
        TYPE_BOTH = 3,
    },
    new = function()
        local o = {}
        setmetatable(o, { __index = packet.GCPlayerShopApply })
        o:ctor()
        return o
    end,
    ctor = function(self)
        self.unknow = 0
    end,
    bis = function(self, buffer)
        local stream = bistream.new()
        stream:attach(buffer)
        self.npc_id = stream:readint()
        self.com_factor = stream:readfloat()
        self.cost = stream:readuint()
        self.type = stream:readuchar()
        self.unknow = stream:readuchar()
    end,
    bos = function(self)
        local stream = bostream.new()
        stream:writeint(self.npc_id)
        stream:writefloat(self.com_factor)
        stream:writeint(self.cost)
        stream:writeuchar(self.type)
        stream:writeuchar(self.unknow)
        return stream:get()
    end
}

当前 Lua 发送链

function script_base:ApplyPlayerShop(selfId, targetId)
    local human = self.scene:get_obj_by_id(selfId)
    local com_factor = playershopmanager:get_com_factor()
    local cost = 300000 * com_factor * 2 * 1.03
    local msg = packet_def.GCPlayerShopError.new()
    if human:get_level() < 30 then
        msg.error_code = define.PLAYER_SHOP_ERR.ERR_NOT_ENOUGH_LEVEL
        human:get_scene():send2client(human, msg)
        return
    end
    local shop_guids = human:get_shop_guids()
    local has_item = false
    local has_pet = false
    for _, guid in ipairs(shop_guids) do
        local shop = playershopmanager:get_player_shop_by_guid(guid)
        if shop then
            if shop:get_owner_guid() == human:get_guid() and shop:get_type() == shop.TYPE.ITEM then
                has_item = true
            end
            if shop:get_owner_guid() == human:get_guid() and shop:get_type() == shop.TYPE.PET then
                has_pet = true
            end
        end
    end
    if has_item and has_pet then
        msg.error_code = define.PLAYER_SHOP_ERR.ERR_ALREADY_HAVE_ENOUGH_SHOP
        human:get_scene():send2client(human, msg)
        return
    end
    msg = packet_def.GCPlayerShopApply.new()
    msg.com_factor = com_factor
    msg.cost = cost
    msg.npc_id = targetId
    if not has_item and not has_pet then
        msg.type = msg.TYPE.TYPE_BOTH
    elseif not has_item then
        msg.type = msg.TYPE.TYPE_ITEM
    elseif not has_pet then
        msg.type = msg.TYPE.TYPE_PET
    end
    human:get_scene():send2client(human, msg)
end

当前 Game.exe 结论

Packets::GCPlayerShopApply::vftable
GetPacketID()   -> 128
GetPacketSize() -> 14
Read()          -> read dword, dword, dword, byte, byte
Write()         -> write dword, dword, dword, byte, byte
Execute()       -> cache packet fields and dispatch client event 209

当前 Game.exe 的读写顺序

0x00 npc_id
0x04 com_factor
0x08 cost
0x0C shop_type
0x0D reserved_flag

结论

  1. GCPlayerShopApply(128) 是当前版本里的固定 14 字节结果包,用于下发玩家商店申请界面的参数。
  2. 现役主链中它不是由 CGPlayerShopApply(117) 驱动,而是由 NPC 脚本直接调用 ApplyPlayerShop() 发送。
  3. 这条包的核心字段只有五个:npc_id、com_factor、cost、shop_type、reserved_flag。
  4. shop_type 已可明确写成店铺类型位:1=道具店、2=珍兽店、3=两者都可申请。
  5. 最后一个字节当前没有足够证据给出更强语义,现阶段应按 reserved_flag 处理,不建议继续沿用文档层面的 unknow 命名。
您需要登录后才可以回帖 登录 | registerss

本版积分规则

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

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

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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