GCCheDiFuLuData 是当前 Temp_Release_v0-07-0300 中仍在实际使用的一条变长回包,用来把彻地符箓 / 遁甲书的传送点槽位数据同步给客户端。当前 Lua 主线支持两种发送方式:玩家主动请求 CGAskCheDiFuLuData(1090) 时做一次全量下发;脚本通过 SetCheDiFuLuData() 改写某个槽位时,再补发一次单槽刷新。
结合当前 Game.exe、Lua 协议定义和彻地符箓物品脚本可以确认,这条包真正同步的只有“槽位索引 + 场景 ID + 坐标”。客户端界面里显示的剩余使用次数 count 与初始化标记 init 由物品参数另行提供,不在本包内。
包信息
| 项目 |
内容 |
| 封包 ID |
1091 |
| 包名 |
GCCheDiFuLuData |
| 方向 |
GC 服务端 -> 客户端 |
| 关联请求 |
CGAskCheDiFuLuData(1090) |
| 包长 |
2 + 16 * size |
| 当前有效上限 |
size <= 30,最大 482 字节 |
| 当前 Lua 协议定义 |
已定义 |
| 当前 Lua 发送入口 |
human:send_chedifulu_data(index) |
| 主要作用 |
同步彻地符箓 30 个槽位中的已配置传送点数据,支持全量同步和单槽刷新 |
当前 Game.exe 的虚函数结论为:
GCCheDiFuLuData
GetPacketID() = 1091;
GetPacketSize() = 16 * size + 2;
Read = 0x1406169B0;
Write = 0x140616AA0;
Execute thunk = 0x1406169A0 -> sub_140080330;
包体结构
这条包是一个“2 字节头 + N 个 16 字节条目”的变长结构。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x01 |
reserved_1 |
uchar |
保留字节;当前 Lua 默认写 0,当前客户端 Execute() 未消费 |
0x01 |
0x01 |
size |
uchar |
条目数量 |
0x02 |
0x10 * size |
list[size] |
结构数组 |
传送点条目列表 |
单个条目的线性结构如下:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
+0x00 |
0x04 |
index |
uint32 |
0 基槽位号,当前有效范围 0..29 |
+0x04 |
0x04 |
sceneid |
uint32 |
目标场景 ID |
+0x08 |
0x04 |
x |
uint32 |
目标坐标 X |
+0x0C |
0x04 |
y |
uint32 |
目标坐标 Y |
当前 Game.exe 的读写顺序与 Lua 协议定义一致:
Read(stream) {
read_u8(reserved_1);
read_u8(size);
for (i = 0; i < size && i < 30; ++i) {
read_u32(index[i]);
read_u32(sceneid[i]);
read_u32(x[i]);
read_u32(y[i]);
}
}
Write(stream) {
write_u8(reserved_1);
write_u8(size);
for (i = 0; i < size && i < 30; ++i) {
write_u32(index[i]);
write_u32(sceneid[i]);
write_u32(x[i]);
write_u32(y[i]);
}
}
需要注意两点:
size 在线路上是 uchar,但当前客户端和服务端实现都把有效槽位数限制在 30。
GetPacketSize() 直接按 size 计算,Read/Write() 则在 30 条时强制截断,所以当前实际使用必须保证 size <= 30。现行 Lua 主链正是按 define.CHEDIFULU_DATA_SIZE = 30 控制的。
当前实际行为
当前版本里,这条包的行为可以收敛为下面几条:
- 玩家发送
CGAskCheDiFuLuData 后,msgagent.lua 会转到场景服 char_ask_chedifulu_data(),再由 human:send_chedifulu_data() 组包并下发。
human:send_chedifulu_data() 在不带 index 参数时,会遍历 define.CHEDIFULU_DATA_SIZE = 30 个槽位,把已有配置逐条写入 msg.list,形成一次全量同步。
- 脚本接口
SetCheDiFuLuData(selfId, index, sceneid, position) 最终落到 human:set_chedifulu_data();这个函数在写入角色数据后会立刻调用 send_chedifulu_data(index),因此支持单槽刷新。
- 当前服务端只同步“已保存的自定义槽位”。如果某个槽位没有记录,物品脚本
893252.lua 会在使用时用默认点位兜底,前四个默认槽位分别是 401(223,225)、402(131,128)、161(13,25)、165(25,108)。
- 当前客户端
Execute() 只把每条记录的 index / sceneid / x / y 写入本地彻地符箓缓存;首字节 reserved_1 没有被当前执行链使用。
- 客户端界面
Item_DunJiaShu.lua 最终通过 LuaFnGetCheDiFuLuPosInfo() 把槽位显示成“场景名 + 坐标”,但其中的剩余次数 count 与初始化标记 init 来自物品参数,不是 GCCheDiFuLuData 的内容。
另外,当前 Lua 协议定义里的 bis() 存在一个只在“服务端反序列化此 GC 包”时才会暴露的问题:它使用 if i < 30 then 判断,因此只会把前 29 条写入 self.list。不过现行服务端主链只发送这条包,不依赖该 bis() 路径,因此不会影响正常下发。
服务端落点
- 协议定义:
/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/define.lua
- 脚本通用接口:
/home/ubuntu/Game2/lualib/script_base.lua
- 彻地符箓物品逻辑与默认点位:
/home/ubuntu/Game2/services/scripts/obj/commonitem/893252.lua
关键代码
当前 Lua 协议定义
packet.GCCheDiFuLuData = {
ctor = function(self)
self.unknow_1 = 0
self.size = 0
self.list = {}
end,
bis = function(self, buffer)
local stream = bistream.new()
stream:attach(buffer)
self.unknow_1 = stream:readuchar()
self.size = stream:readuchar()
if self.size > 0 then
for i = 1, self.size do
if i < 30 then
local l = {}
l.index = stream:readuint()
l.sceneid = stream:readuint()
l.x = stream:readuint()
l.y = stream:readuint()
table.insert(self.list, l)
end
end
end
end,
bos = function(self)
local stream = bostream.new()
stream:writeuchar(self.unknow_1)
stream:writeuchar(self.size)
for i = 1, self.size do
local l = self.list[i] or { index = 0, sceneid = 0, x = 0, y = 0 }
stream:writeuint(l.index)
stream:writeuint(l.sceneid)
stream:writeuint(l.x)
stream:writeuint(l.y)
end
return stream:get()
end
}
当前请求与发送链
function request:CGAskCheDiFuLuData()
skynet.call(my_scene, "lua", "char_ask_chedifulu_data", my_obj_id, self)
end
function scenecore:char_ask_chedifulu_data(my_obj_id)
local obj_me = self:get_obj_by_id(my_obj_id)
obj_me:send_chedifulu_data()
end
function human:send_chedifulu_data(index)
self.chedifulu_data = self.chedifulu_data or {}
local msg = packet_def.GCCheDiFuLuData.new()
msg.list = {}
if index then
local data = self.chedifulu_data[index]
if data then
local l = {}
l.index = tonumber(index)
l.sceneid = data.sceneid
l.x = data.x
l.y = data.y
table.insert(msg.list, l)
end
else
for i = 1, define.CHEDIFULU_DATA_SIZE do
local data = self.chedifulu_data[tostring(i - 1)]
if data then
local l = {}
l.index = i - 1
l.sceneid = data.sceneid
l.x = data.x
l.y = data.y
table.insert(msg.list, l)
end
end
end
msg.size = #msg.list
self:get_scene():send2client(self, msg)
end
脚本改单槽位时会立即触发回包
function script_base:SetCheDiFuLuData(selfId, index, sceneid, position)
local human = self.scene:get_obj_by_id(selfId)
human:set_chedifulu_data(index, sceneid, position)
end
function human:set_chedifulu_data(index, sceneid, position)
self.chedifulu_data = self.chedifulu_data or {}
index = tostring(index)
self.chedifulu_data[index] = {
sceneid = sceneid,
x = math.ceil(position.x),
y = math.ceil(position.y),
}
self:send_chedifulu_data(index)
end
彻地符箓默认点位
local default_transports = {
["0"] = { sceneid = 401, x = 223, y = 225},
["1"] = { sceneid = 402, x = 131, y = 128},
["2"] = { sceneid = 161, x = 13, y = 25},
["3"] = { sceneid = 165, x = 25, y = 108},
}
当前 Game.exe 的执行结论
GetPacketSize() = 16 * size + 2;
Execute() {
for (i = 0; i < size && i < 30; ++i) {
if (index[i] <= 29) {
client_update_chedifulu_slot(index[i], sceneid[i], x[i], y[i]);
}
}
return 2;
}
结论
GCCheDiFuLuData(1091) 在当前 Temp_Release_v0-07-0300 里的定位已经比较明确:
- 它是当前版本实际启用的“彻地符箓传送点数据同步包”。
- 包体为
reserved_1 + size + list[size],每条记录固定 16 字节,内容是 index / sceneid / x / y。
- 当前有效槽位上限是
30,索引使用 0 基编号。
- 服务端既支持玩家主动请求后的全量同步,也支持脚本改写单槽位后的单条刷新。
- 当前客户端执行链只消费条目列表本身,不使用首字节
reserved_1。
- 彻地符箓的剩余次数、初始化状态与默认点位策略并不由本包完整承载,而是分别来自物品参数和物品脚本兜底逻辑。
因此,把 GCCheDiFuLuData 归类为“彻地符箓 / 遁甲书传送点槽位同步包”是符合当前版本实际行为的。