WGCRetNormalRankList 是普通排行榜界面的返回包,用于把多个榜单页签、每个榜单的玩家排行数据,以及榜单尾部的领奖状态一起下发给客户端。
包信息
| 项目 |
内容 |
| 封包 ID |
9032 |
| 包名 |
WGCRetNormalRankList |
| 方向 |
服务端 -> 客户端 |
| 对应请求 |
CGWNormalRankingList (9031) |
| 包体长度 |
变长 |
| 当前作用 |
返回普通排行榜页签、每个榜单的玩家列表,以及榜单奖励状态 |
包体结构
包头固定为 23 字节,之后跟随 rank_list_count 个榜单块。Game.exe 的 GetPacketSize() 计算公式为:
23 + Σ(16 + 48 * player_count[i])
其中:
23 是总包头长度。
- 每个榜单块固定头为
10 字节。
- 每个玩家条目固定
48 字节。
- 每个榜单块尾部还有
6 字节奖励状态结构。
总包头结构如下:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
rank_list_count |
uint32 |
本次返回的榜单数量 |
0x04 |
0x04 |
reserved_u32_1 |
uint32 |
客户端会读取,当前 Lua 测试值写死为 11,含义未定 |
0x08 |
0x02 |
reserved_u16_1 |
uint16 |
客户端会读取,当前 Lua 测试值写死为 22,含义未定 |
0x0A |
0x04 |
reserved_u32_2 |
uint32 |
客户端会读取,当前 Lua 测试值写死为 33,含义未定 |
0x0E |
0x01 |
ui_message_type |
int8 |
Lua 注释指向 LevelupMessageType,疑似领奖提示或消息类型 |
0x0F |
0x04 |
cur_rank_type |
int32 |
当前选中的榜单类型 |
0x13 |
变长 |
rank_entries |
rank_entry[rank_list_count] |
榜单块数组 |
单个榜单块的结构如下:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
rank_type |
uint32 |
榜单类型 |
0x04 |
0x01 |
reserved_flag_1 |
int8 |
榜单 UI 控制位,当前 Lua 测试值写 9 |
0x05 |
0x01 |
reserved_flag_2 |
int8 |
榜单 UI 控制位,当前 Lua 测试值写 8 |
0x06 |
0x04 |
player_count |
int32 |
当前榜单内的玩家数量 |
0x0A |
48 * player_count |
players |
rank_player[player_count] |
上榜玩家数组 |
0x0A + 48 * player_count |
0x06 |
reward_info |
rank_reward_info |
榜单尾部奖励状态 |
单个玩家条目的结构如下:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
rank |
uint32 |
当前名次 |
0x04 |
0x04 |
rank_time |
uint32 |
上榜或结算时间戳,当前 Lua 测试值写 2505112359 |
0x08 |
0x04 |
rank_value |
uint32 |
排行值或展示分数,当前 Lua 测试值写 100 + j |
0x0C |
0x01 |
reserved_player_flag |
int8 |
玩家行 UI 控制位,当前 Lua 测试值写 1 |
0x0D |
0x04 |
player_guid |
uint32 |
玩家 GUID |
0x11 |
0x01 |
school_id |
int8 |
门派/职业 ID |
0x12 |
0x1E |
player_name |
char[30] |
玩家名,客户端按定长 30 字节读取 |
榜单尾部奖励状态结构如下:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x01 |
reward_state |
int8 |
Lua 注释对应 nreward |
0x01 |
0x01 |
reward_insert_flag |
int8 |
Lua 注释对应 nInsert |
0x02 |
0x04 |
reward_index |
int32 |
Lua 注释对应 nIndex |
当前实际行为
- 客户端侧
Game.exe 已明确把这条包实现为“变长榜单列表包”,不是固定长度包。
- 当前服务端请求入口是
request:CGRankingLists(),它只把玩家对象转发给场景服,不读取 CGWNormalRankingList(9031) 的请求体内容。
- 当前场景服
scenecore:char_ranking_lists() 只是 new() 一个 WGCRetNormalRankList 对象后直接发送,没有查询 .ranking 服务,也没有拼接真实排行榜。
- 当前实际返回内容全部写死在
/home/ubuntu/Game2/services/game/packet.lua 的 packet.WGCRetNormalRankList.bos() 中:固定返回 4 个榜单,每个榜单固定 10 个玩家,rank_type 直接写成 1..4。
- 这段 Lua
bos() 明显带有调试痕迹,注释里多次写了“需要调试看 UI 表现”“疑似预留数据”。
- 更关键的是,当前 Lua 拼包并未完全对齐
Game.exe 的正式读取格式:
- 玩家名当前按
string.len(name) 写出,而客户端固定按 30 字节读取。
- 榜单尾部奖励状态当前只写了
3 个 char,而客户端固定按 1 + 1 + 4 共 6 字节读取。
- 因此,当前服务端实现更像普通排行榜 UI 的临时测试桩,不是已经完全打通的正式排行榜协议。
服务端落点
/home/ubuntu/Game2/services/msgagent.lua
request:CGRankingLists() 收到排行榜请求后,直接调用场景服的 char_ranking_lists。
/home/ubuntu/Game2/services/scene/scenecore.lua
scenecore:char_ranking_lists(selfId) 仅创建 WGCRetNormalRankList 并发送给客户端。
/home/ubuntu/Game2/services/game/packet.lua
packet.WGCRetNormalRankList.bos() 负责真正序列化整包,当前内容全部为硬编码测试值。
/home/ubuntu/Game2/lualib/iostream.lua
bostream:write(data, len) 会严格写入固定长度字节,因此当前玩家名长度和奖励尾结构的写法是否匹配客户端格式,完全取决于 bos() 传入的长度。
关键代码
当前服务端发送链路很短:
function request:CGRankingLists()
skynet.call(my_scene, "lua", "char_ranking_lists", my_obj_id, self)
end
function scenecore:char_ranking_lists(selfId)
local ret = packet_def.WGCRetNormalRankList.new()
self:send2client(selfId, ret)
end
当前 Lua 里真正下发的排行榜内容全部来自 bos() 的硬编码测试数据:
local nCurCount = 4
stream:writeuint(nCurCount)
stream:writeuint(11)
stream:writeshort(22)
stream:writeuint(33)
stream:writechar(1)
stream:writeint(1)
stream:writeuint(44)
for i = 1, nCurCount do
stream:writeuint(i)
stream:writechar(9)
stream:writechar(8)
stream:writeint(10)
...
end
Game.exe 中 WGCRetNormalRankList 的读取逻辑可还原为:
read_u32(rank_list_count);
read_u32(reserved_u32_1);
read_u16(reserved_u16_1);
read_u32(reserved_u32_2);
read_i8(ui_message_type);
read_u32(cur_rank_type);
read_u32(reserved_u32_3);
for (i = 0; i < rank_list_count; ++i) {
read_u32(rank_type);
read_i8(reserved_flag_1);
read_i8(reserved_flag_2);
read_i32(player_count);
for (j = 0; j < player_count; ++j) {
read_u32(rank);
read_u32(rank_time);
read_u32(rank_value);
read_i8(reserved_player_flag);
read_u32(player_guid);
read_i8(school_id);
read_fixed_string(player_name, 30);
}
read_i8(reward_state);
read_i8(reward_insert_flag);
read_i32(reward_index);
}
GetPacketID() = 9032;
GetPacketSize() = 23 + Σ(16 + 48 * player_count[i]);
客户端对子结构的长度要求也很明确:
rank_entry_size = 10 + 48 * player_count;
reward_info_size = 6;
player_name_length = 30;
结论
WGCRetNormalRankList(9032) 在当前版本里是“普通排行榜界面的返回包”,正式协议形态已经能从 Game.exe 明确还原为变长结构。
但当前服务端实现并不是正式排行榜数据链路,而是一个明显的测试桩:
- 请求侧没有消费
9031 的真实请求参数。
- 场景服没有查询
.ranking,只发送一个空对象。
- 所有榜单内容都在
packet.lua 里硬编码。
- Lua 现有
bos() 还没有完全按客户端正式格式写满玩家名和奖励尾字段。
如果后续要把这条协议改成可正式使用的排行榜回包,优先应以 Game.exe 当前的变长读包格式为准,而不是继续沿用现在这份临时测试写法。
|