找回密码
 registerss
搜索
查看: 20|回复: 0

洱海月 v0-07-0300 封包解析:ID 469 CGAskLeanAbility(生活技能升级请求)

[复制链接]
  • 打卡等级:本地老炮
  • 打卡总天数:603
  • 打卡月天数:1
  • 打卡总奖励:600
  • 最近打卡:2026-10-03 06:32:28
Waylee 发表于 2026-4-14 12:27 | 显示全部楼层 |阅读模式 | Google Chrome | Windows 10

马上注册,查看网站隐藏内容!!

您需要 登录 才可以下载或查看,没有账号?registerss

×

CGAskLeanAbility 是当前版本里用于提交生活技能升级请求的客户端封包。虽然名字里仍保留了 LeanAbility 这种旧写法,但现役 /home/ubuntu/Game2 Lua 主链的实际行为不是“首次学习生活技能”,而是“对已学会的生活技能申请升到下一等级”。

包信息

项目 内容
封包 ID 469
包名 CGAskLeanAbility
方向 CG 客户端 -> 游戏服
固定包长 18
常见后续封包 GCAbilityLevel / GCAbilityTeacherInfo
后续封包 ID 760 / 731
当前 Lua 状态 现役协议;由场景服 ask_lean_ability 处理
作用 提交某个生活技能的升级申请,并附带客户端界面上缓存的消耗参数

当前 Game.exe 可直接确认:

GetPacketID()   = 469;
GetPacketSize() = 18;
Read()          = read_u16 + read_4x_u32;
Write()         = write_u16 + write_4x_u32;
Execute()       = 2;

包体结构

当前 Game.exe 与现役 Lua packet.lua 一致确认,这条包是固定 18 字节结构:

偏移 长度 字段 类型 含义
0x00 0x02 m_nAbilityID uint16 目标生活技能 ID
0x02 0x04 m_nNeedMoney uint32 客户端界面缓存的升级所需金钱
0x06 0x04 m_nNeedExp uint32 客户端界面缓存的升级所需角色经验
0x0A 0x04 m_nNeedSkillExp uint32 客户端界面缓存的升级所需技能熟练度
0x0E 0x04 m_iNpcID uint32 当前交互导师 NPC 的场景对象 ID

总长度固定为:

18

当前实际行为

  • 客户端发包后,msgagent 会把角色对象 ID、NPC 对象 ID、技能 ID 和三项消耗参数一起转交给场景服的 ask_lean_ability。
  • 场景服随后调用人物对象的 human:ask_lean_ability() 执行真正的升级逻辑。
  • 当前现役 Lua 首先要求该生活技能已经存在;如果 self:get_ability(ability_id) 取不到,就直接提示“你还没有学会该生活技能”并返回。
  • 如果 m_iNpcID 非 0,当前 Lua 还会校验该 NPC 对象当前仍在场景里存在;不存在则提示“目标不存在”。
  • 现役 Lua 会按配置表重新计算下一等级真正需要的:
    • 金钱
    • 经验
    • 技能熟练度
    • 角色等级限制
  • 因此 m_nNeedMoney / m_nNeedExp / m_nNeedSkillExp 当前更像“客户端界面缓存的回显值”,不会直接作为服务端最终判定依据。
  • 真正的校验顺序是:
    • 角色等级是否达到要求
    • 金钱是否足够
    • 角色经验是否足够
    • 技能熟练度是否足够
  • 通过校验后,服务端会扣钱、扣经验、提升技能等级、刷新角色属性,并补发两类后续封包:
    • GCAbilityLevel(760):同步该生活技能的新等级
    • GCAbilityTeacherInfo(731):刷新导师界面下一次升级所需数据
  • 当前 Lua 仍会把客户端传来的三项消耗值带进 cost_money_with_priority(..., "CGAskLeanAbility", {...}) 的上下文里,但实际扣费金额仍使用服务端重新计算的 need_money。
  • 当前 Game.exe 的 Execute() 只是空壳返回 2,没有额外本地业务逻辑。

这也说明一件容易误判的事:CGAskLeanAbility 并不是“单包即可决定升级结果”的封包,当前版本里的真正结果表现是由提示文本、GCAbilityLevel 与 GCAbilityTeacherInfo 组合完成的。

服务端落点

  • 协议定义:
    • /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

关键代码

当前 Lua 协议定义

packet.CGAskLeanAbility = {
    xy_id = packet.XYID_CG_ASK_LEAN_ABILITY,
    ctor = function(self)
        self.m_nAbilityID = 0
        self.m_nNeedMoney = 0
        self.m_nNeedExp = 0
        self.m_nNeedSkillExp = 0
        self.m_iNpcID = 0
    end,
    bis = function(self, buffer)
        local stream = bistream.new()
        stream:attach(buffer)
        self.m_nAbilityID = stream:readushort()
        self.m_nNeedMoney = stream:readuint()
        self.m_nNeedExp = stream:readuint()
        self.m_nNeedSkillExp = stream:readuint()
        self.m_iNpcID = stream:readuint()
    end
}

当前 Lua 请求入口

function request:CGAskLeanAbility()
    skynet.call(
        my_scene,
        "lua",
        "ask_lean_ability",
        my_obj_id,
        self.m_iNpcID,
        self.m_nAbilityID,
        self.m_nNeedMoney,
        self.m_nNeedExp,
        self.m_nNeedSkillExp
    )
end

function scenecore:ask_lean_ability(whos, npc_id, ability_id, need_money, need_exp, need_skill_exp)
    local human = self:get_obj_by_id(whos)
    if not human then
        return
    end
    human:ask_lean_ability(npc_id, ability_id, need_money, need_exp, need_skill_exp)
end

当前 Lua 的现役升级逻辑

function human:ask_lean_ability(npc_id, ability_id, client_need_money, client_need_exp, client_need_skill_exp)
    local ability = self:get_ability(ability_id)
    if not ability then
        self:notify_tips("你还没有学会该生活技能。")
        return false
    end
    if npc_id and npc_id ~= 0 and not self:get_scene():get_obj_by_id(npc_id) then
        self:notify_tips("目标不存在。")
        return false
    end

    local next_level = ability.level + 1
    local level_config = self:get_ability_level_up_config(ability_id, next_level)
    if not level_config then
        self:notify_tips("当前技能已无法继续升级。")
        return false
    end

    local need_money = tonumber(level_config["需求金钱"]) or 0
    local need_exp = tonumber(level_config["需求经验"]) or 0
    local need_skill_exp = tonumber(level_config["技能熟练度限制"]) or 0
    local need_level = tonumber(level_config["等级限制"]) or 0

    if self:get_attrib("level") < need_level then
        self:notify_tips("你的等级不够。")
        return false
    end
    if not self:check_money_with_priority(need_money) then
        self:notify_tips("#{no_money}")
        return false
    end
    if self:get_exp() < need_exp then
        self:notify_tips("经验不足。")
        return false
    end
    if self:get_ability_exp(ability_id) < need_skill_exp then
        self:notify_tips("技能熟练度不足。")
        return false
    end

    local ok = self:cost_money_with_priority(need_money, "CGAskLeanAbility", {
        ability_id = ability_id,
        npc_id = npc_id,
        client_need_money = client_need_money,
        client_need_exp = client_need_exp,
        client_need_skill_exp = client_need_skill_exp,
        level = next_level,
    })
    if not ok then
        self:notify_tips("#{no_money}")
        return false
    end

    self:set_exp(self:get_exp() - need_exp)
    self:study_ability(ability_id, ability.exp or 0, next_level)
    self:send_refresh_attrib()
    self:send_ability_teacher_info(npc_id, ability_id)
    return true
end

当前 Game.exe 的读写与执行结论

GetPacketID() = 469;
GetPacketSize() = 18;

Read(stream) {
    read_u16(m_nAbilityID);
    read_u32(m_nNeedMoney);
    read_u32(m_nNeedExp);
    read_u32(m_nNeedSkillExp);
    read_u32(m_iNpcID);
}

Write(stream) {
    write_u16(m_nAbilityID);
    write_u32(m_nNeedMoney);
    write_u32(m_nNeedExp);
    write_u32(m_nNeedSkillExp);
    write_u32(m_iNpcID);
}

Execute() {
    return 2;
}

结论

  1. CGAskLeanAbility(469) 是固定 18 字节的生活技能升级请求包,字段结构为 uint16 + 4 * uint32。
  2. 当前现役 Lua 并不把它当成“首次学习生活技能”入口,而是要求目标技能已经存在,随后执行“升到下一等级”的校验与扣费。
  3. 包里的 m_nNeedMoney / m_nNeedExp / m_nNeedSkillExp 当前不会直接决定服务端结果,服务端会重新按配置表计算真实需求。
  4. m_iNpcID 当前主要用于校验导师 NPC 是否存在,以及升级成功后刷新 GCAbilityTeacherInfo(731)。
  5. 升级成功后的关键后续封包是 GCAbilityLevel(760) 与 GCAbilityTeacherInfo(731);失败时则主要通过提示文本反馈,而不是专门的失败回包。
您需要登录后才可以回帖 登录 | registerss

本版积分规则

QQ|手机版|雪舞知识库 ( 浙ICP备15015590号-1 | 萌ICP备20232229号|浙公网安备33048102000118号 )|天天打卡

GMT+8, 2026-10-4 11:26 , Processed in 0.083706 second(s), 26 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表