CGUseAbility 是客户端向服务端发起生活技能操作的请求包。
包信息
| 项目 |
内容 |
| 封包 ID |
239 |
| 包名 |
CGUseAbility |
| 直接响应 |
GCAbilityResult |
| 响应 ID |
533 |
| 附加同步 |
GCAbilityAction / GCAbilitySucc |
| 附加同步 ID |
812 / 70 |
| 作用 |
发起一次生活技能操作,并携带配方、平台对象和材料位信息 |
包体结构
这条包在当前项目里需要分两层理解:
- 按当前
Game.exe 类定义,包体长度是 15 字节。
- 按当前 Lua 服务端主链,实际只读取前
11 字节。
按当前 Game.exe 类结构可先这样记录:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x02 |
ability |
uint16 |
生活技能 ID |
0x02 |
0x04 |
prescription |
int32 |
配方 ID |
0x06 |
0x04 |
platform_obj_id |
uint32 |
辅助平台对象 ID |
0x0A |
0x01 |
mat_index |
uint8 |
材料背包位 |
0x0B |
0x04 |
client_flag |
uint32 |
当前 Game.exe 额外客户端开关,Lua 主链未读取 |
当前 Lua 主链真正参与处理的是:
ability
prescription
platform_obj_id
mat_index
总长度:
当前 Game.exe:15 字节
当前 Lua 主链有效读取:11 字节
回包结构
GCAbilityResult
这是当前最直接的响应包,用于返回本次能力操作是否允许开始,或中途失败结果。
按当前 Lua 主链结构可按下面理解:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x02 |
ability |
uint16 |
生活技能 ID |
0x02 |
0x04 |
prescription |
int32 |
配方 ID |
0x06 |
0x04 |
result |
int32 |
操作结果码 |
长度说明:
当前 Lua 主链读写:10 字节
当前 Game.exe GetPacketSize:12 字节
GCAbilityAction
这是当前能力操作开始时的广播包。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
m_objID |
uint32 |
角色对象 ID |
0x04 |
0x04 |
logic_count |
uint32 |
逻辑计数 |
0x08 |
0x02 |
ability |
uint16 |
生活技能 ID |
0x0A |
0x04 |
prescription |
int32 |
配方 ID |
0x0E |
0x04 |
target_obj_id |
int32 |
平台对象 ID |
0x12 |
0x01 |
begin_or_end |
uint8 |
开始或结束标记 |
总长度:
19 字节
GCAbilitySucc
这是当前能力成功产出后的成功通知包。
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x02 |
ability |
uint16 |
生活技能 ID |
0x02 |
0x04 |
prescription |
int32 |
配方 ID |
0x06 |
0x04 |
item_index |
uint32 |
产出物品 ID |
0x0A |
0x01 |
num |
uint8 |
产出数量 |
0x0B |
0x01 |
padding |
uint8 |
当前 Lua 固定补 0 |
总长度:
12 字节
当前实际行为
- 当前
msgagent.lua 先处理这条包。
- 如果
ability 命中 define.COMPOUND_MAT_ITEM_INDEX,服务端会先检查 mat_index 指向的背包位材料是否属于该技能允许的材料。
- 材料校验不通过时,当前实现只记录
log_char_use_ability_rec 日志并直接返回,不继续进入场景服主链。
- 正常情况下,请求会进入
scenecore:char_use_ability()。
- 场景服会先检查角色是否死亡、是否可行动、当前是否忙碌、是否已处于生活技能操作状态。
- 如果
ability 在配置表里不存在,当前函数会直接 return,不会继续发结果包。
- 校验通过后,服务端会把本次操作写入
ability_opera,包括技能 ID、配方 ID、平台对象 ID、材料背包位和操作时长。
- 随后调用 AI 的
push_command_use_ability(),并立即回一个 GCAbilityResult。
- 如果本次操作正式开始,角色会广播
GCAbilityAction,并把 begin_or_end 设为 1。
- 当前项目里只看到
begin_or_end = 1 的开始广播,未看到对应的结束广播。
- 能力完成后,由具体能力脚本决定结果;成功时通常会发
GCAbilitySucc。
- 当前
character.lua 的失败收尾分支把 msg.result 写成了 msg.resilt,这会导致该分支的 GCAbilityResult 结果字段无法按预期写入。
字段说明
ability:本次要执行的生活技能 ID。
prescription:本次要使用的配方 ID。
platform_obj_id:辅助平台对象 ID,例如工作台、采集点或相关操作对象。
mat_index:材料所在背包位。
client_flag:当前 Game.exe 额外带出的客户端开关字段;当前 Lua 主链未读取,也未参与当前项目主逻辑。
服务端落点
- 协议定义:
services/game/packet.lua
- 请求入口:
services/msgagent.lua
- 场景处理:
services/scene/scenecore.lua
- 操作开始广播:
services/scene/obj/character.lua
- 能力执行框架:
lualib/abilityenginer.lua
- 能力实现:
services/scene/ability/*.lua
- 成功结果发送:
lualib/script_base.lua
关键代码
当前 Lua 协议定义
packet.CGUseAbility = {
xy_id = packet.XYID_CG_USEABILITY,
bis = function(self, buffer)
local stream = bistream.new()
stream:attach(buffer)
self.ability = stream:readushort()
self.prescription = stream:readint()
self.platform = stream:readuint()
self.mat_index = stream:readuchar()
end
}
当前请求入口与材料校验
function request:CGUseAbility()
local compound_mat_item_indexs = define.COMPOUND_MAT_ITEM_INDEX[self.ability]
if compound_mat_item_indexs then
local baglist = skynet.call(my_scene, "lua", "call_human_function", my_obj_id, "get_item_bag_list")
local mat = baglist[self.mat_index]
assert(mat, self.mat_index)
local item_index = mat.item_index
if compound_mat_item_indexs[item_index] == nil then
log_char_use_ability(self, mat)
return
end
end
skynet.call(my_scene, "lua", "char_use_ability", my_obj_id, self)
end
当前场景服处理
human:reset_ability_opera()
local ability_opera = human:get_ability_opera()
ability_opera.ability_id = use.ability
ability_opera.pres_id = use.prescription
ability_opera.obj = use.platform
ability_opera.max_time = ability["操作时间基数"]
ability_opera.mat_bag_index = use.mat_index
local res = human:get_ai():push_command_use_ability()
local msg = packet_def.GCAbilityResult.new()
msg.ability = use.ability
msg.prescription = use.prescription
msg.result = res or define.OPERATE_RESULT.OR_ERROR
self:send2client(human, msg)
当前开始广播
local msg = packet_def.GCAbilityAction.new()
msg.m_objID = self:get_obj_id()
msg.logic_count = self:get_logic_count()
msg.ability = ability_opera.ability_id
msg.prescription = ability_opera.pres_id
msg.target_obj_id = ability_opera.obj
msg.begin_or_end = 1
self:get_scene():broadcast(self, msg, true)
当前成功结果
local msg = packet_def.GCAbilitySucc.new()
msg.ability = ability_id
msg.prescription = prescription["index"]
msg.item_index = result_id
msg.num = result_num
human:get_scene():send2client(human, msg)
结论
CGUseAbility 本质上就是旧版客户端的生活技能操作请求包。
当前项目里真正要记住的只有六点:
- 这条包的 ID 是
239。
- 当前
Game.exe 类大小是 15 字节,但 Lua 主链实际只读取前 11 字节。
- 当前真正参与业务的核心字段是
ability、prescription、platform_obj_id 和 mat_index。
- 直接响应包是
GCAbilityResult,当前 Lua 主链有效结构是 10 字节;附加同步主要是 GCAbilityAction 和 GCAbilitySucc。
- 合成类能力会先校验
mat_index 指向的材料是否合法,不合法时当前实现只记日志,不继续处理。
- 当前项目里
GCAbilityAction 只看到开始广播,没有看到结束广播;能力收尾失败分支还存在 msg.resilt 拼写错误。