GCDoubleExpInfo 是当前版本双倍经验系统的正式状态回包。当前 Game.exe 与当前 Lua 服务端都能确认,这条包固定长度为 20 字节,结构稳定为 5 个连续的 uint32 字段,用来同步剩余可领取小时数、冻结状态、付费双倍剩余时间、总双倍剩余时间和重置时间戳。
当前 Temp_Release_v0-07-0300 的现役查询链已经明确切到:
- 客户端请求:
CGAskDoubleExp(709)
- 服务端回包:
GCDoubleExpInfo(675)
旧版客户端里虽然还保留 CGCheckDoubleExp(985),但当前 Lua 主链实际不再接那条请求。
包信息
| 项目 |
内容 |
| 封包 ID |
675 |
| 包名 |
GCDoubleExpInfo |
| 方向 |
GC 服务端 -> 客户端 |
| 固定包长 |
20 |
| 当前 Lua 协议定义 |
/home/ubuntu/Game2/services/game/packet.lua |
| 当前 Lua 主要发送点 |
/home/ubuntu/Game2/services/scene/obj/human.lua |
| 当前请求入口 |
/home/ubuntu/Game2/services/msgagent.lua、/home/ubuntu/Game2/services/scene/scenecore.lua |
| 上游请求 |
CGAskDoubleExp |
| 主要作用 |
向客户端同步当前角色的双倍经验状态 |
当前 Game.exe 的虚函数结论为:
GetPacketID() = 675;
GetPacketSize() = 20;
包体结构
按当前 Game.exe 与当前 Lua 服务端现状,这条包固定为 20 字节。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
available_hour |
uint32 |
当前还能领取的双倍经验小时数 |
0x04 |
0x04 |
is_lock |
uint32 |
双倍经验是否冻结,0=未冻结,1=冻结 |
0x08 |
0x04 |
money_time |
uint32 |
当前剩余的付费双倍经验秒数 |
0x0C |
0x04 |
total_time |
uint32 |
当前总双倍经验剩余秒数,现役 Lua 写法为 money_time + free_time |
0x10 |
0x04 |
rtime |
uint32 |
双倍经验周重置或状态参考时间戳 |
当前 Lua 协议对象里虽然在 ctor() 中残留了一个 unknow_3 = 0,但它没有参与 bis() / bos() 的读写,也不是实际包体字段。当前线上的真实协议字段就是上面这 5 个 uint32。
当前实际行为 / 模式说明
当前版本的双倍经验查询主链非常直接:
- 客户端发送空包
CGAskDoubleExp
msgagent.lua 把请求转给场景服 char_ask_double_exp_info
- 场景对象
human:send_double_exp_info() 组装 GCDoubleExpInfo
- 客户端收到后更新本地双倍经验状态并触发界面刷新
当前 Lua 的字段写法可以直接说明语义:
available_hour 来自 self.double_exp_info.available_hour
is_lock 来自 self.double_exp_info.is_lock and 1 or 0
money_time 来自 math.floor(self.double_exp_info.money_time)
total_time 来自 math.floor(self.double_exp_info.money_time) + math.floor(self.double_exp_info.free_time)
rtime 直接沿用 self.double_exp_info.rtime
结合当前 script_base.lua 的辅助接口,可以进一步确认:
available_hour 表示当前剩余可提取的周双倍经验小时数
money_time 是商城或付费来源的双倍经验剩余时间
free_time 是免费双倍经验剩余时间,但它不会单独出现在包体中,而是被合并进 total_time
rtime 会在周重置逻辑 DEResetWeeklyFreeTime() 中被写成 os.time()
当前客户端 Execute() 没有再做复杂解析,而是把这 5 个 uint32 整体写入本地双倍经验状态,再触发一次界面更新事件。这也说明当前协议重点是“完整覆盖同步当前双倍经验状态”,不是增量更新某一个字段。
服务端落点
- 协议定义:
/home/ubuntu/Game2/services/game/packet.lua
- 请求入口:
/home/ubuntu/Game2/services/msgagent.lua
- 场景转发:
/home/ubuntu/Game2/services/scene/scenecore.lua
- 状态发送:
/home/ubuntu/Game2/services/scene/obj/human.lua
- 双倍经验辅助逻辑:
/home/ubuntu/Game2/lualib/script_base.lua
当前发送入口非常明确,scenecore:char_ask_double_exp_info() 只做一件事,就是调用角色对象的 send_double_exp_info()。因此这条包在现役版本里没有多级包装或中转变形,协议字段可以直接按 Lua 发送值理解。
关键代码
当前 Lua 协议定义
packet.GCDoubleExpInfo = {
xy_id = packet.XYID_GC_DOUBLE_EXP_INFO,
bis = function(self, buffer)
local stream = bistream.new()
stream:attach(buffer)
self.available_hour = stream:readuint()
self.is_lock = stream:readuint()
self.money_time = stream:readuint()
self.total_time = stream:readuint()
self.rtime = stream:readuint()
end,
bos = function(self)
local stream = bostream.new()
stream:writeuint(self.available_hour)
stream:writeuint(self.is_lock)
stream:writeuint(self.money_time)
stream:writeuint(self.total_time)
stream:writeuint(self.rtime)
return stream:get()
end
}
当前 Lua 发送链
function scenecore:char_ask_double_exp_info(who)
local obj = self.objs[who]
obj:send_double_exp_info()
end
function human:send_double_exp_info()
local msg = packet_def.GCDoubleExpInfo.new()
msg.available_hour = self.double_exp_info.available_hour
msg.is_lock = self.double_exp_info.is_lock and 1 or 0
msg.money_time = math.floor(self.double_exp_info.money_time)
msg.total_time = math.floor(self.double_exp_info.money_time) + math.floor(self.double_exp_info.free_time)
msg.rtime = self.double_exp_info.rtime
self:get_scene():send2client(self, msg)
end
当前 Game.exe 的读写结论
Read(stream) {
read_u32(available_hour);
read_u32(is_lock);
read_u32(money_time);
read_u32(total_time);
read_u32(rtime);
return 1;
}
Write(stream) {
write_u32(available_hour);
write_u32(is_lock);
write_u32(money_time);
write_u32(total_time);
write_u32(rtime);
return 1;
}
当前 Game.exe 的执行结论
Execute(packet) {
ClientDoubleExp.available_hour = packet.available_hour;
ClientDoubleExp.is_lock = packet.is_lock;
ClientDoubleExp.money_time = packet.money_time;
ClientDoubleExp.total_time = packet.total_time;
ClientDoubleExp.rtime = packet.rtime;
NotifyUiEvent(368);
return 2;
}
这里的状态名是按当前 Execute() 实际行为做的语义化整理:客户端确实会把包内这 5 个值按顺序写入本地状态,再触发一次双倍经验界面刷新事件。
结论
GCDoubleExpInfo 的封包 ID 是 675,固定长度 20 字节。
- 当前
Game.exe 与当前 Lua 一致确认,包体就是 5 个连续的 uint32 字段。
- 当前字段名可以直接确定为
available_hour、is_lock、money_time、total_time、rtime,不需要再保留 unknow_x。
- 当前现役查询链是
CGAskDoubleExp -> GCDoubleExpInfo,而不是旧的 CGCheckDoubleExp。
total_time 在当前 Lua 里等于 money_time + free_time,因此它代表客户端展示使用的总双倍经验剩余时间。