CGCharAllTitles 是旧版客户端里用于请求角色全部称号列表的查询包。
当前版本中,这条包本身只携带一个对象 ID,但当前 Lua 场景服处理并不会使用这个字段,而是固定把“请求者自己”的全部称号列表整理成 GCCharAllTitles 回给客户端。
包信息
| 项目 |
内容 |
| 封包 ID |
487 |
| 包名 |
CGCharAllTitles |
| 方向 |
CG 客户端 -> 场景服 |
| 固定包长 |
4 |
| 直接回包 |
GCCharAllTitles |
| 回包 ID |
237 |
| 当前 Lua 入口 |
request:CGCharAllTitles() |
| 当前场景服处理 |
scenecore:char_all_titles() |
| 主要作用 |
请求当前角色全部称号数据并回传数值称号与字符串称号列表 |
当前 Game.exe 的虚函数结论为:
GetPacketID() = 487;
GetPacketSize() = 4;
包体结构
这条包是固定长度包,只有一个 uint32 字段。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
target_obj_id |
uint32 |
客户端上传的目标对象 ID |
当前实际行为 / 模式说明
- 当前 Lua 请求入口是
request:CGCharAllTitles(),随后调用场景服 char_all_titles()。
- 当前场景服处理函数签名是
scenecore:char_all_titles(who),只使用请求者对象 ID who,并没有读取包体里的 target_obj_id。
- 因此在当前项目现状下,这条包虽然上传了一个对象 ID,但服务端实际固定回的是“当前请求角色自己的称号列表”。
- 角色对象会调用
update_titles_to_client() 构造 GCCharAllTitles:
self.id_titles 作为数值称号列表
self.titles 作为字符串称号列表
- 当前 Lua 中这两类列表的真实结构已经能直接确认:
id_titles 元素形如 { id = xxx }
titles 元素形如 { id = xxx, str = "xxx" }
GCCharAllTitles 在当前 Lua 发送时还会补齐两个数量字段:
m_nTitleId = #self.id_titles
m_nTitleStr = #self.titles
- 当前
Game.exe 的 GCCharAllTitles 处理器还明确限制:
- 数值称号数量最大
0x86,即 134
- 字符串称号数量最大
0x10,即 16
关联回包结构
GCCharAllTitles 是变长回包,头部固定 6 字节,后面依次跟:
12 * m_nTitleId 字节的数值称号数组
sum(13 + title_len) 字节的字符串称号数组
当前 Game.exe 的 GetPacketSize() 公式可归纳为:
size = 6 + 12 * title_id_count + Σ(13 + title_len);
GCCharAllTitles 头部结构
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
obj_id |
uint32 |
被回填称号列表的对象 ID |
0x04 |
0x01 |
title_id_count |
uint8 |
数值称号数量,客户端最大接收 134 |
0x05 |
0x01 |
title_str_count |
uint8 |
字符串称号数量,客户端最大接收 16 |
数值称号单项结构
每个数值称号固定 12 字节。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
+0x00 |
0x04 |
reserved |
uint32 |
当前 Lua 发送时固定写 0 |
+0x04 |
0x04 |
title_id |
uint32 |
数值称号 ID |
+0x08 |
0x04 |
remain_time |
uint32 |
剩余时间,当前 Lua 默认写 0 |
字符串称号单项结构
每个字符串称号长度为 13 + title_len 字节。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
+0x00 |
0x04 |
title_id |
uint32 |
字符串称号类型 ID |
+0x04 |
0x01 |
title_len |
uint8 |
称号字符串长度 |
+0x05 |
0x04 |
reserved_1 |
uint32 |
当前 Lua 发送时固定写 0 |
+0x09 |
0x04 |
reserved_2 |
uint32 |
当前 Lua 发送时固定写 0 |
+0x0D |
n |
title_str |
char[n] |
GBK 字符串内容 |
服务端落点
这条包在当前项目里的关键位置如下:
- 协议定义:
/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
关键代码
当前 Lua 协议定义
packet.CGCharAllTitles = {
xy_id = packet.XYID_CG_CHAR_ALL_TITLES,
ctor = function(self) self.m_objID = 0 end,
bis = function(self, buffer)
local stream = bistream.new()
stream:attach(buffer)
self.m_objID = stream:readuint()
end
}
当前 Lua 请求入口与场景服处理
function request:CGCharAllTitles()
skynet.call(my_scene, "lua", "char_all_titles", my_obj_id, self)
end
function scenecore:char_all_titles(who)
local obj_me = self:get_obj_by_id(who)
obj_me:update_titles_to_client()
end
这里可以直接看出:当前场景服没有使用 self.m_objID,而是固定把请求者 who 的称号列表发回客户端。
当前角色称号回包构造
function human:update_titles_to_client()
local msg = packet_def.GCCharAllTitles.new()
msg.m_objID = self:get_obj_id()
msg.m_nTitleId = #self.id_titles
msg.m_TitleIdList = self.id_titles
msg.m_nTitleStr = #self.titles
msg.m_TitleStrList = self.titles
self:get_scene():send2client(self, msg)
end
当前称号列表真实数据结构
function human:add_id_title(id)
table.insert(self.id_titles, { id = id })
end
function human:set_title(id, titlestr)
local title = { id = id, str = titlestr }
table.insert(self.titles, title)
end
当前 Game.exe 关键结论
CGCharAllTitles::Read/Write 都只处理一个 uint32:
Read : read(a1 + 24, 4);
Write: write(a1 + 24, 4);
GetPacketSize() = 4;
GCCharAllTitles 则是变长回包:
GetPacketID() = 237;
GetPacketSize() = 6 + 12 * title_id_count + Σ(13 + title_len);
客户端处理器 GCCharAllTitlesHandler.cpp 还能进一步确认:
title_id_count <= 134
title_str_count <= 16
- 数值称号项按三段
uint32 读取
- 字符串称号项按
uint32 + uint8 + uint32 + uint32 + char[n] 读取
结论
CGCharAllTitles 在当前旧版项目中可以明确归类为“请求全部称号列表”的固定 4 字节查询包。
它的包体确实带有一个 target_obj_id,但当前 Lua 服务端并不使用这个字段,实际行为是:
- 固定回当前请求角色自己的称号列表
- 回包
GCCharAllTitles 同时带回数值称号与字符串称号两套数据
因此,这条包在当前项目中的最终用途应当直接写成:
“请求当前角色全部称号数据并同步到客户端称号缓存”。