KBEngine warring项目源码阅读(二) 登录和baseapp的负载均衡
转自:http://www.cnblogs.com/lsm19870508/p/6910130.html
原本不打算把登录拿出来写的,但是阅读登录部分的代码的时候发现登录和注册还不太一回事,因为登录涉及到分配baseapp的ip,负载均衡的实现,所以水一下。
流程图:

和上次一样,先是找unity控件

找到ui.cs下的login
1 void login()
2 {
3 Common.DEBUG_MSG("login is Click, name=" + username.input.text + ", password=" + password.input.text + "!");
4
5 log_label.obj.text = "请求连接服务器...";
6 log_label.obj.color = UnityEngine.Color.green;
7 if(username.input.text == "" || username.input.text.Length > 30)
8 {
9 log_label.obj.text = "用户名或者邮箱地址不合法。";
10 log_label.obj.color = UnityEngine.Color.red;
11 Common.WARNING_MSG("ui::login: invalid username!");
12 return;
13 }
14
15 if(password.input.text.Length < 6 || password.input.text.Length > 16)
16 {
17 log_label.obj.text = "密码不合法, 长度应该在6-16位之间。";
18 log_label.obj.color = UnityEngine.Color.red;
19 Common.WARNING_MSG("ui::login: invalid reg_password!");
20 return;
21 }
22
23 KBEngine.Event.fireIn("login", username.input.text, password.input.text, System.Text.Encoding.UTF8.GetBytes("kbengine_unity_warring"));
24 log_label.obj.text = "连接成功,等待处理请稍后...";
25 }
根据上篇文章思路找到Kbengine.cs下的login_loginapp
/*
登录到服务端(loginapp), 登录成功后还必须登录到网关(baseapp)登录流程才算完毕
*/
public void login_loginapp(bool noconnect)
{
if(noconnect)
{
reset();
_networkInterface.connectTo(_args.ip, _args.port, onConnectTo_loginapp_callback, null);
}
else
{
Dbg.DEBUG_MSG("KBEngine::login_loginapp(): send login! username=" + username);
Bundle bundle = Bundle.createObject();
bundle.newMessage(Message.messages["Loginapp_login"]);
bundle.writeInt8((sbyte)_args.clientType);
bundle.writeBlob(KBEngineApp.app._clientdatas);
bundle.writeString(username);
bundle.writeString(password);
bundle.send(_networkInterface);
}
}
去服务器的loginapp项目找login
//-------------------------------------------------------------------------------------
void Loginapp::login(Network::Channel* pChannel, MemoryStream& s)
{
AUTO_SCOPED_PROFILE("login");
COMPONENT_CLIENT_TYPE ctype;
CLIENT_CTYPE tctype = UNKNOWN_CLIENT_COMPONENT_TYPE;
std::string loginName;
std::string password;
std::string datas;
// 前端类别
s >> tctype;
ctype = static_cast<COMPONENT_CLIENT_TYPE>(tctype);
// 附带数据
s.readBlob(datas);
// 帐号登录名
s >> loginName;
// 密码
s >> password;
loginName = KBEngine::strutil::kbe_trim(loginName);
if(loginName.size() == 0)
{
INFO_MSG("Loginapp::login: loginName is NULL.\n");
_loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
s.done();
return;
}
if(loginName.size() > ACCOUNT_NAME_MAX_LENGTH)
{
INFO_MSG(fmt::format("Loginapp::login: loginName is too long, size={}, limit={}.\n",
loginName.size(), ACCOUNT_NAME_MAX_LENGTH));
_loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
s.done();
return;
}
if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
{
INFO_MSG(fmt::format("Loginapp::login: password is too long, size={}, limit={}.\n",
password.size(), ACCOUNT_PASSWD_MAX_LENGTH));
_loginFailed(pChannel, loginName, SERVER_ERR_PASSWORD, datas, true);
s.done();
return;
}
if(datas.size() > ACCOUNT_DATA_MAX_LENGTH)
{
INFO_MSG(fmt::format("Loginapp::login: bindatas is too long, size={}, limit={}.\n",
datas.size(), ACCOUNT_DATA_MAX_LENGTH));
_loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
s.done();
return;
}
// 首先必须baseappmgr和dbmgr都已经准备完毕了。
Components::ComponentInfos* baseappmgrinfos = Components::getSingleton().getBaseappmgr();
if(baseappmgrinfos == NULL || baseappmgrinfos->pChannel == NULL || baseappmgrinfos->cid == 0)
{
datas = "";
_loginFailed(pChannel, loginName, SERVER_ERR_SRV_NO_READY, datas, true);
s.done();
return;
}
Components::ComponentInfos* dbmgrinfos = Components::getSingleton().getDbmgr();
if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == 0)
{
datas = "";
_loginFailed(pChannel, loginName, SERVER_ERR_SRV_NO_READY, datas, true);
s.done();
return;
}
if(!g_kbeSrvConfig.getDBMgr().allowEmptyDigest)
{
std::string clientDigest;
if(s.length() > 0)
s >> clientDigest;
if(clientDigest.size() > 0)
{
if(clientDigest != digest_)
{
INFO_MSG(fmt::format("Loginapp::login: loginName({}), digest not match. curr({}) != dbmgr({})\n",
loginName, clientDigest, digest_));
datas = "";
_loginFailed(pChannel, loginName, SERVER_ERR_ENTITYDEFS_NOT_MATCH, datas, true);
return;
}
}
else
{
//WARNING_MSG(fmt::format("Loginapp::login: loginName={} no check entitydefs!\n", loginName));
}
}
s.done();
if(shuttingdown_ != SHUTDOWN_STATE_STOP)
{
INFO_MSG(fmt::format("Loginapp::login: shutting down, {} login failed!\n", loginName));
datas = "";
_loginFailed(pChannel, loginName, SERVER_ERR_IN_SHUTTINGDOWN, datas, true);
return;
}
if(initProgress_ < 1.f)
{
datas = fmt::format("initProgress: {}", initProgress_);
_loginFailed(pChannel, loginName, SERVER_ERR_SRV_STARTING, datas, true);
return;
}
// 把请求交由脚本处理
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
const_cast<char*>("onReuqestLogin"),
const_cast<char*>("ssby#"),
loginName.c_str(),
password.c_str(),
tctype,
datas.c_str(), datas.length());
if(pyResult != NULL)
{
bool login_check = true;
if(PySequence_Check(pyResult) && PySequence_Size(pyResult) == 5)
{
char* sname;
char* spassword;
char *extraDatas;
Py_ssize_t extraDatas_size = 0;
SERVER_ERROR_CODE error;
if(PyArg_ParseTuple(pyResult, "H|s|s|b|y#", &error, &sname, &spassword, &tctype, &extraDatas, &extraDatas_size) == -1)
{
ERROR_MSG(fmt::format("Loginapp::login: {}.onReuqestLogin, Return value error! loginName={}\n",
g_kbeSrvConfig.getLoginApp().entryScriptFile, loginName));
login_check = false;
_loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
}
if(login_check)
{
loginName = sname;
password = spassword;
if (extraDatas && extraDatas_size > 0)
datas.assign(extraDatas, extraDatas_size);
else
SCRIPT_ERROR_CHECK();
}
if(error != SERVER_SUCCESS)
{
login_check = false;
_loginFailed(pChannel, loginName, error, datas, true);
}
if(loginName.size() == 0)
{
INFO_MSG("Loginapp::login: loginName is NULL.\n");
_loginFailed(pChannel, loginName, SERVER_ERR_NAME, datas, true);
s.done();
return;
}
}
else
{
ERROR_MSG(fmt::format("Loginapp::login: {}.onReuqestLogin, Return value error, must be errorcode or tuple! loginName={}\n",
g_kbeSrvConfig.getLoginApp().entryScriptFile, loginName));
login_check = false;
_loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
}
Py_DECREF(pyResult);
if(!login_check)
return;
}
else
{
SCRIPT_ERROR_CHECK();
_loginFailed(pChannel, loginName, SERVER_ERR_OP_FAILED, datas, true);
}
PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.find(loginName);
if(ptinfos != NULL)
{
datas = "";
_loginFailed(pChannel, loginName, SERVER_ERR_BUSY, datas, true);
return;
}
ptinfos = new PendingLoginMgr::PLInfos;
ptinfos->ctype = ctype;
ptinfos->datas = datas;
ptinfos->accountName = loginName;
ptinfos->password = password;
ptinfos->addr = pChannel->addr();
pendingLoginMgr_.add(ptinfos);
if(ctype < UNKNOWN_CLIENT_COMPONENT_TYPE || ctype >= CLIENT_TYPE_END)
ctype = UNKNOWN_CLIENT_COMPONENT_TYPE;
INFO_MSG(fmt::format("Loginapp::login: new client[{0}], loginName={1}, datas={2}.\n",
COMPONENT_CLIENT_NAME[ctype], loginName, datas));
pChannel->extra(loginName);
// 向dbmgr查询用户合法性
Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(DbmgrInterface::onAccountLogin);
(*pBundle) << loginName << password;
(*pBundle).appendBlob(datas);
dbmgrinfos->pChannel->send(pBundle);
}
这个函数进行了一系列的检查,确定合法后向dbmgr发送一个登陆请求包“(*pBundle).newMessage(DbmgrInterface::onAccountLogin);
然后在dbmgr.cpp中
//-------------------------------------------------------------------------------------
void Dbmgr::onAccountLogin(Network::Channel* pChannel, KBEngine::MemoryStream& s)
{
std::string loginName, password, datas;
s >> loginName >> password;
s.readBlob(datas);
if(loginName.size() == 0)
{
ERROR_MSG("Dbmgr::onAccountLogin: loginName is empty.\n");
return;
}
pInterfacesAccountHandler_->loginAccount(pChannel, loginName, password, datas);
}
转到interfaces_handler.cpp中
1 //-------------------------------------------------------------------------------------
2 bool InterfacesHandler_Dbmgr::loginAccount(Network::Channel* pChannel, std::string& loginName,
3 std::string& password, std::string& datas)
4 {
5 std::string dbInterfaceName = Dbmgr::getSingleton().selectAccountDBInterfaceName(loginName);
6
7 thread::ThreadPool* pThreadPool = DBUtil::pThreadPool(dbInterfaceName);
8 if (!pThreadPool)
9 {
10 ERROR_MSG(fmt::format("InterfacesHandler_Dbmgr::loginAccount: not found dbInterface({})!\n",
11 dbInterfaceName));
12
13 return false;
14 }
15
16 pThreadPool->addTask(new DBTaskAccountLogin(pChannel->addr(),
17 loginName, loginName, password, SERVER_SUCCESS, datas, datas, true));
18
19 return true;
20 }
点开任务DBTaskAccountLogin,基于上篇文章说过的理由只看这两个函数
1 bool DBTaskAccountLogin::db_thread_process()
2 {
3 // 如果Interfaces已经判断不成功就没必要继续下去
4 if(retcode_ != SERVER_SUCCESS)
5 {
6 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): interfaces is failed!\n"));
7 return false;
8 }
9
10 retcode_ = SERVER_ERR_OP_FAILED;
11
12 if(accountName_.size() == 0)
13 {
14 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): accountName is NULL!\n"));
15 retcode_ = SERVER_ERR_NAME;
16 return false;
17 }
18
19 ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
20
21 if(pModule == NULL)
22 {
23 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account script[{}], login[{}] failed!\n",
24 DBUtil::accountScriptName(), accountName_));
25
26 retcode_ = SERVER_ERR_SRV_NO_READY;
27 return false;
28 }
29
30 EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi_->name());
31 KBEEntityLogTable* pELTable = static_cast<KBEEntityLogTable*>
32 (entityTables.findKBETable("kbe_entitylog"));
33
34 KBE_ASSERT(pELTable);
35
36 KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
37 KBE_ASSERT(pTable);
38
39 ACCOUNT_INFOS info;
40 info.dbid = 0;
41 info.flags = 0;
42 info.deadline = 0;
43
44 if(!pTable->queryAccount(pdbi_, accountName_, info))
45 {
46 flags_ = info.flags;
47 deadline_ = info.deadline;
48
49 if(ACCOUNT_TYPE(g_kbeSrvConfig.getLoginApp().account_type) != ACCOUNT_TYPE_NORMAL)
50 {
51 if (email_isvalid(accountName_.c_str()))
52 {
53 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): account[{}] is email, autocreate failed!\n",
54 accountName_));
55
56 retcode_ = SERVER_ERR_CANNOT_USE_MAIL;
57 return false;
58 }
59 }
60
61 if (g_kbeSrvConfig.getDBMgr().notFoundAccountAutoCreate ||
62 (Network::Address::NONE != g_kbeSrvConfig.interfacesAddr() && !needCheckPassword_/*第三方处理成功则自动创建账号*/))
63 {
64 if(!DBTaskCreateAccount::writeAccount(pdbi_, accountName_, password_, postdatas_, info) || info.dbid == 0 || info.flags != ACCOUNT_FLAG_NORMAL)
65 {
66 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): writeAccount[{}] is error!\n",
67 accountName_));
68
69 retcode_ = SERVER_ERR_DB;
70 return false;
71 }
72
73 INFO_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], autocreate successfully!\n",
74 accountName_));
75
76 info.password = KBE_MD5::getDigest(password_.data(), (int)password_.length());
77 }
78 else
79 {
80 ERROR_MSG(fmt::format("DBTaskAccountLogin::db_thread_process(): not found account[{}], login failed!\n",
81 accountName_));
82
83 retcode_ = SERVER_ERR_NOT_FOUND_ACCOUNT;
84 return false;
85 }
86 }
87
88 if(info.dbid == 0)
89 return false;
90
91 if(info.flags != ACCOUNT_FLAG_NORMAL)
92 {
93 flags_ = info.flags;
94 return false;
95 }
96
97 if (needCheckPassword_ || Network::Address::NONE == g_kbeSrvConfig.interfacesAddr())
98 {
99 if (kbe_stricmp(info.password.c_str(), KBE_MD5::getDigest(password_.data(), (int)password_.length()).c_str()) != 0)
100 {
101 retcode_ = SERVER_ERR_PASSWORD;
102 return false;
103 }
104 }
105
106 pTable->updateCount(pdbi_, accountName_, info.dbid);
107
108 retcode_ = SERVER_ERR_ACCOUNT_IS_ONLINE;
109 KBEEntityLogTable::EntityLog entitylog;
110 bool success = !pELTable->queryEntity(pdbi_, info.dbid, entitylog, pModule->getUType());
111
112 // 如果有在线纪录
113 if(!success)
114 {
115 componentID_ = entitylog.componentID;
116 entityID_ = entitylog.entityID;
117 }
118 else
119 {
120 retcode_ = SERVER_SUCCESS;
121 }
122
123 dbid_ = info.dbid;
124 flags_ = info.flags;
125 deadline_ = info.deadline;
126 return false;
127 }
128
129 //-------------------------------------------------------------------------------------
130 thread::TPTask::TPTaskState DBTaskAccountLogin::presentMainThread()
131 {
132 DEBUG_MSG(fmt::format("Dbmgr::onAccountLogin:loginName={0}, accountName={1}, success={2}, componentID={3}, dbid={4}, flags={5}, deadline={6}.\n",
133 loginName_,
134 accountName_,
135 retcode_,
136 componentID_,
137 dbid_,
138 flags_,
139 deadline_
140 ));
141
142 // 一个用户登录, 构造一个数据库查询指令并加入到执行队列, 执行完毕将结果返回给loginapp
143 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
144 (*pBundle).newMessage(LoginappInterface::onLoginAccountQueryResultFromDbmgr);
145
146 (*pBundle) << retcode_;
147 (*pBundle) << loginName_;
148 (*pBundle) << accountName_;
149 (*pBundle) << password_;
150 (*pBundle) << componentID_; // 如果大于0则表示账号还存活在某个baseapp上
151 (*pBundle) << entityID_;
152 (*pBundle) << dbid_;
153 (*pBundle) << flags_;
154 (*pBundle) << deadline_;
155 (*pBundle).appendBlob(getdatas_);
156
157 if(!this->send(pBundle))
158 {
159 ERROR_MSG(fmt::format("DBTaskAccountLogin::presentMainThread: channel({}) not found.\n", addr_.c_str()));
160 Network::Bundle::reclaimPoolObject(pBundle);
161 }
162
163 return DBTask::presentMainThread();
164 }
阅读完以后知道要点是queryAccount,查询完毕以后的调用函数是LoginappInterface::onLoginAccountQueryResultFromDbmgr
1 //-------------------------------------------------------------------------------------
2 bool KBEAccountTableMysql::queryAccount(DBInterface * pdbi, const std::string& name, ACCOUNT_INFOS& info)
3 {
4 std::string sqlstr = "select entityDBID, password, flags, deadline from kbe_accountinfos where accountName=\"";
5
6 char* tbuf = new char[name.size() * 2 + 1];
7
8 mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
9 tbuf, name.c_str(), name.size());
10
11 sqlstr += tbuf;
12 sqlstr += "\" or email=\"";
13 sqlstr += tbuf;
14 sqlstr += "\" LIMIT 1";
15 SAFE_RELEASE_ARRAY(tbuf);
16
17 // 如果查询失败则返回存在, 避免可能产生的错误
18 if(!pdbi->query(sqlstr.c_str(), sqlstr.size(), false))
19 return true;
20
21 info.dbid = 0;
22 MYSQL_RES * pResult = mysql_store_result(static_cast<DBInterfaceMysql*>(pdbi)->mysql());
23 if(pResult)
24 {
25 MYSQL_ROW arow = mysql_fetch_row(pResult);
26 if(arow != NULL)
27 {
28 KBEngine::StringConv::str2value(info.dbid, arow[0]);
29 info.name = name;
30 info.password = arow[1];
31
32 KBEngine::StringConv::str2value(info.flags, arow[2]);
33 KBEngine::StringConv::str2value(info.deadline, arow[3]);
34 }
35
36 mysql_free_result(pResult);
37 }
38
39 return info.dbid > 0;
40 }
这里十分出乎我的意料,竟然直接读取了数据库,按道理来说这里读取内存更好些。
好吧,把疑问暂且放下,我们回头来看onLoginAccountQueryResultFromDbmgr
1 //-------------------------------------------------------------------------------------
2 void Loginapp::onLoginAccountQueryResultFromDbmgr(Network::Channel* pChannel, MemoryStream& s)
3 {
4 if(pChannel->isExternal())
5 return;
6
7 std::string loginName, accountName, password, datas;
8 SERVER_ERROR_CODE retcode = SERVER_SUCCESS;
9 COMPONENT_ID componentID;
10 ENTITY_ID entityID;
11 DBID dbid;
12 uint32 flags;
13 uint64 deadline;
14
15 s >> retcode;
16
17 // 登录名既登录时客户端输入的名称, 账号名则是dbmgr查询得到的名称
18 // 这个机制用于一个账号多名称系统或者多个第三方账号系统登入服务器
19 // accountName为本游戏服务器账号所绑定的终身名称
20 // 客户端得到baseapp地址的同时也会返回这个账号名称
21 // 客户端登陆baseapp应该使用这个账号名称登陆
22 s >> loginName;
23 s >> accountName;
24
25 s >> password;
26 s >> componentID;
27 s >> entityID;
28 s >> dbid;
29 s >> flags;
30 s >> deadline;
31
32 s.readBlob(datas);
33
34 //DEBUG_MSG(fmt::format("Loginapp::onLoginAccountQueryResultFromDbmgr: loginName={}.\n",
35 // loginName));
36
37 if((flags & ACCOUNT_FLAG_LOCK) > 0)
38 {
39 _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_LOCK, datas);
40 return;
41 }
42
43 if((flags & ACCOUNT_FLAG_NOT_ACTIVATED) > 0)
44 {
45 _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_NOT_ACTIVATED, datas);
46 return;
47 }
48
49 if(deadline > 0 && ::time(NULL) - deadline <= 0)
50 {
51 _loginFailed(NULL, loginName, SERVER_ERR_ACCOUNT_DEADLINE, datas);
52 return;
53 }
54
55 PendingLoginMgr::PLInfos* infos = pendingLoginMgr_.find(loginName);
56 if(infos == NULL)
57 {
58 _loginFailed(NULL, loginName, SERVER_ERR_SRV_OVERLOAD, datas);
59 return;
60 }
61
62 // 把请求交由脚本处理
63 SCOPED_PROFILE(SCRIPTCALL_PROFILE);
64 PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
65 const_cast<char*>("onLoginCallbackFromDB"),
66 const_cast<char*>("ssHy#"),
67 loginName.c_str(),
68 accountName.c_str(),
69 retcode,
70 datas.c_str(), datas.length());
71
72 if(pyResult != NULL)
73 {
74 Py_DECREF(pyResult);
75 }
76 else
77 {
78 SCRIPT_ERROR_CHECK();
79 }
80
81 infos->datas = datas;
82
83 Network::Channel* pClientChannel = this->networkInterface().findChannel(infos->addr);
84 if(pClientChannel)
85 pClientChannel->extra("");
86
87 if(retcode != SERVER_SUCCESS && entityID == 0 && componentID == 0)
88 {
89 _loginFailed(NULL, loginName, retcode, datas);
90 return;
91 }
92
93 // 获得baseappmgr地址。
94 Components::COMPONENTS& cts = Components::getSingleton().getComponents(BASEAPPMGR_TYPE);
95 Components::ComponentInfos* baseappmgrinfos = NULL;
96 if(cts.size() > 0)
97 baseappmgrinfos = &(*cts.begin());
98
99 if(baseappmgrinfos == NULL || baseappmgrinfos->pChannel == NULL || baseappmgrinfos->cid == 0)
100 {
101 _loginFailed(NULL, loginName, SERVER_ERR_SRV_NO_READY, datas);
102 return;
103 }
104
105 // 如果大于0则说明当前账号仍然存活于某个baseapp上
106 if(componentID > 0)
107 {
108 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
109 (*pBundle).newMessage(BaseappmgrInterface::registerPendingAccountToBaseappAddr);
110 (*pBundle) << componentID << loginName << accountName << password << entityID << dbid << flags << deadline << infos->ctype;
111 (*pBundle).appendBlob(infos->datas);
112 baseappmgrinfos->pChannel->send(pBundle);
113 return;
114 }
115 else
116 {
117 // 注册到baseapp并且获取baseapp的地址
118 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
119 (*pBundle).newMessage(BaseappmgrInterface::registerPendingAccountToBaseapp);
120
121 (*pBundle) << loginName;
122 (*pBundle) << accountName;
123 (*pBundle) << password;
124 (*pBundle) << dbid;
125 (*pBundle) << flags;
126 (*pBundle) << deadline;
127 (*pBundle) << infos->ctype;
128 (*pBundle).appendBlob(infos->datas);
129 baseappmgrinfos->pChannel->send(pBundle);
130 }
131 }
好吧,戏肉来了,终于到了从BaseappMgr请求BaseApp节点的地方,看下它是怎么做负载均衡的
1 //-------------------------------------------------------------------------------------
2 void Baseappmgr::registerPendingAccountToBaseapp(Network::Channel* pChannel, MemoryStream& s)
3 {
4 std::string loginName;
5 std::string accountName;
6 std::string password;
7 std::string datas;
8 DBID entityDBID;
9 uint32 flags;
10 uint64 deadline;
11 COMPONENT_TYPE componentType;
12
13 s >> loginName >> accountName >> password >> entityDBID >> flags >> deadline >> componentType;
14 s.readBlob(datas);
15
16 Components::ComponentInfos* cinfos = Components::getSingleton().findComponent(pChannel);
17 if(cinfos == NULL || cinfos->pChannel == NULL)
18 {
19 ERROR_MSG("Baseappmgr::registerPendingAccountToBaseapp: not found loginapp!\n");
20 return;
21 }
22
23 pending_logins_[loginName] = cinfos->cid;
24
25 updateBestBaseapp();
26
27 if (bestBaseappID_ == 0 && numLoadBalancingApp() == 0)
28 {
29 ERROR_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp: Unable to allocate baseapp for load balancing! baseappSize={}, accountName={}.\n",
30 baseapps_.size(), loginName));
31 }
32
33 ENTITY_ID eid = 0;
34 cinfos = Components::getSingleton().findComponent(BASEAPP_TYPE, bestBaseappID_);
35
36 if (cinfos == NULL || cinfos->pChannel == NULL || cinfos->state != COMPONENT_STATE_RUN)
37 {
38 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
39 ForwardItem* pFI = new AppForwardItem();
40
41 pFI->pBundle = pBundle;
42 (*pBundle).newMessage(BaseappInterface::registerPendingLogin);
43 (*pBundle) << loginName << accountName << password << eid << entityDBID << flags << deadline << componentType;
44 pBundle->appendBlob(datas);
45
46 int runstate = -1;
47 if (cinfos)
48 runstate = (int)cinfos->state;
49
50 WARNING_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp: not found baseapp({}, runstate={}, pChannel={}), message is buffered.\n",
51 bestBaseappID_, runstate, (cinfos && cinfos->pChannel ? cinfos->pChannel->c_str() : "NULL")));
52
53 pFI->pHandler = NULL;
54 forward_anywhere_baseapp_messagebuffer_.push(pFI);
55 return;
56 }
57
58 std::map< COMPONENT_ID, Baseapp >::iterator baseapps_iter = baseapps_.find(bestBaseappID_);
59
60 DEBUG_MSG(fmt::format("Baseappmgr::registerPendingAccountToBaseapp:{}. allocBaseapp={}, numEntities={}.\n",
61 accountName, bestBaseappID_, (bestBaseappID_ > 0 ? baseapps_iter->second.numEntities() : 0)));
62
63 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
64 (*pBundle).newMessage(BaseappInterface::registerPendingLogin);
65 (*pBundle) << loginName << accountName << password << eid << entityDBID << flags << deadline << componentType;
66 pBundle->appendBlob(datas);
67 cinfos->pChannel->send(pBundle);
68
69 // 预先将实体数量增加
70 if (baseapps_iter != baseapps_.end())
71 {
72 baseapps_iter->second.incNumProxices();
73 }
74 }
在函数updateBestBaseapp();中选取了bestBaseappID_ ,然后通过BaseappInterface::registerPendingLogin将对应的数据发给了对应的BaseApp,我们先看下这个选取过程是如何进行的。
1 //-------------------------------------------------------------------------------------
2 void Baseappmgr::updateBestBaseapp()
3 {
4 bestBaseappID_ = findFreeBaseapp();
5 }
6
7 //-------------------------------------------------------------------------------------
8 COMPONENT_ID Baseappmgr::findFreeBaseapp()
9 {
10 std::map< COMPONENT_ID, Baseapp >::iterator iter = baseapps_.begin();
11 COMPONENT_ID cid = 0;
12
13 float minload = 1.f;
14 ENTITY_ID numEntities = 0x7fffffff;
15
16 for(; iter != baseapps_.end(); ++iter)
17 {
18 if ((iter->second.flags() & APP_FLAGS_NOT_PARTCIPATING_LOAD_BALANCING) > 0)
19 continue;
20
21 // 首先进程必须活着且初始化完毕
22 if(!iter->second.isDestroyed() && iter->second.initProgress() > 1.f)
23 {
24 // 如果没有任何实体则无条件分配
25 if(iter->second.numEntities() == 0)
26 return iter->first;
27
28 // 比较并记录负载最小的进程最终被分配
29 if(minload > iter->second.load() ||
30 (minload == iter->second.load() && numEntities > iter->second.numEntities()))
31 {
32 cid = iter->first;
33
34 numEntities = iter->second.numEntities();
35 minload = iter->second.load();
36 }
37 }
38 }
39
40 return cid;
41 }
可见负载均衡策略如下:
1、首先进程必须活着且初始化完毕
2、如果没有任何实体则无条件分配
3、比较并记录负载最小的进程最终被分配
到这里,我们回头看BaseApp是如何处理BaseAppMgr发来的消息的
1 //-------------------------------------------------------------------------------------
2 void Baseapp::registerPendingLogin(Network::Channel* pChannel, KBEngine::MemoryStream& s)
3 {
4 if(pChannel->isExternal())
5 {
6 s.done();
7 return;
8 }
9
10 std::string loginName;
11 std::string accountName;
12 std::string password;
13 std::string datas;
14 ENTITY_ID entityID;
15 DBID entityDBID;
16 uint32 flags;
17 uint64 deadline;
18 COMPONENT_TYPE componentType;
19
20 s >> loginName >> accountName >> password >> entityID >> entityDBID >> flags >> deadline >> componentType;
21 s.readBlob(datas);
22
23 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
24 (*pBundle).newMessage(BaseappmgrInterface::onPendingAccountGetBaseappAddr);
25
26 (*pBundle) << loginName;
27 (*pBundle) << accountName;
28
29 if(strlen((const char*)&g_kbeSrvConfig.getBaseApp().externalAddress) > 0)
30 {
31 (*pBundle) << g_kbeSrvConfig.getBaseApp().externalAddress;
32 }
33 else
34 {
35 (*pBundle) << inet_ntoa((struct in_addr&)networkInterface().extaddr().ip);
36 }
37
38 (*pBundle) << this->networkInterface().extaddr().port;
39 pChannel->send(pBundle);
40
41 PendingLoginMgr::PLInfos* ptinfos = new PendingLoginMgr::PLInfos;
42 ptinfos->accountName = accountName;
43 ptinfos->password = password;
44 ptinfos->entityID = entityID;
45 ptinfos->entityDBID = entityDBID;
46 ptinfos->flags = flags;
47 ptinfos->deadline = deadline;
48 ptinfos->ctype = (COMPONENT_CLIENT_TYPE)componentType;
49 ptinfos->datas = datas;
50 pendingLoginMgr_.add(ptinfos);
51 }
做了两件事情,
第一件是将本节点的外网IP和端口号加到消息里发给BaseAppMgr,消息名是onPendingAccountGetBaseappAddr
第二件事情是将登陆消息加到pendingLoginMgr中
1 //-------------------------------------------------------------------------------------
2 void Baseappmgr::onPendingAccountGetBaseappAddr(Network::Channel* pChannel,
3 std::string& loginName, std::string& accountName, std::string& addr, uint16 port)
4 {
5 sendAllocatedBaseappAddr(pChannel, loginName, accountName, addr, port);
6 }
7
8 //-------------------------------------------------------------------------------------
9 void Baseappmgr::sendAllocatedBaseappAddr(Network::Channel* pChannel,
10 std::string& loginName, std::string& accountName, const std::string& addr, uint16 port)
11 {
12 KBEUnordered_map< std::string, COMPONENT_ID >::iterator iter = pending_logins_.find(loginName);
13 if(iter == pending_logins_.end())
14 {
15 ERROR_MSG("Baseappmgr::sendAllocatedBaseappAddr: not found loginapp, pending_logins is error!\n");
16 return;
17 }
18
19 Components::ComponentInfos* cinfos = Components::getSingleton().findComponent(iter->second);
20 if(cinfos == NULL || cinfos->pChannel == NULL)
21 {
22 ERROR_MSG("Baseappmgr::sendAllocatedBaseappAddr: not found loginapp!\n");
23 return;
24 }
25
26 Network::Bundle* pBundleToLoginapp = Network::Bundle::createPoolObject();
27 (*pBundleToLoginapp).newMessage(LoginappInterface::onLoginAccountQueryBaseappAddrFromBaseappmgr);
28
29 LoginappInterface::onLoginAccountQueryBaseappAddrFromBaseappmgrArgs4::staticAddToBundle((*pBundleToLoginapp), loginName,
30 accountName, addr, port);
31
32 cinfos->pChannel->send(pBundleToLoginapp);
33 pending_logins_.erase(iter);
34 }
可以看见,这里返回了消息给loginapp
1 //-------------------------------------------------------------------------------------
2 void Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr(Network::Channel* pChannel, std::string& loginName,
3 std::string& accountName, std::string& addr, uint16 port)
4 {
5 if(pChannel->isExternal())
6 return;
7
8 if(addr.size() == 0)
9 {
10 ERROR_MSG(fmt::format("Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr:accountName={}, not found baseapp, Please check the baseappmgr errorlog!\n",
11 loginName));
12
13 std::string datas;
14 _loginFailed(NULL, loginName, SERVER_ERR_SRV_NO_READY, datas);
15 }
16
17 Network::Address address(addr, ntohs(port));
18
19 DEBUG_MSG(fmt::format("Loginapp::onLoginAccountQueryBaseappAddrFromBaseappmgr:accountName={0}, addr={1}.\n",
20 loginName, address.c_str()));
21
22 // 这里可以不做删除, 仍然使其保留一段时间避免同一时刻同时登录造成意外影响
23 PendingLoginMgr::PLInfos* infos = pendingLoginMgr_.remove(loginName);
24 if(infos == NULL)
25 return;
26
27 infos->lastProcessTime = timestamp();
28 Network::Channel* pClientChannel = this->networkInterface().findChannel(infos->addr);
29
30 if(pClientChannel == NULL)
31 {
32 SAFE_RELEASE(infos);
33 return;
34 }
35
36 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
37 (*pBundle).newMessage(ClientInterface::onLoginSuccessfully);
38 uint16 fport = ntohs(port);
39 (*pBundle) << accountName;
40 (*pBundle) << addr;
41 (*pBundle) << fport;
42 (*pBundle).appendBlob(infos->datas);
43 pClientChannel->send(pBundle);
44
45 SAFE_RELEASE(infos);
46 }
我们回到客户端
/*
登录loginapp成功了
*/
public void Client_onLoginSuccessfully(MemoryStream stream)
{
var accountName = stream.readString();
username = accountName;
baseappIP = stream.readString();
baseappPort = stream.readUint16();
Dbg.DEBUG_MSG("KBEngine::Client_onLoginSuccessfully: accountName(" + accountName + "), addr(" +
baseappIP + ":" + baseappPort + "), datas(" + _serverdatas.Length + ")!");
_serverdatas = stream.readBlob();
Person p= (Person)BytesToObject (_serverdatas);
Dbg.DEBUG_MSG("KBEngine::Client_onLoginSuccessfully: p.Age(" + p.Age + "), p.Name(" + p.Name +")!");
login_baseapp(true);
}
客户端向对应的baseapp发送登录请求
/*
登录到服务端,登录到网关(baseapp)
*/
public void login_baseapp(bool noconnect)
{
if(noconnect)
{
Event.fireOut("onLoginBaseapp", new object[]{});
_networkInterface.reset();
_networkInterface = new NetworkInterface();
_networkInterface.connectTo(baseappIP, baseappPort, onConnectTo_baseapp_callback, null);
}
else
{
Bundle bundle = Bundle.createObject();
bundle.newMessage(Message.messages["Baseapp_loginBaseapp"]);
bundle.writeString(username);
bundle.writeString(password);
bundle.send(_networkInterface);
}
}
回到服务器对应的baseapp,查看loginBaseapp方法
1 //-------------------------------------------------------------------------------------
2 void Baseapp::loginBaseapp(Network::Channel* pChannel,
3 std::string& accountName,
4 std::string& password)
5 {
6 accountName = KBEngine::strutil::kbe_trim(accountName);
7 if(accountName.size() > ACCOUNT_NAME_MAX_LENGTH)
8 {
9 ERROR_MSG(fmt::format("Baseapp::loginBaseapp: accountName too big, size={}, limit={}.\n",
10 accountName.size(), ACCOUNT_NAME_MAX_LENGTH));
11
12 return;
13 }
14
15 if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
16 {
17 ERROR_MSG(fmt::format("Baseapp::loginBaseapp: password too big, size={}, limit={}.\n",
18 password.size(), ACCOUNT_PASSWD_MAX_LENGTH));
19
20 return;
21 }
22
23 INFO_MSG(fmt::format("Baseapp::loginBaseapp: new user[{0}], channel[{1}].\n",
24 accountName, pChannel->c_str()));
25
26 Components::ComponentInfos* dbmgrinfos = Components::getSingleton().getDbmgr();
27 if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == 0)
28 {
29 loginBaseappFailed(pChannel, accountName, SERVER_ERR_SRV_NO_READY);
30 return;
31 }
32
33 PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.find(accountName);
34 if(ptinfos == NULL)
35 {
36 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ILLEGAL_LOGIN);
37 return;
38 }
39 else if (!ptinfos->addr.isNone() && ptinfos->addr != pChannel->addr())
40 {
41 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ILLEGAL_LOGIN);
42 return;
43 }
44
45 if(ptinfos->password != password)
46 {
47 loginBaseappFailed(pChannel, accountName, SERVER_ERR_PASSWORD);
48 return;
49 }
50
51 if((ptinfos->flags & ACCOUNT_FLAG_LOCK) > 0)
52 {
53 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_LOCK);
54 return;
55 }
56
57 if((ptinfos->flags & ACCOUNT_FLAG_NOT_ACTIVATED) > 0)
58 {
59 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_NOT_ACTIVATED);
60 return;
61 }
62
63 if(ptinfos->deadline > 0 && ::time(NULL) - ptinfos->deadline <= 0)
64 {
65 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_DEADLINE);
66 return;
67 }
68
69 if(idClient_.size() == 0)
70 {
71 ERROR_MSG("Baseapp::loginBaseapp: idClient size is 0.\n");
72 loginBaseappFailed(pChannel, accountName, SERVER_ERR_SRV_NO_READY);
73 return;
74 }
75
76 // 如果entityID大于0则说明此entity是存活状态登录
77 if(ptinfos->entityID > 0)
78 {
79 INFO_MSG(fmt::format("Baseapp::loginBaseapp: user[{}] has entity({}).\n",
80 accountName.c_str(), ptinfos->entityID));
81
82 Proxy* base = static_cast<Proxy*>(findEntity(ptinfos->entityID));
83 if(base == NULL || base->isDestroyed())
84 {
85 loginBaseappFailed(pChannel, accountName, SERVER_ERR_BUSY);
86 return;
87 }
88
89 pendingLoginMgr_.removeNextTick(accountName);
90
91 // 防止在onLogOnAttempt中销毁了
92 Py_INCREF(base);
93
94 // 通知脚本异常登录请求有脚本决定是否允许这个通道强制登录
95 int32 ret = base->onLogOnAttempt(pChannel->addr().ipAsString(),
96 ntohs(pChannel->addr().port), password.c_str());
97
98 if (base->isDestroyed())
99 {
100 Py_DECREF(base);
101
102 loginBaseappFailed(pChannel, accountName, SERVER_ERR_OP_FAILED);
103 return;
104 }
105
106 switch(ret)
107 {
108 case LOG_ON_ACCEPT:
109 if(base->clientMailbox() != NULL)
110 {
111 // 通告在别处登录
112 Network::Channel* pOldClientChannel = base->clientMailbox()->getChannel();
113 if(pOldClientChannel != NULL)
114 {
115 INFO_MSG(fmt::format("Baseapp::loginBaseapp: script LOG_ON_ACCEPT. oldClientChannel={}\n",
116 pOldClientChannel->c_str()));
117
118 kickChannel(pOldClientChannel, SERVER_ERR_ACCOUNT_LOGIN_ANOTHER);
119 }
120 else
121 {
122 INFO_MSG("Baseapp::loginBaseapp: script LOG_ON_ACCEPT.\n");
123 }
124
125 base->clientMailbox()->addr(pChannel->addr());
126 base->addr(pChannel->addr());
127 base->setClientType(ptinfos->ctype);
128 base->setClientDatas(ptinfos->datas);
129 createClientProxies(base, true);
130 base->onGetWitness();
131 }
132 else
133 {
134 // 创建entity的客户端mailbox
135 EntityMailbox* entityClientMailbox = new EntityMailbox(base->pScriptModule(),
136 &pChannel->addr(), 0, base->id(), MAILBOX_TYPE_CLIENT);
137
138 base->clientMailbox(entityClientMailbox);
139 base->addr(pChannel->addr());
140 base->setClientType(ptinfos->ctype);
141 base->setClientDatas(ptinfos->datas);
142
143 // 将通道代理的关系与该entity绑定, 在后面通信中可提供身份合法性识别
144 entityClientMailbox->getChannel()->proxyID(base->id());
145 createClientProxies(base, true);
146 base->onGetWitness();
147 }
148 break;
149 case LOG_ON_WAIT_FOR_DESTROY:
150 default:
151 INFO_MSG("Baseapp::loginBaseapp: script LOG_ON_REJECT.\n");
152 loginBaseappFailed(pChannel, accountName, SERVER_ERR_ACCOUNT_IS_ONLINE);
153 Py_DECREF(base);
154 return;
155 };
156
157 Py_DECREF(base);
158 }
159 else
160 {
161 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
162 (*pBundle).newMessage(DbmgrInterface::queryAccount);
163
164 ENTITY_ID entityID = idClient_.alloc();
165 KBE_ASSERT(entityID > 0);
166
167 DbmgrInterface::queryAccountArgs7::staticAddToBundle((*pBundle), accountName, password, g_componentID,
168 entityID, ptinfos->entityDBID, pChannel->addr().ip, pChannel->addr().port);
169
170 dbmgrinfos->pChannel->send(pBundle);
171 }
172
173 // 记录客户端地址
174 ptinfos->addr = pChannel->addr();
175 }
这里最重要的事情是,
1、如果存在实体,创立mailbox,并且绑定mailbox给对应的实体
2、如果不存在实体,那么调用DbmgrInterface::queryAccount
1 void Dbmgr::queryAccount(Network::Channel* pChannel,
2 std::string& accountName,
3 std::string& password,
4 COMPONENT_ID componentID,
5 ENTITY_ID entityID,
6 DBID entityDBID,
7 uint32 ip,
8 uint16 port)
9 {
10 if(accountName.size() == 0)
11 {
12 ERROR_MSG("Dbmgr::queryAccount: accountName is empty.\n");
13 return;
14 }
15
16 Buffered_DBTasks* pBuffered_DBTasks =
17 findBufferedDBTask(Dbmgr::getSingleton().selectAccountDBInterfaceName(accountName));
18
19 if (!pBuffered_DBTasks)
20 {
21 ERROR_MSG(fmt::format("Dbmgr::queryAccount: not found dbInterface({})!\n",
22 Dbmgr::getSingleton().selectAccountDBInterfaceName(accountName)));
23 return;
24 }
25
26 pBuffered_DBTasks->addTask(new DBTaskQueryAccount(pChannel->addr(), accountName, password,
27 componentID, entityID, entityDBID, ip, port));
28
29 numQueryEntity_++;
30 }
我们来看下DBTaskQueryAccount的最关键的两个方法
1 //-------------------------------------------------------------------------------------
2 bool DBTaskQueryAccount::db_thread_process()
3 {
4 if(accountName_.size() == 0)
5 {
6 error_ = "accountName_ is NULL";
7 return false;
8 }
9
10 EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi_->name());
11 KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
12 KBE_ASSERT(pTable);
13
14 ACCOUNT_INFOS info;
15 info.name = "";
16 info.password = "";
17 info.dbid = dbid_;
18
19 if(dbid_ == 0)
20 {
21 if(!pTable->queryAccount(pdbi_, accountName_, info))
22 {
23 error_ = "pTable->queryAccount() is failed!";
24
25 if(pdbi_->getlasterror() > 0)
26 {
27 error_ += pdbi_->getstrerror();
28 }
29
30 return false;
31 }
32
33 if(info.dbid == 0)
34 {
35 error_ = "dbid is 0";
36 return false;
37 }
38
39 if(info.dbid == 0 || info.flags != ACCOUNT_FLAG_NORMAL)
40 {
41 error_ = "flags != ACCOUNT_FLAG_NORMAL";
42 flags_ = info.flags;
43 return false;
44 }
45
46 if (kbe_stricmp(info.password.c_str(), KBE_MD5::getDigest(password_.data(), (int)password_.length()).c_str()) != 0)
47 {
48 error_ = "password is error";
49 return false;
50 }
51 }
52
53 ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
54 success_ = entityTables.queryEntity(pdbi_, info.dbid, &s_, pModule);
55
56 if(!success_ && pdbi_->getlasterror() > 0)
57 {
58 error_ += "queryEntity: ";
59 error_ += pdbi_->getstrerror();
60 }
61
62 dbid_ = info.dbid;
63
64 if(!success_)
65 return false;
66
67 success_ = false;
68
69 // 先写log, 如果写失败则可能这个entity已经在线
70 KBEEntityLogTable* pELTable = static_cast<KBEEntityLogTable*>
71 (entityTables.findKBETable("kbe_entitylog"));
72 KBE_ASSERT(pELTable);
73
74 success_ = pELTable->logEntity(pdbi_, inet_ntoa((struct in_addr&)ip_), port_, dbid_,
75 componentID_, entityID_, pModule->getUType());
76
77 if(!success_ && pdbi_->getlasterror() > 0)
78 {
79 error_ += "logEntity: ";
80 error_ += pdbi_->getstrerror();
81 }
82
83 flags_ = info.flags;
84 deadline_ = info.deadline;
85
86 return false;
87 }
88
89 //-------------------------------------------------------------------------------------
90 thread::TPTask::TPTaskState DBTaskQueryAccount::presentMainThread()
91 {
92 DEBUG_MSG(fmt::format("Dbmgr::queryAccount: {}, success={}, flags={}, deadline={}.\n",
93 accountName_.c_str(), success_, flags_, deadline_));
94
95 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
96 (*pBundle).newMessage(BaseappInterface::onQueryAccountCBFromDbmgr);
97 (*pBundle) << pdbi_->dbIndex();
98 (*pBundle) << accountName_;
99 (*pBundle) << password_;
100 (*pBundle) << dbid_;
101 (*pBundle) << success_;
102 (*pBundle) << entityID_;
103 (*pBundle) << flags_;
104 (*pBundle) << deadline_;
105
106 if(success_)
107 {
108 pBundle->append(s_);
109 }
110 else
111 {
112 (*pBundle) << error_;
113 }
114
115 if(!this->send(pBundle))
116 {
117 ERROR_MSG(fmt::format("DBTaskQueryAccount::presentMainThread: channel({}) not found.\n", addr_.c_str()));
118 Network::Bundle::reclaimPoolObject(pBundle);
119 }
120
121 return EntityDBTask::presentMainThread();
122 }
比对过密码的MD5等数据后,通过onQueryAccountCBFromDbmgr消息返回对应的baseapp,
1 //-------------------------------------------------------------------------------------
2 void Baseapp::onQueryAccountCBFromDbmgr(Network::Channel* pChannel, KBEngine::MemoryStream& s)
3 {
4 if(pChannel->isExternal())
5 return;
6
7 std::string accountName;
8 std::string password;
9 bool success = false;
10 DBID dbid;
11 ENTITY_ID entityID;
12 uint32 flags;
13 uint64 deadline;
14 uint16 dbInterfaceIndex;
15
16 s >> dbInterfaceIndex >> accountName >> password >> dbid >> success >> entityID >> flags >> deadline;
17
18 PendingLoginMgr::PLInfos* ptinfos = pendingLoginMgr_.remove(accountName);
19 if(ptinfos == NULL)
20 {
21 ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: PendingLoginMgr not found({})\n",
22 accountName.c_str()));
23
24 s.done();
25 return;
26 }
27
28 Proxy* base = static_cast<Proxy*>(createEntity(g_serverConfig.getDBMgr().dbAccountEntityScriptType,
29 NULL, false, entityID));
30
31 Network::Channel* pClientChannel = this->networkInterface().findChannel(ptinfos->addr);
32
33 if(!base)
34 {
35 ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: create {} is failed! error(base == NULL)\n",
36 accountName.c_str()));
37
38 s.done();
39
40 loginBaseappFailed(pClientChannel, accountName, SERVER_ERR_SRV_NO_READY);
41 return;
42 }
43
44 if(!success)
45 {
46 std::string error;
47 s >> error;
48 ERROR_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: query {} is failed! error({})\n",
49 accountName.c_str(), error));
50
51 s.done();
52
53 loginBaseappFailed(pClientChannel, accountName, SERVER_ERR_SRV_NO_READY);
54
55 this->destroyEntity(base->id(), true);
56 return;
57 }
58
59 KBE_ASSERT(base != NULL);
60 base->hasDB(true);
61 base->dbid(dbInterfaceIndex, dbid);
62 base->setClientType(ptinfos->ctype);
63 base->setClientDatas(ptinfos->datas);
64
65 PyObject* pyDict = createCellDataDictFromPersistentStream(s, g_serverConfig.getDBMgr().dbAccountEntityScriptType);
66
67 PyObject* py__ACCOUNT_NAME__ = PyUnicode_FromString(accountName.c_str());
68 PyDict_SetItemString(pyDict, "__ACCOUNT_NAME__", py__ACCOUNT_NAME__);
69 Py_DECREF(py__ACCOUNT_NAME__);
70
71 PyObject* py__ACCOUNT_PASSWD__ = PyUnicode_FromString(KBE_MD5::getDigest(password.data(), (int)password.length()).c_str());
72 PyDict_SetItemString(pyDict, "__ACCOUNT_PASSWORD__", py__ACCOUNT_PASSWD__);
73 Py_DECREF(py__ACCOUNT_PASSWD__);
74
75 Py_INCREF(base);
76 base->initializeEntity(pyDict);
77 Py_DECREF(pyDict);
78
79 if(pClientChannel != NULL)
80 {
81 // 创建entity的客户端mailbox
82 EntityMailbox* entityClientMailbox = new EntityMailbox(base->pScriptModule(),
83 &pClientChannel->addr(), 0, base->id(), MAILBOX_TYPE_CLIENT);
84
85 base->clientMailbox(entityClientMailbox);
86 base->addr(pClientChannel->addr());
87
88 createClientProxies(base);
89
90 /*
91 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
92 (*pBundle).newMessage(DbmgrInterface::onAccountOnline);
93
94 DbmgrInterface::onAccountOnlineArgs3::staticAddToBundle((*pBundle), accountName,
95 componentID_, base->id());
96
97 pChannel->send(pBundle);
98 */
99 }
100
101 INFO_MSG(fmt::format("Baseapp::onQueryAccountCBFromDbmgr: user={}, uuid={}, entityID={}, flags={}, deadline={}.\n",
102 accountName, base->rndUUID(), base->id(), flags, deadline));
103
104 SAFE_RELEASE(ptinfos);
105 Py_DECREF(base);
106 }
通过
Proxy* base = static_cast<Proxy*>(createEntity(g_serverConfig.getDBMgr().dbAccountEntityScriptType,
NULL, false, entityID));
创建了对应的实体,并且创建对应的mailbox绑定上去,值得注意的是createClientProxies(base);这个函数,这个函数会将实体创建完毕的消息传回给客户端,并告知客户端此通道的存在
此后脚本层的消息传递,就直接通过这个通道进行
1 //-------------------------------------------------------------------------------------
2 bool Baseapp::createClientProxies(Proxy* base, bool reload)
3 {
4 Py_INCREF(base);
5
6 // 将通道代理的关系与该entity绑定, 在后面通信中可提供身份合法性识别
7 Network::Channel* pChannel = base->clientMailbox()->getChannel();
8 pChannel->proxyID(base->id());
9 base->addr(pChannel->addr());
10
11 // 重新生成一个ID
12 if(reload)
13 base->rndUUID(genUUID64());
14
15 // 一些数据必须在实体创建后立即访问
16 base->initClientBasePropertys();
17
18 // 让客户端知道已经创建了proxices, 并初始化一部分属性
19 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
20 (*pBundle).newMessage(ClientInterface::onCreatedProxies);
21 (*pBundle) << base->rndUUID();
22 (*pBundle) << base->id();
23 (*pBundle) << base->ob_type->tp_name;
24 //base->clientMailbox()->postMail((*pBundle));
25 base->sendToClient(ClientInterface::onCreatedProxies, pBundle);
26
27 // 本应该由客户端告知已经创建好entity后调用这个接口。
28 //if(!reload)
29 base->onEntitiesEnabled();
30 Py_DECREF(base);
31 return true;
32 }
在这里一方面告知客户端onCreatedProxies消息,一方面进行脚本的onEntitiesEnabled消息的回调
我们回到unity端,阅读对应的代码
/*
服务端通知创建一个角色
*/
public void Client_onCreatedProxies(UInt64 rndUUID, Int32 eid, string entityType)
{
Dbg.DEBUG_MSG("KBEngine::Client_onCreatedProxies: eid(" + eid + "), entityType(" + entityType + ")!");
entity_uuid = rndUUID;
entity_id = eid;
entity_type = entityType;
if(!this.entities.ContainsKey(eid))
{
ScriptModule module = null;
if(!EntityDef.moduledefs.TryGetValue(entityType, out module))
{
Dbg.ERROR_MSG("KBEngine::Client_onCreatedProxies: not found module(" + entityType + ")!");
return;
}
Type runclass = module.script;
if(runclass == null)
return;
Entity entity = (Entity)Activator.CreateInstance(runclass);
entity.id = eid;
entity.className = entityType;
entity.baseMailbox = new Mailbox();
entity.baseMailbox.id = eid;
entity.baseMailbox.className = entityType;
entity.baseMailbox.type = Mailbox.MAILBOX_TYPE.MAILBOX_TYPE_BASE;
entities[eid] = entity;
MemoryStream entityMessage = null;
_bufferedCreateEntityMessage.TryGetValue(eid, out entityMessage);
if(entityMessage != null)
{
Client_onUpdatePropertys(entityMessage);
_bufferedCreateEntityMessage.Remove(eid);
entityMessage.reclaimObject();
}
entity.__init__();
entity.inited = true;
if(_args.isOnInitCallPropertysSetMethods)
entity.callPropertysSetMethods();
}
else
{
MemoryStream entityMessage = null;
_bufferedCreateEntityMessage.TryGetValue(eid, out entityMessage);
if(entityMessage != null)
{
Client_onUpdatePropertys(entityMessage);
_bufferedCreateEntityMessage.Remove(eid);
entityMessage.reclaimObject();
}
}
}
客户端也进行了mailbox的绑定,并且回调了对应entity的init方法,account的entity是account.cs,点开看下
public override void __init__()
{
Event.fireOut("onLoginSuccessfully", new object[]{KBEngineApp.app.entity_uuid, id, this});
baseCall("reqAvatarList", new object[0]);
}
这里开始,请求角色列表,放到下一篇文章吧。
我这篇文章拷贝黏贴了大量的代码,C++的代码十分臃肿,KBE已经进行了大量的封装,使其更接近逻辑的本质。
我是青岛远硕信息科技发展有限公司的Peter,如果转载的话,请保留这段文字。

浙公网安备 33010602011771号