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;
}
结论
这条包在当前旧版项目里要记住七点:
CGPlayerShopMoney 的封包 ID 是 587。
- 它是固定
19 字节结构:PlayerShopGUID(12) + opt(1) + type(1) + amount(4) + serial(1)。
opt 决定是存钱还是取钱,type 决定操作本钱还是利润。
- 当前版本只有店主或合伙人可操作,且店铺处于已盘出状态时不会执行。
- 存钱时角色被扣的是完整
amount,店铺实际只入账 97%。
- 当前回包
GCPlayerShopMoney(327) 的 amount 字段在不同分支下含义并不统一,读取时必须结合操作类型判断。
- 当前存钱分支对非法
type 缺少兜底保护,存在扣除角色金钱但不更新店铺资金的现状。