CGAskChangeScene 是当前项目里用于确认执行切场景的请求包。
它不是一条独立决定目标坐标的封包,正常流程必须先收到 GCNotifyChangeScene,再由客户端发回这条包,服务端才继续完成切场景交接。
包信息
| 项目 |
内容 |
| 封包 ID |
107 |
| 包名 |
CGAskChangeScene |
| 前置通知 |
GCNotifyChangeScene |
| 前置通知 ID |
287 |
| 结果回包 |
GCRetChangeScene |
| 结果回包 ID |
515 |
| 作用 |
客户端确认开始切场景,推动旧场景离场、角色存档切换与新场景连接交接 |
包体结构
按当前 Temp_Release_v0-07-0300 的 Game.exe 与 Lua 服务端,CGAskChangeScene 固定长度为 12 字节。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x02 |
scene_from |
uint16 |
当前所在场景 ID |
0x02 |
0x02 |
scene_to |
uint16 |
目标场景 ID |
0x04 |
0x04 |
client_memory_mb |
uint32 |
客户端当前进程内存占用,单位 MB |
0x08 |
0x04 |
client_state_float_raw |
uint32 |
客户端附带上报的 4 字节浮点状态值原始位模式;当前 Lua 不使用 |
总长度:
12 字节
补充说明:
- 当前
Game.exe 发包时,第 4 个字段实际来源是一个 float32 返回值。
- 当前 Lua 服务端用
readuint() 读入该字段,而且整个主链完全不参与判定。
- 因为它的具体业务语义在当前版里还不能 100% 精确命名,所以这里按“客户端状态浮点值原始位模式”处理最稳。
当前实际行为
- 当前切场景主链先由场景服下发
GCNotifyChangeScene。
msgagent.lua 收到 GCNotifyChangeScene 后,会先把目标场景号和目标坐标保存到 change_scene_args:
sceneid = notify.scene_to
world_pos = notify.world_pos
- 客户端随后发送
CGAskChangeScene,当前 Lua 请求入口 request:CGAskChangeScene() 本身并不使用包内的 scene_from、scene_to、client_memory_mb、client_state_float_raw 做业务判断。
- 当前 Lua 真正采用的切场景目标数据,来自前一步缓存下来的
change_scene_args,不是来自这条包自身。
- 进入
request:CGAskChangeScene() 后,服务端会先让角色从旧场景 leave,取回角色存档数据。
- 然后把存档中的
sceneid 与 world_pos 改成 change_scene_args 里的目标值,再保存为 "change_scene" 或 "leave_scene" 相关交接数据。
- 最后向
.gamed 取回新场景连接信息,并回发 GCRetChangeScene:
- 这说明当前项目里,
CGAskChangeScene 的实际作用更接近“切场景确认触发包”。
- 如果前置
GCNotifyChangeScene 没有先到,当前 Lua 这条链就不成立;这也是为什么它不能脱离前置通知单独使用。
服务端落点
- 协议定义:
/home/ubuntu/Game2/services/game/packet.lua
- 切场景前置通知缓存:
/home/ubuntu/Game2/services/msgagent.lua
- 切场景请求入口:
/home/ubuntu/Game2/services/msgagent.lua
- 场景服下发切场景通知:
/home/ubuntu/Game2/services/scene/scenecore.lua
- 角色切场景存档写入:
/home/ubuntu/Game2/services/scene/obj/human.lua
关键代码
packet.CGAskChangeScene = {
xy_id = packet.XYID_CG_ASK_CHANGE_SCENE,
bis = function(self, buffer)
local stream = bistream.new()
stream:attach(buffer)
self.scene_from = stream:readushort()
self.scene_to = stream:readushort()
self.client_memory_mb = stream:readuint()
self.client_state_float_raw = stream:readuint()
end
}
local function on_notify_change_scene(notify)
change_scene_args = {}
change_scene_args.sceneid = notify.scene_to
change_scene_args.world_pos = notify.world_pos
local my_guid = ma_func:get_guid()
skynet.call(".world", "lua", "char_start_change_scene", my_guid)
end
function request:CGAskChangeScene()
...
data.attrib.sceneid = change_scene_args.sceneid
data.attrib.world_pos = change_scene_args.world_pos
ma_func:save_my_data(data, call_name)
...
ret.result = 1
ret.ip = ma_func:get_game_ip() or server_conf.server_ip
ret.port = port
ret.key = my_connect_key
Net:send(ret)
end
function scenecore:notify_change_scene(obj_id, sceneid, x, y, client_res)
local ret = packet_def.GCNotifyChangeScene.new()
ret.scene_from = self.id
ret.scene_to = sceneid
ret.world_pos.x = x
ret.world_pos.y = y
ret.client_res = client_res or sceneid
self:send2client(obj_id, ret)
end
// 当前 Game.exe 的发送点
v24 = GetCurrentSceneID();
v25 = dest_scene_id;
v26 = sub_14007AF80();
v27 = (*(float (__fastcall **)(__int64))(*(_QWORD *)qword_140911068 + 1064LL))(qword_140911068);
__int64 sub_14007AF80()
{
...
return LODWORD(ppsmemCounters.PagefileUsage) >> 20;
}
结论
CGAskChangeScene 在当前版里可以直接按下面理解:
- 它的包号是
107,固定长度 12 字节。
- 它不是“单独决定去哪”的包,而是收到
GCNotifyChangeScene 之后的切场景确认包。
- 当前 Lua 真正采用的目标场景号和目标坐标,来自前置
GCNotifyChangeScene 缓存的 change_scene_args。
- 包内后 8 字节属于客户端附带上报数据,其中第 1 个可明确为当前进程内存占用 MB,第 2 个当前不参与服务端逻辑。