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

洱海月 v0-07-0300 封包解析:ID 793 GCTeamResult(队伍结果广播)

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

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

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

×

GCTeamResult 是当前旧版项目队伍主链里的结果广播包。它由世界服构造,先交给场景服更新本地 team_info,再按场景服返回值决定是否继续转发给客户端。因此它既是服务端跨服通知对象,也是实际会发到客户端的队伍事件同步包。

当前 Game.exe 可以明确确认这条包的基础头是“固定 24 字节 + 名字变长”,并且仅在 return_type == 0 或 8 时额外附带一段 44 字节 TeamMemberInfo 尾段。结合当前 Lua 主链,现役发送类型主要是入队、离队、踢人、任命新队长、解散队伍、开始切场景和进入新场景。

包信息

项目 内容
封包 ID 793
包名 GCTeamResult
方向 GC 服务端 -> 客户端
包长模式 动态长度
最小包长 24
最大包长 98
当前 Lua 状态 现役协议;世界服构造后,经 msgagent -> scenecore -> human:update_team_info() 处理,再视返回值转发客户端
主要作用 同步队伍事件结果,驱动客户端队伍状态刷新与成员场景状态变更

当前 Game.exe 的虚函数结论为:

GetPacketID()   = 793;
GetPacketSize() = name_size + 24 + ((return_type == 0 || return_type == 8) ? 44 : 0);
Execute()       = 2;

包体结构

基础头结构如下:

偏移 长度 字段 类型 含义
0x00 0x01 return_type uint8 队伍结果类型
0x01 0x04 guid int32 事件主体 GUID;不同 return_type 下表示入队者、离队者、切场景者等当前事件主体
0x05 0x02 team_id uint16 队伍 ID
0x07 0x04 guid_ex int32 附加 ID;当前按结果类型复用,入队同步时还可能在场景服转发前被改写为成员 obj_id
0x0B 0x02 sceneid int16 相关场景 ID
0x0D 0x02 client_res int16 场景客户端资源 ID
0x0F 0x01 name_size uint8 名字长度,当前客户端允许 0..30
0x10 char[n] name char[n] 队员名字
0x10 + n 0x04 portrait_id uint32 头像 ID
0x14 + n 0x02 model uint16 当前 Lua 实际写入 uinfo.model;旧实现里的 m_uDataID 可作为参考命名
0x16 + n 0x02 reserved_1 int16 当前 Game.exe 会继续传给界面层;Lua 默认写 -1,未见现役发送点赋予明确语义

当 return_type == 0 或 8 时,还会在基础头后继续附带一段固定 44 字节的 TeamMemberInfo:

偏移 长度 字段 类型 含义
0x18 + n + 0x00 0x04 member_info.model uint32 队员模型 ID
0x18 + n + 0x04 0x04 member_info.menpai uint32 门派 ID
0x18 + n + 0x08 0x04 member_info.level uint32 等级
0x18 + n + 0x0C 0x04 member_info.reserved_1 uint32 当前 Lua 默认 0
0x18 + n + 0x10 0x04 member_info.reserved_2 uint32 当前 Lua 默认 259
0x18 + n + 0x14 0x04 member_info.reserved_3 uint32 当前 Lua 默认 0
0x18 + n + 0x18 0x04 member_info.fixed_zero int32 当前 Lua 固定写 0
0x18 + n + 0x1C 0x04 member_info.reserved_4 uint32 当前 Lua 默认 8
0x18 + n + 0x20 0x04 member_info.fixed_neg1_a int32 当前 Lua 固定写 -1
0x18 + n + 0x24 0x04 member_info.fixed_neg1_b int32 当前 Lua 固定写 -1
0x18 + n + 0x28 0x04 member_info.fixed_neg1_c int32 当前 Lua 固定写 -1

长度公式可写成:

普通结果包: size = 24 + name_size
入队/进场景: size = 68 + name_size

老 Server 可作为参考:旧实现里基础字段名依次是 m_Return / m_GUID / m_TeamID / m_GUIDEx / m_SceneID / m_SceneResID / m_Name / m_nPortrait / m_uDataID。但旧实现没有当前 Game.exe 里的额外 reserved_1 和 44 字节 TeamMemberInfo 尾段,不能直接视为当前版本最终结构。

当前实际行为

当前现役发送类型与用途如下:

return_type 常量 当前用途 是否附带 TeamMemberInfo
0 TEAM_RESULT_MEMBERENTERTEAM 新成员入队通知 是
1 TEAM_RESULT_MEMBERLEAVETEAM 普通成员退队 否
2 TEAM_RESULT_LEADERLEAVETEAM 队长离队并转让队长 否
3 TEAM_RESULT_TEAMDISMISS 队伍解散 否
4 TEAM_RESULT_TEAMKICK 踢出队员 否
5 TEAM_RESULT_TEAMAPPOINT 任命新队长 否
7 TEAM_RESULT_STARTCHANGESCENE 队员开始切换场景 否
8 TEAM_RESULT_ENTERSCENE 队员进入新场景 是

当前可见代码里没有找到这些类型的世界服现役发送点:

return_type 常量 当前状态
6 TEAM_RESULT_TEAMREFRESH 仅保留在枚举和场景更新逻辑中,未见当前世界服发送
9 TEAM_RESULT_REFRESHSERVERINFO 仅保留在枚举中,未见当前发送链
10 TEAM_RESULT_MEMBEROFFLINE 场景更新逻辑保留分支,但未见当前世界服发送

这条包的当前服务端行为还有几个关键点:

  • 世界服先构造 GCTeamResult,再交给 msgagent。
  • msgagent 不会直接发送,而是先调用场景服 on_notify_team_result()。
  • 场景服里的 human:update_team_info() 会先更新本地队伍缓存,再决定是否返回 true 允许继续转发客户端。
  • leader_guid 和 uinfo 是服务端内部辅助字段,不在 packet.GCTeamResult.bos() 的网络序列化里。
  • TEAM_RESULT_MEMBERENTERTEAM(0) 分支中,场景服会在转发前把 guid_ex 改写为队员当前 obj_id 或 INVAILD_ID。
  • 对事件主体本人,TEAM_RESULT_MEMBERENTERTEAM(0)、TEAM_RESULT_STARTCHANGESCENE(7)、TEAM_RESULT_ENTERSCENE(8) 当前可能只更新服务端 team_info 而不转发客户端,因为 human:update_team_info() 会返回 false。

服务端落点

  • 协议定义与序列化:/home/ubuntu/Game2/services/game/packet.lua
  • 世界服构造与广播:/home/ubuntu/Game2/services/world/worldcore.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/team.lua

关键代码

当前 Lua 的网络序列化

stream:writeuchar(self.return_type)
stream:writeint(self.guid)
stream:writeushort(self.team_id)
stream:writeint(self.guid_ex)
stream:writeshort(self.sceneid)
stream:writeshort(self.client_res)
stream:writeuchar(self.name_size)
if self.name_size > 0 and self.name_size <= 0x1E then
    stream:write(self.name, self.name_size)
end
stream:writeuint(self.portrait_id)
stream:writeushort(self.model or self.sex or 0)
stream:writeshort(self.unknow_7)
if self.return_type == 8 or self.return_type == 0 then
    packet.TeamMemberInfo.bos(self.uinfo, stream)
end

当前 msgagent 的转发门槛

elseif xy_id == packet_def.GCTeamResult.xy_id then
    ret = on_notify_team_result(packet)
end
if ret then
    Net:send2client(xy_id, packet, my_obj_id)
end

场景服在入队同步时会改写 guid_ex

if msg.return_type == define.TEAM_RESULT.TEAM_RESULT_MEMBERENTERTEAM
    or msg.return_type == define.TEAM_RESULT.TEAM_RESULT_TEAMREFRESH then
    local mb = {}
    mb.guid = msg.guid
    mb.sceneid = msg.sceneid
    if mb.guid == self:get_guid() then
        mb.m_objID = self:get_obj_id()
    elseif mb.sceneid == self.scene:get_id() then
        local obj = self.scene:get_obj_by_id(msg.guid_ex)
        if obj then
            mb.m_objID = obj:get_obj_id()
        end
        bNotifyTeamInfoFlag = true
    else
        mb.m_objID = define.INVAILD_ID
        bNotifyTeamInfoFlag = true
    end
    msg.guid_ex = mb.m_objID
end

当前 Game.exe 的长度判定

UINT GetPacketSize() {
    return name_size + 24 + ((return_type == 0 || return_type == 8) ? 44 : 0);
}

结论

  1. GCTeamResult(793) 是当前旧版项目里仍在实际使用的队伍结果广播包,不是单纯的客户端遗留协议。
  2. 当前 Game.exe 明确确认它的网络结构是“基础头 24 + name_size”,并且只有 return_type == 0 或 8 时才追加 44 字节 TeamMemberInfo 尾段。
  3. 这条包先在场景服更新本地 team_info,再按返回值决定是否转发客户端,因此同一个 GCTeamResult 既承担服务端内部同步,也承担实际客户端通知。
  4. guid_ex 不是固定语义字段,而是按结果类型复用;其中入队同步时,场景服还会在转发前把它改写为成员 obj_id。
  5. 老 Server 只适合提供命名参考;当前版本最终应以当前 Lua 主链和当前 Game.exe 的 Read / Write / GetPacketSize / Execute 结论为准。
您需要登录后才可以回帖 登录 | registerss

本版积分规则

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

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

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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