原本不打算把登录拿出来写的,但是阅读登录部分的代码的时候发现登录和注册还不太一回事,因为登录涉及到分配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);elseSCRIPT_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         else79         {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     else77     {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的客户端mailbox82         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,如果转载的话,请保留这段文字。

KBEngine warring项目源码阅读(二) 登录和baseapp的负载均衡相关推荐

  1. KBEngine warring项目源码阅读(一) 项目简介和注册

    首先介绍下warring项目,是kbe自带的一个演示示例,大部分人了解kbe引擎也是从warring项目开始的. 项目地址:https://github.com/kbengine/kbengine_u ...

  2. KBEngine warring项目源码阅读(三) 实体文件与Account处理

    上一篇开始,我们就提到了一个概念,并且进行了初步的运用,这个概念就是实体. KBE中的实体是一个很重要的概念,可以说,有了实体就有了一切. 我们首先接着上一章的内容,来看Account.def对应的实 ...

  3. mybatis源码阅读(二):mybatis初始化上

    转载自  mybatis源码阅读(二):mybatis初始化上 1.初始化入口 //Mybatis 通过SqlSessionFactory获取SqlSession, 然后才能通过SqlSession与 ...

  4. openedge-hub模块请求处理源码浅析——百度BIE边缘侧openedge项目源码阅读(2)

    前言 在openedge-hub模块启动源码浅析--百度BIE边缘侧openedge项目源码阅读(1)一文中浅析了openedge-hub模块的启动过程,openedge-hub为每一个连接的请求创建 ...

  5. LeGo-LOAM激光雷达定位算法源码阅读(二)

    文章目录 1.featureAssociation框架 1.1节点代码主体 1.2 FeatureAssociation构造函数 1.3 runFeatureAssociation()主体函数 2.重 ...

  6. oim(类QQ)开源项目源码阅读笔记(1)——登录部分

    oim项目是码云上相当优秀的开源项目,项目完整,有客户端,服务器端web端,项目地址:https://gitee.com/oimchat 因为最近打算使用javafx写个客户端,所以看了一下该项目基于 ...

  7. 开源项目源码阅读方法

    本文转载自:知乎:关于阅读开源项目的源码,有哪些经验值得分享? iammutex 读源码,用CodeWa吧~ 214 人赞同 我先说下自己的经历吧. 我比较完整的读过的有wordpress,ttser ...

  8. openedge-hub模块启动源码浅析——百度BIE边缘侧openedge项目源码阅读(1)

    前言 因为最近项目需要用到边缘计算,结合百度的openedge进行开发,openedge目前主要功能为结合docker容器实现边缘计算,具体内容官网很多,其架构中,openedge-hub作为所有模块 ...

  9. nginx源码阅读(二).初始化:main函数及ngx_init_cycle函数

    前言 在分析源码时,我们可以先把握主干,然后其他部分再挨个分析就行了.接下来我们先看看nginx的main函数干了些什么. main函数 这里先介绍一些下面会遇到的变量类型: ngx_int_t: t ...

最新文章

  1. 传感器数据完善 AI 功能,激起机器人“网络效应”
  2. Sturt2做表单重复提交
  3. S/4HANA生产订单的标准状态和透明工厂原型状态的映射
  4. rsync推拉模型及结合inotify实现推模型自动同步
  5. Silverlight 动画性能
  6. XElement.Load 需要释放吗_因为信用卡逾期还不上坐牢了,刑满释放后,还需要继续还钱吗?...
  7. Java集合Collection之实现原理解读(HashSet)
  8. 频域波束形成matlab,关于FFT波束形成
  9. matlab抛物柱面 y 2=2x,求曲面z=根号(x2+y2)被柱面z2=2x割下部分的面积
  10. 一张图教你清理IE浏览器缓存
  11. 流程执行效果如何评估
  12. 最新《播布客李明新概念C语言作品》
  13. Win7系统打印机不能打印的问题
  14. loj534. 「LibreOJ Round #6」花团
  15. android屏幕坏 操作手机,手机屏幕碎了怎么备份操作?
  16. 购买运动耳机应该考虑什么问题,六款运动耳机推荐
  17. 开发信如何写打开率才更高
  18. 03 html基础详解
  19. 世界读书日:带你走近Go语言编程思维
  20. JavaWeb做项目所需模板-代码库(开源)

热门文章

  1. 程序员必备的提升工作效率的方法
  2. Linux C编程一站式学习笔记2
  3. 174. 地下城游戏;剑指 Offer 40. 最小的k个数;378. 有序矩阵中第K小的元素;703. 数据流中的第K大元素
  4. java判定成绩等级_Java编程判断一组学生成绩等级
  5. 做人,别伤人,别骗人,别负人!
  6. 安徽农商行计算机类笔试考什么,2019安徽农商行社会招聘:笔试考什么?如何复习? 【附带笔试模拟题】...
  7. 新书的各种购买方式汇总【人人都是产品经理】
  8. 基于android的同学录校友录系统app
  9. ST电机库v5.4.4源代码分析(7): SpeednPosFdbk_Handle_t在几个结构体中的关系
  10. 如何将Scratch作品转换成HTML或者应用程序