GCBusMove 是当前项目里用于通知客户端“载具开始沿下一段路径移动”的广播包。它不负责逐帧同步载具当前位置,而是告诉客户端:哪辆载具、当前平面坐标是多少、下一目标点在哪里、应当按什么速度移动。
当前 Temp_Release_v0-07-0300 里,这条协议既保留在 Game.exe,也确实由 /home/ubuntu/Game2/services/scene/obj/bus.lua 发送。Lua 侧只有 bos() 没有 bis(),说明当前主线只把它当作服务端到客户端的单向移动通知包使用。
包信息
| 项目 |
内容 |
| 封包 ID |
598 |
| 包名 |
GCBusMove |
| 方向 |
GC 服务端 -> 客户端 |
| 固定包长 |
28 |
| 当前 Lua 协议定义 |
已定义,仅 bos() |
| 当前 Lua 发送点 |
/home/ubuntu/Game2/services/scene/obj/bus.lua |
| 主要作用 |
通知客户端指定载具从当前坐标开始,朝下一目标点按给定速度移动 |
| 相关包 |
GCNewBus_Move / GCBusStopMove / GCBusAddPassenger / GCBusRemoveAllPassenger |
当前 Game.exe 的虚函数结论为:
GCBusMove
GetPacketID() = 598;
GetPacketSize() = 28;
Read = 0x140610790;
Write = 0x140610810;
Execute thunk = 0x140610780 -> sub_1403325B0;
包体结构
当前 Game.exe 与 Lua 定义都能确认,这条包是固定 28 字节结构:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
m_objID |
int32 |
载具对象 ID |
0x04 |
0x04 |
world_pos_x |
float |
当前平面 X 坐标 |
0x08 |
0x04 |
world_pos_y |
float |
当前平面 Y 坐标 |
0x0C |
0x04 |
next_pos_x |
float |
下一目标点 X 坐标 |
0x10 |
0x04 |
next_pos_y |
float |
下一目标点 Y 坐标 |
0x14 |
0x04 |
next_pos_z |
float |
下一目标点 Z 坐标 |
0x18 |
0x04 |
speed |
float |
本段移动速度 |
Lua 序列化顺序与 Game.exe 的 Read() / Write() 完全一致:
Read(stream) {
read_i32(m_objID);
read_f32(world_pos_x);
read_f32(world_pos_y);
read_f32(next_pos_x);
read_f32(next_pos_y);
read_f32(next_pos_z);
read_f32(speed);
}
当前协议只携带“当前平面坐标” world_pos.x/y,并不包含当前 world_pos.z。也就是说,客户端真正依赖的是“从当前二维起点朝三维目标点移动”的通知,而不是一份完整三维当前位置快照。
当前实际行为
当前项目里,载具移动逻辑落在 /home/ubuntu/Game2/services/scene/obj/bus.lua:
bus:start_patrol() 会根据巡逻路径和往返方向生成 move_path。
- 创建出第一段目标点后,立即调用
send_move_to() 广播 GCBusMove。
- 载具在
logic_move() 中按速度推进;当跨过当前路径点且后面还有下一段时,会更新自身坐标并再次调用 send_move_to(),通知客户端切换到下一段目标点。
- 当最后一个路径点走完后,
move_path 被清空,转入 on_arrive_dest()。
这里有一个很重要的现状差异:
- 当前 Lua 主线没有在
on_arrive_dest() 里发送 GCBusStopMove。
- 当前实际做法是切换往返方向、让所有乘客下车,然后重新广播
create_new_obj_packet() 生成的 GCNewBus 或 GCNewBus_Move。
因此,GCBusMove 在当前版本里的职责更接近“开始下一段移动”或“切段移动”,而不是“完整移动生命周期通知”。
客户端 Execute() 的行为也比较直接:
- 先用
next_pos.x/y 做一次目标位置校验。
- 如果目标点非法,会抛出
"ERROR POSITION @ GCBusMoveHandler target"。
- 找到对应
m_objID 的客户端对象后,把一个内部移动命令(常量 43)和 next_pos + speed 下发给对象。
这说明客户端收到包后,不是直接把载具瞬移到目标点,而是把它交给本地对象运动系统按目标点与速度继续跑。
服务端落点
当前项目里与这条协议直接相关的落点如下:
- 协议定义:
/home/ubuntu/Game2/services/game/packet.lua
- 载具巡逻与移动广播:
/home/ubuntu/Game2/services/scene/obj/bus.lua
- 到站后的乘客清理与新对象重发:
/home/ubuntu/Game2/services/scene/obj/bus.lua
关键代码
当前 Lua 协议定义
packet.GCBusMove = {
xy_id = packet.XYID_GC_BUS_MOVE,
bos = function(self, buffer)
local stream = bostream.new()
stream:writeint(self.m_objID)
stream:writefloat(self.world_pos.x)
stream:writefloat(self.world_pos.y)
stream:writefloat(self.next_pos.x)
stream:writefloat(self.next_pos.y)
stream:writefloat(self.next_pos.z)
stream:writefloat(self.speed or 0)
return stream:get()
end
}
当前 Lua 发送链
function bus:send_move_to()
local world_pos = self:get_world_pos()
local msg = packet_def.GCBusMove.new()
msg.m_objID = self:get_obj_id()
msg.world_pos = world_pos
msg.next_pos = self.move_path[1]
msg.speed = self:get_speed()
self:get_scene():broadcast(self, msg, true)
end
function bus:start_patrol()
local patrol_id = self:get_patrol_id()
local partol = self:get_scene():get_patrol_path_by_index(patrol_id)
if partol then
self.move_path = self:get_athwart_path(partol)
self:send_move_to()
end
end
当前到站后的处理方式
function bus:on_arrive_dest()
self.stop_time_timer = self:get_stop_time()
local athwart = self:get_athwart()
if athwart == 0 then
athwart = 1
else
athwart = 0
end
self:set_athwart(athwart)
self:passengers_off()
local msg = self:create_new_obj_packet()
self:get_scene():broadcast(self, msg, true)
end
当前 Game.exe 的执行结论
GetPacketID() = 598;
GetPacketSize() = 28;
Execute() {
validate_target_position(next_pos_x, next_pos_y);
target = find_object(m_objID);
if (target) {
send_move_command_43(target, next_pos_x, next_pos_z, next_pos_y, speed);
}
return 2;
}
结论
GCBusMove 在当前 Temp_Release_v0-07-0300 项目里的结论可以直接收敛为:
- 这条包的 ID 是
598,固定长度 28。
- 包体字段很简单,就是载具对象 ID、当前平面坐标、下一目标点和速度。
- 当前 Lua 只在载具开始巡逻或切到下一段路径时广播这条包。
- 当前协议不携带当前
world_pos.z,只发 world_pos.x/y 与完整的 next_pos.x/y/z。
- 当前客户端收到后会校验目标点,并把移动目标交给本地对象移动系统处理,而不是直接瞬移。
- 载具到达巡逻终点时,当前主线并不是发送
GCBusStopMove,而是切换方向、清乘客并重新广播新的对象状态包。
因此,GCBusMove 更适合归类为“载具分段移动启动通知包”,而不是完整的停止/刷新总控包。