LCRetCreateChar 是登录服返回给客户端的一条创建角色结果包,对应请求包 CLAskCreateChar(522)。当前 Game.exe 与现役 /home/ubuntu/Game2/services/login Lua 都能确认:它是一条固定 4 字节的下行包,包体只有一个 int32 result。现役登录服在收到 CLAskCreateChar 后,会把 .Playermanager 的创建结果原样写入该字段;若结果为成功,还会紧接着补发最新 LCRetCharList。
包信息
| 项目 |
内容 |
| 封包 ID |
440 |
| 包名 |
LCRetCreateChar |
| 方向 |
LC 登录服 -> 客户端 |
| 固定包长 |
4 |
| 对应请求 |
CLAskCreateChar(522) |
| 当前 Lua 状态 |
现役协议;登录服已接入 |
| 作用 |
返回本次创建角色请求的结果码,并在成功时配合后续角色列表刷新完成创建流程 |
当前 Game.exe 可直接确认:
LCRetCreateChar
GetPacketID() = 0x1405EE5C0 -> 440;
GetPacketSize() = 0x1403A0C40 -> 4;
Read = 0x1405C6D30;
Write = 0x140606B30;
Execute thunk = 0x1405FB240 -> 0x140364B90;
包体结构
当前 Game.exe 与现役 Lua 一致,这条回包固定只有一个 int32 字段:
| 偏移 |
长度 |
字段 |
类型 |
含义 |
0x00 |
0x04 |
result |
int32 |
创建角色结果码;当前登录服把 .Playermanager:create_char(...) 的返回值原样写入该字段 |
当前现役 Lua 可确认会返回的结果子集为:
result |
当前 Lua 含义 |
0 |
创建成功 |
3 |
当前账号角色已满 |
4 |
角色重名 |
7 |
名字非法 |
其中 1 / 2 / 5 / 6 在当前 define.ASKCREATECHAR_RESULT 里仍有保留名,但现役 playermanager_core:create_char() 不会走到这些分支。
当前实际行为
- 当前登录服在收到
CLAskCreateChar 后,会先校验登录会话,再调用 .Playermanager 的 create_char(uuid, server_id, packet, uid)。
create_char() 当前会先检查角色数量是否已满,再检查名字是否含 #、*、敏感词或重名;通过后才真正插入新角色并更新账号 char_list。
- 登录服随后构造
LCRetCreateChar,把返回结果写入 ret.result 后立即发送给客户端。
- 若
result == 0,登录服会紧接着调用 server.send_char_list(fd, char_list, idx),因此客户端的成功分支不是孤立结束,而是紧跟一次角色列表刷新。
- 当前客户端对
result == 0 的处理是关闭/切换创建角色相关界面状态,并继续进入成功后的角色列表刷新流程。
- 当前客户端仍保留了大量旧错误码分支:
1 / 2 / 3 / 4 / 5 / 6 / 8 / 9 / 10 / 11 / 12。从字符串资源可直接确认的旧提示名包括 Server_Busy_Retry、Role_Is_Full、Name_Exist、INVALID_STRING_IN_NAME、FaceModel_Invaild、HairModel_Invaild、HeadImage_Invaild 等;但这些都属于客户端保留分支,不代表现役 Lua 会实际返回。
- 但现役 Lua 实际只会回
0 / 3 / 4 / 7。其中最关键的兼容性差异是:当前 Lua 把“名字非法”写成 7,而当前客户端显式提示分支使用的是 case 8 -> INVALID_STRING_IN_NAME,没有单独的 case 7。
- 因此,现役服务端返回
result == 7 时,当前客户端不会命中“非法名字”专用提示,而会落入默认的 Unknow_Err 分支。这是当前 Lua 与当前 Game.exe 之间的一个结果码不对齐点。
服务端落点
- 登录协议定义:
/home/ubuntu/Game2/services/login/packet.lua
- 登录包处理:
/home/ubuntu/Game2/services/login/logind.lua
- 创建角色结果码定义:
/home/ubuntu/Game2/lualib/define.lua
- 创建角色核心逻辑:
/home/ubuntu/Game2/lualib/playermanager_core.lua
关键代码
以下代码块中,将 sub_1403F9B00 按读取/写入语义简化记为 read_u32 / write_u32,便于对照结构理解。
当前 Lua 协议定义
packet.LCRetCreateChar = {
xy_id = packet.XYID_LCRETCREATECHAR,
new = function()
local o = {}
setmetatable(o, {__index = packet.LCRetCreateChar})
o:ctor()
return o
end,
ctor = function(self)
self.result = 0
end,
bos = function(self)
local stream = bostream.new()
stream:writeint(self.result)
return stream:get()
end
}
当前登录服回包逻辑
elseif packet.xy_id == packet_def.XYID_CLASKCREATECHAR then
local result, char_list = skynet.call(".Playermanager", "lua", "create_char", uuid, server.server_id, packet, uid)
ret = packet_def.LCRetCreateChar.new()
ret.result = result
loginservice.send(fd, idx, ret)
if result == define.ASKCREATECHAR_RESULT.ASKCREATECHAR_SUCCESS then
server.send_char_list(fd, char_list, idx)
end
end
当前 Lua 创建角色结果来源
define.ASKCREATECHAR_RESULT = {
ASKCREATECHAR_SUCCESS = 0,
ASKCREATECHAR_SERVER_BUSY = 1,
ASKCREATECHAR_OP_TIMES = 2,
ASKCREATECHAR_FULL = 3,
ASKCREATECHAR_SAME_NAME = 4,
ASKCREATECHAR_OP_ERROR = 5,
ASKCREATECHAR_INTERNAL_ERROR = 6,
ASKCREATECHAR_INVALID_NAME = 7
}
function playermanager_core:create_char(uuid, server_id, req, uid)
...
if self:check_character_full(server_id, char_guids) then
return define.ASKCREATECHAR_RESULT.ASKCREATECHAR_FULL
end
local result = self:check_name_vaild(name, server_id)
if result ~= define.ASKCREATECHAR_RESULT.ASKCREATECHAR_SUCCESS then
return result
end
...
return define.ASKCREATECHAR_RESULT.ASKCREATECHAR_SUCCESS, char_guids
end
当前 Game.exe 读写与包长
__int64 __fastcall LCRetCreateChar_Read(__int64 packet, __int64 stream)
{
read_u32(stream, packet + 24);
return 1;
}
__int64 __fastcall LCRetCreateChar_Write(__int64 packet, __int64 stream)
{
write_u32(stream, packet + 24);
return 1;
}
__int64 LCRetCreateChar_GetPacketID()
{
return 440;
}
__int64 LCRetCreateChar_GetPacketSize()
{
return 4;
}
当前客户端结果分支要点
switch (result)
{
case 0:
// 关闭/切换创建角色相关界面,并继续进入成功后的角色列表刷新链
post_event(604);
break;
case 3:
// "Role Is Full"
show_prompt(272, "LCRetCreateCharHandler_Info_Role_Is_Full");
post_event(605);
break;
case 4:
// "Name Exist"
show_prompt(272, "Login_Create_Role_Name_Exist");
post_event(605);
break;
case 8:
// "INVALID_STRING_IN_NAME"
show_prompt(272, "INVALID_STRING_IN_NAME");
post_event(605);
break;
default:
// "Unknow_Err"
show_prompt(272, "LCRetCreateCharHandler_Info_Unknow_Err");
post_event(605);
break;
}
结论
LCRetCreateChar(440) 是 CLAskCreateChar(522) 的结果回包,固定长度为 4 字节。
- 包体只有一个
result(int32) 字段,当前登录服把 .Playermanager:create_char(...) 的返回值原样写入该字段。
- 现役登录服当前实际会回的结果子集是
0 / 3 / 4 / 7,成功时还会立即补发最新角色列表。
- 当前客户端保留了更多旧错误码分支,但并不代表现役 Lua 一定会返回这些值。
- 当前最值得注意的现役差异是:Lua 使用
7 表示非法名字,而当前客户端显式非法名字提示分支使用的是 8,因此现役 result == 7 会落入默认未知错误分支。