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

洱海月 v0-07-0300 封包解析:ID 587 CGPlayerShopMoney(玩家商店资金存取请求)

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

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

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

×

CGPlayerShopMoney 是旧版客户端里管理九州商会店铺资金时使用的请求包。当前 Game.exe 与 Lua 服务端都能确认,这条包固定携带目标店铺 GUID、操作类型、资金类型、金额和店铺流水号。

包信息

项目 内容
封包 ID 587
包名 CGPlayerShopMoney
相关回包 GCPlayerShopMoney
相关回包 ID 327
错误回包 GCPlayerShopError
错误回包 ID 207
作用 对指定玩家商店执行本钱或利润的存入、提取操作

包体结构

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

偏移 长度 字段 类型 含义
0x00 0x02 shop_id.world uint16 商店 GUID 的 world 段
0x02 0x04 shop_id.server uint32 商店 GUID 的 server 段
0x06 0x04 shop_id.scene uint32 商店 GUID 的 scene 段
0x0A 0x02 shop_id.pollpos uint16 商店 GUID 的 pollpos 段
0x0C 0x01 opt uint8 操作类型,0=存钱,1=取钱
0x0D 0x01 type uint8 资金类型,0=base_money,1=profit_money
0x0E 0x04 amount uint32 本次操作金额
0x12 0x01 serial uint8 店铺流水号,用于并发校验

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

19

当前实际行为 / 模式说明

  • 当前 Game.exe 中,CGPlayerShopMoney::GetPacketID() 返回 587,GetPacketSize() 返回 19。
  • 当前 Game.exe 的 Read() / Write() 顺序固定为:
    • shop_id
    • opt
    • type
    • amount
    • serial
  • 当前 Game.exe 的 Execute() 对客户端侧是空处理,直接返回 2。
  • 当前 Lua 请求入口 request:CGPlayerShopMoney() 会先检查交易限制;被限制交易时直接提示并拒绝。
  • 当前场景服处理会先按 shop_id 查找商店;查不到会直接触发 assert。
  • 当前版本只有以下身份可以操作店铺资金:
    • 店主
    • 店铺合伙人
  • 如果商店已经处于 STATUS_PLAYER_SHOP_ON_SALE,当前处理会直接返回,不发送任何结果包。
  • 如果请求里的 serial 与店铺当前流水号不一致,服务端会回 GCPlayerShopError(207),错误码为 ERR_SHOP_SALE_OUT_SERIAL_IS_CHANGED。
  • 当 opt = 0 表示存钱:
    • amount < 1 或人物当前金钱不足时,直接返回
    • type = 0 时写入 base_money
    • type = 1 时写入 profit_money
    • 实际入账金额按 amount * 0.97 计算,当前实现固定扣除 3%
    • 存入本钱时额外限制:base_money + amount <= 10000000
  • 当 opt = 1 表示取钱:
    • type = 0 时从 base_money 提取
    • type = 1 时从 profit_money 提取
    • 店铺对应资金不足或 amount < 1 时,直接返回
    • 提取成功后,人物获得的金额是完整的 amount
  • 当前 GCPlayerShopMoney(327) 回包固定带回:
    • shop_id
    • type
    • amount
    • serial
  • 当前回包里的 amount 在不同分支里的含义并不完全一致:
    • 存入本钱:回当前本钱余额
    • 存入利润:回当前利润余额
    • 提取本钱:回本次提取金额
    • 提取利润:回提取后的剩余利润
  • 当前存钱分支没有对非法 type 做兜底拦截;如果 type 既不是 0 也不是 1,人物金钱仍会被扣除,但店铺资金不会更新,也不会发送成功回包。
  • 当前取钱分支遇到非法 type 只会写日志,不会给客户端返回错误包。

服务端落点

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

关键代码

当前 Lua 协议定义

packet.CGPlayerShopMoney = {
    xy_id = packet.XYID_CG_PLAYER_SHOP_MONEY,
    OPT = {
        OPT_SAVE_MONEY = 0,
        OPT_GET_MONEY = 1,
    },
    TYPE = {
        TYPE_BASE_MONEY = 0,
        TYPE_PROFIT_MONEY = 1,
    },
    bis = function(self, buffer)
        local stream = bistream.new()
        stream:attach(buffer)
        self.shop_id = packet.PlayerShopGUID.new()
        self.shop_id:bis(stream)
        self.opt = stream:readuchar()
        self.type = stream:readuchar()
        self.amount = stream:readuint()
        self.serial = stream:readuchar()
    end
}

当前 Lua 请求入口

function request:CGPlayerShopMoney()
    if ma_func:check_right_limit_exchange() then
        ma_func:notify_tips("账号限制交易,请联系客服处理")
        return
    end
    skynet.call(my_scene, "lua", "char_player_shop_money", my_obj_id, self)
end

当前 Lua 场景处理

function scenecore:char_player_shop_money(who, psm)
    local obj_me = self.objs[who]
    local playershop = playershopmanager:get_player_shop_by_guid(psm.shop_id)
    assert(playershop, table.tostr(psm.shop_id))
    if playershop:get_status() == playershop.STATUS.STATUS_PLAYER_SHOP_ON_SALE then
        return
    end
    local is_mine = playershop:get_owner_guid() == obj_me:get_guid()
    local can_manager = playershop:is_partner(obj_me:get_guid())
    if not is_mine and not can_manager then
        return
    end
    if psm.serial ~= playershop:get_serial() then
        local msg = packet_def.GCPlayerShopError.new()
        msg.error_code = define.PLAYER_SHOP_ERR.ERR_SHOP_SALE_OUT_SERIAL_IS_CHANGED
        self:send2client(obj_me, msg)
        return
    end
    local msg = packet_def.GCPlayerShopMoney.new()
    msg.shop_id = playershop:get_guid()
    if psm.opt == packet_def.CGPlayerShopMoney.OPT.OPT_SAVE_MONEY then
        if psm.amount < 1 or obj_me:get_money() < psm.amount then
            return
        end
        if psm.type == packet_def.CGPlayerShopMoney.TYPE.TYPE_BASE_MONEY then
            local base_money = playershop:get_base_money()
            if base_money + psm.amount > 10000000 then
                return
            end
            local real_base_money = base_money + psm.amount * (1 - 0.03)
            playershop:set_base_money(real_base_money)
            msg.type = psm.type
            msg.amount = real_base_money
            msg.serial = playershop:inc_serial()
            self:send2client(obj_me, msg)
        elseif psm.type == packet_def.CGPlayerShopMoney.TYPE.TYPE_PROFIT_MONEY then
            local profit_money = playershop:get_profit_money()
            local real_profit_money = profit_money + psm.amount * (1 - 0.03)
            playershop:set_profit_money(real_profit_money)
            msg.type = psm.type
            msg.amount = real_profit_money
            msg.serial = playershop:inc_serial()
            self:send2client(obj_me, msg)
        end
        obj_me:set_money(obj_me:get_money() - psm.amount, "九州商会-存钱")
    elseif psm.opt == packet_def.CGPlayerShopMoney.OPT.OPT_GET_MONEY then
        if psm.type == packet_def.CGPlayerShopMoney.TYPE.TYPE_BASE_MONEY then
            local base_money = playershop:get_base_money()
            if psm.amount < 1 or base_money < psm.amount then
                return
            end
            local sub_base_money = base_money - psm.amount
            playershop:set_base_money(sub_base_money)
            obj_me:set_money(obj_me:get_money() + psm.amount, "九州商会-提取利润")
            msg.type = psm.type
            msg.amount = psm.amount
            msg.serial = playershop:inc_serial()
            self:send2client(obj_me, msg)
        elseif psm.type == packet_def.CGPlayerShopMoney.TYPE.TYPE_PROFIT_MONEY then
            local profit_money = playershop:get_profit_money()
            if psm.amount < 1 or profit_money < psm.amount then
                return
            end
            local sub_profit_money = profit_money - psm.amount
            playershop:set_profit_money(sub_profit_money)
            obj_me:set_money(obj_me:get_money() + psm.amount, "九州商会-提取利润")
            msg.type = psm.type
            msg.amount = sub_profit_money
            msg.serial = playershop:inc_serial()
            self:send2client(obj_me, msg)
        end
    end
end

当前 Game.exe 的读写与执行结论

GetPacketID() = 587;
GetPacketSize() = 19;

Read(stream) {
    read_bytes(shop_id, 12);
    read_u8(opt);
    read_u8(type);
    read_u32(amount);
    read_u8(serial);
}

Write(stream) {
    write_bytes(shop_id, 12);
    write_u8(opt);
    write_u8(type);
    write_u32(amount);
    write_u8(serial);
}

Execute() {
    return 2;
}

结论

这条包在当前旧版项目里要记住七点:

  1. CGPlayerShopMoney 的封包 ID 是 587。
  2. 它是固定 19 字节结构:PlayerShopGUID(12) + opt(1) + type(1) + amount(4) + serial(1)。
  3. opt 决定是存钱还是取钱,type 决定操作本钱还是利润。
  4. 当前版本只有店主或合伙人可操作,且店铺处于已盘出状态时不会执行。
  5. 存钱时角色被扣的是完整 amount,店铺实际只入账 97%。
  6. 当前回包 GCPlayerShopMoney(327) 的 amount 字段在不同分支下含义并不统一,读取时必须结合操作类型判断。
  7. 当前存钱分支对非法 type 缺少兜底保护,存在扣除角色金钱但不更新店铺资金的现状。
您需要登录后才可以回帖 登录 | registerss

本版积分规则

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

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

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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