首先介绍下warring项目,是kbe自带的一个演示示例,大部分人了解kbe引擎也是从warring项目开始的。

项目地址:https://github.com/kbengine/kbengine_unity3d_warring

项目截图:

项目的下载和安装不再多说,现在开始进入代码讲解阶段:

注册:

流程图:

可以看到控件绑定代码为reg_ok,点进去

 1 void reg_ok()
 2     {
 3         log_label.obj.text = "请求连接服务器...";
 4         log_label.obj.color = UnityEngine.Color.green;
 5
 6         if(reg_username.input.text == "" || reg_username.input.text.Length > 30)
 7         {
 8             log_label.obj.color = UnityEngine.Color.red;
 9             log_label.obj.text = "用户名或者邮箱地址不合法, 最大长度限制30个字符。";
10             Common.WARNING_MSG("ui::reg_ok: invalid username!");
11             return;
12         }
13
14         if(reg_password.input.text.Length < 6 || reg_password.input.text.Length > 16)
15         {
16             log_label.obj.color = UnityEngine.Color.red;
17             log_label.obj.text = "密码不合法, 长度限制在6~16位之间。";
18             Common.WARNING_MSG("ui::reg_ok: invalid reg_password!");
19             return;
20         }
21
22         if(reg_password.input.text != reg_passwordok.input.text)
23         {
24             log_label.obj.color = UnityEngine.Color.red;
25             log_label.obj.text = "二次输入密码不匹配。";
26             Common.WARNING_MSG("ui::reg_ok: reg_password != reg_passwordok!");
27             return;
28         }
29
30         KBEngine.Event.fireIn("createAccount", reg_username.input.text, reg_passwordok.input.text, System.Text.Encoding.UTF8.GetBytes("kbengine_unity_warring"));
31         log_label.obj.text = "连接成功,等待处理请稍后...";
32     }

  可以看到接下来是fireIn("createAccount",xxxx,...)

  这里需要讲解一下客户端的fireIn和fireOut是怎么一回事,fireIn是指u3d脚本层触发一个事件给kbe插件执行,fireOut是是插件向u3d脚本层触发的事件,总之是从unity到kbe插件的一个交互过程。既然是插件层层,那么我们打开KBEngine.cs去找对应的registerIn,可以找到下面的代码

 1         void installEvents()
 2         {
 3             Event.registerIn("createAccount", this, "createAccount");
 4             Event.registerIn("login", this, "login");
 5             Event.registerIn("reloginBaseapp", this, "reloginBaseapp");
 6             Event.registerIn("resetPassword", this, "resetPassword");
 7             Event.registerIn("bindAccountEmail", this, "bindAccountEmail");
 8             Event.registerIn("newPassword", this, "newPassword");
 9
10             // 内部事件
11             Event.registerIn("_closeNetwork", this, "_closeNetwork");
12         }

然后在同一文件的第727行,找到对应的消息,可以看到下一步是调用的createAccount_loginapp(false)函数

点开进去

 1         /*
 2             创建账号,通过loginapp
 3         */
 4         public void createAccount_loginapp(bool noconnect)
 5         {
 6             if(noconnect)
 7             {
 8                 reset();
 9                 _networkInterface.connectTo(_args.ip, _args.port, onConnectTo_createAccount_callback, null);
10             }
11             else
12             {
13                 Bundle bundle = Bundle.createObject();
14                 bundle.newMessage(Message.messages["Loginapp_reqCreateAccount"]);
15                 bundle.writeString(username);
16                 bundle.writeString(password);
17                 //string imei = 'AET89766-124';
18                 //bundle.writeString(imei);
19                 bundle.writeBlob(KBEngineApp.app._clientdatas);
20                 bundle.send(_networkInterface);
21             }
22         }

可以看到这里开始给后端发了一个消息,消息关键字是Loginapp_reqCreateAccount。我们打开kbe的C++部分源码

在loginapp项目中,找到loginapp.cpp的reqCreateAccount方法,为什么要找这个方法,因为在代码底层识别的时候将关键字变为了前半段代表的节点名,后半段代表消息。

 1 //-------------------------------------------------------------------------------------
 2 void Loginapp::reqCreateAccount(Network::Channel* pChannel, MemoryStream& s)
 3 {
 4     std::string accountName, password, datas;
 5
 6     s >> accountName >> password;
 7     s.readBlob(datas);
 8
 9     if(!_createAccount(pChannel, accountName, password, datas, ACCOUNT_TYPE(g_serverConfig.getLoginApp().account_type)))
10         return;
11 }

点开_createAccount

  1 //-------------------------------------------------------------------------------------
  2 bool Loginapp::_createAccount(Network::Channel* pChannel, std::string& accountName,
  3                                  std::string& password, std::string& datas, ACCOUNT_TYPE type)
  4 {
  5     AUTO_SCOPED_PROFILE("createAccount");
  6
  7     ACCOUNT_TYPE oldType = type;
  8
  9     if(!g_kbeSrvConfig.getDBMgr().account_registration_enable)
 10     {
 11         ERROR_MSG(fmt::format("Loginapp::_createAccount({}): not available! modify kbengine[_defs].xml->dbmgr->account_registration.\n",
 12             accountName));
 13
 14         std::string retdatas = "";
 15         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
 16         (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
 17         SERVER_ERROR_CODE retcode = SERVER_ERR_ACCOUNT_REGISTER_NOT_AVAILABLE;
 18         (*pBundle) << retcode;
 19         (*pBundle).appendBlob(retdatas);
 20         pChannel->send(pBundle);
 21         return false;
 22     }
 23
 24     accountName = KBEngine::strutil::kbe_trim(accountName);
 25     password = KBEngine::strutil::kbe_trim(password);
 26
 27     if(accountName.size() > ACCOUNT_NAME_MAX_LENGTH)
 28     {
 29         ERROR_MSG(fmt::format("Loginapp::_createAccount: accountName too big, size={}, limit={}.\n",
 30             accountName.size(), ACCOUNT_NAME_MAX_LENGTH));
 31
 32         return false;
 33     }
 34
 35     if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
 36     {
 37         ERROR_MSG(fmt::format("Loginapp::_createAccount: password too big, size={}, limit={}.\n",
 38             password.size(), ACCOUNT_PASSWD_MAX_LENGTH));
 39
 40         return false;
 41     }
 42
 43     if(datas.size() > ACCOUNT_DATA_MAX_LENGTH)
 44     {
 45         ERROR_MSG(fmt::format("Loginapp::_createAccount: bindatas too big, size={}, limit={}.\n",
 46             datas.size(), ACCOUNT_DATA_MAX_LENGTH));
 47
 48         return false;
 49     }
 50
 51     std::string retdatas = "";
 52     if(shuttingdown_ != SHUTDOWN_STATE_STOP)
 53     {
 54         WARNING_MSG(fmt::format("Loginapp::_createAccount: shutting down, create {} failed!\n", accountName));
 55
 56         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
 57         (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
 58         SERVER_ERROR_CODE retcode = SERVER_ERR_IN_SHUTTINGDOWN;
 59         (*pBundle) << retcode;
 60         (*pBundle).appendBlob(retdatas);
 61         pChannel->send(pBundle);
 62         return false;
 63     }
 64
 65     PendingLoginMgr::PLInfos* ptinfos = pendingCreateMgr_.find(const_cast<std::string&>(accountName));
 66     if(ptinfos != NULL)
 67     {
 68         WARNING_MSG(fmt::format("Loginapp::_createAccount: pendingCreateMgr has {}, request create failed!\n",
 69             accountName));
 70
 71         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
 72         (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
 73         SERVER_ERROR_CODE retcode = SERVER_ERR_BUSY;
 74         (*pBundle) << retcode;
 75         (*pBundle).appendBlob(retdatas);
 76         pChannel->send(pBundle);
 77         return false;
 78     }
 79
 80     {
 81         // 把请求交由脚本处理
 82         SERVER_ERROR_CODE retcode = SERVER_SUCCESS;
 83         SCOPED_PROFILE(SCRIPTCALL_PROFILE);
 84
 85         PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
 86                                             const_cast<char*>("onRequestCreateAccount"),
 87                                             const_cast<char*>("ssy#"),
 88                                             accountName.c_str(),
 89                                             password.c_str(),
 90                                             datas.c_str(), datas.length());
 91
 92         if(pyResult != NULL)
 93         {
 94             if(PySequence_Check(pyResult) && PySequence_Size(pyResult) == 4)
 95             {
 96                 char* sname;
 97                 char* spassword;
 98                 char *extraDatas;
 99                 Py_ssize_t extraDatas_size = 0;
100
101                 if(PyArg_ParseTuple(pyResult, "H|s|s|y#",  &retcode, &sname, &spassword, &extraDatas, &extraDatas_size) == -1)
102                 {
103                     ERROR_MSG(fmt::format("Loginapp::_createAccount: {}.onReuqestLogin, Return value error! accountName={}\n",
104                         g_kbeSrvConfig.getLoginApp().entryScriptFile, accountName));
105
106                     retcode = SERVER_ERR_OP_FAILED;
107                 }
108                 else
109                 {
110                     accountName = sname;
111                     password = spassword;
112
113                     if (extraDatas && extraDatas_size > 0)
114                         datas.assign(extraDatas, extraDatas_size);
115                     else
116                         SCRIPT_ERROR_CHECK();
117                 }
118             }
119             else
120             {
121                 ERROR_MSG(fmt::format("Loginapp::_createAccount: {}.onReuqestLogin, Return value error, must be errorcode or tuple! accountName={}\n",
122                     g_kbeSrvConfig.getLoginApp().entryScriptFile, accountName));
123
124                 retcode = SERVER_ERR_OP_FAILED;
125             }
126
127             Py_DECREF(pyResult);
128         }
129         else
130         {
131             SCRIPT_ERROR_CHECK();
132             retcode = SERVER_ERR_OP_FAILED;
133         }
134
135         if(retcode != SERVER_SUCCESS)
136         {
137             Network::Bundle* pBundle = Network::Bundle::createPoolObject();
138             (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
139             (*pBundle) << retcode;
140             (*pBundle).appendBlob(retdatas);
141             pChannel->send(pBundle);
142             return false;
143         }
144         else
145         {
146             if(accountName.size() == 0)
147             {
148                 ERROR_MSG(fmt::format("Loginapp::_createAccount: accountName is empty!\n"));
149
150                 retcode = SERVER_ERR_NAME;
151                 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
152                 (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
153                 (*pBundle) << retcode;
154                 (*pBundle).appendBlob(retdatas);
155                 pChannel->send(pBundle);
156                 return false;
157             }
158         }
159     }
160
161     if(type == ACCOUNT_TYPE_SMART)
162     {
163         if (email_isvalid(accountName.c_str()))
164         {
165             type = ACCOUNT_TYPE_MAIL;
166         }
167         else
168         {
169             if(!validName(accountName))
170             {
171                 ERROR_MSG(fmt::format("Loginapp::_createAccount: invalid accountName({})\n",
172                     accountName));
173
174                 Network::Bundle* pBundle = Network::Bundle::createPoolObject();
175                 (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
176                 SERVER_ERROR_CODE retcode = SERVER_ERR_NAME;
177                 (*pBundle) << retcode;
178                 (*pBundle).appendBlob(retdatas);
179                 pChannel->send(pBundle);
180                 return false;
181             }
182
183             type = ACCOUNT_TYPE_NORMAL;
184         }
185     }
186     else if(type == ACCOUNT_TYPE_NORMAL)
187     {
188         if(!validName(accountName))
189         {
190             ERROR_MSG(fmt::format("Loginapp::_createAccount: invalid accountName({})\n",
191                 accountName));
192
193             Network::Bundle* pBundle = Network::Bundle::createPoolObject();
194             (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
195             SERVER_ERROR_CODE retcode = SERVER_ERR_NAME;
196             (*pBundle) << retcode;
197             (*pBundle).appendBlob(retdatas);
198             pChannel->send(pBundle);
199             return false;
200         }
201     }
202     else if (!email_isvalid(accountName.c_str()))
203     {
204         /*
205         std::string user_name, domain_name;
206         user_name = regex_replace(accountName, _g_mail_pattern, std::string("$1") );
207         domain_name = regex_replace(accountName, _g_mail_pattern, std::string("$2") );
208         */
209         WARNING_MSG(fmt::format("Loginapp::_createAccount: invalid mail={}\n",
210             accountName));
211
212         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
213         (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
214         SERVER_ERROR_CODE retcode = SERVER_ERR_NAME_MAIL;
215         (*pBundle) << retcode;
216         (*pBundle).appendBlob(retdatas);
217         pChannel->send(pBundle);
218         return false;
219     }
220
221     DEBUG_MSG(fmt::format("Loginapp::_createAccount: accountName={}, passwordsize={}, type={}, oldType={}.\n",
222         accountName.c_str(), password.size(), type, oldType));
223
224     ptinfos = new PendingLoginMgr::PLInfos;
225     ptinfos->accountName = accountName;
226     ptinfos->password = password;
227     ptinfos->datas = datas;
228     ptinfos->addr = pChannel->addr();
229     pendingCreateMgr_.add(ptinfos);
230
231     Components::COMPONENTS& cts = Components::getSingleton().getComponents(DBMGR_TYPE);
232     Components::ComponentInfos* dbmgrinfos = NULL;
233
234     if(cts.size() > 0)
235         dbmgrinfos = &(*cts.begin());
236
237     if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == 0)
238     {
239         ERROR_MSG(fmt::format("Loginapp::_createAccount: create({}), not found dbmgr!\n",
240             accountName));
241
242         Network::Bundle* pBundle = Network::Bundle::createPoolObject();
243         (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
244         SERVER_ERROR_CODE retcode = SERVER_ERR_SRV_NO_READY;
245         (*pBundle) << retcode;
246         (*pBundle).appendBlob(retdatas);
247         pChannel->send(pBundle);
248         return false;
249     }
250
251     pChannel->extra(accountName);
252
253     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
254     (*pBundle).newMessage(DbmgrInterface::reqCreateAccount);
255     uint8 uatype = uint8(type);
256     (*pBundle) << accountName << password << uatype;
257     (*pBundle).appendBlob(datas);
258     dbmgrinfos->pChannel->send(pBundle);
259     return true;
260 }

可以看到,进行了一堆繁琐的验证以后,最后将解析出来的用户名密码等其他数据

我们打开dbmgr,找到reqCreateAccount函数

 1 //-------------------------------------------------------------------------------------
 2 void Dbmgr::reqCreateAccount(Network::Channel* pChannel, KBEngine::MemoryStream& s)
 3 {
 4     std::string registerName, password, datas;
 5     uint8 uatype = 0;
 6
 7     s >> registerName >> password >> uatype;
 8     s.readBlob(datas);
 9
10     if(registerName.size() == 0)
11     {
12         ERROR_MSG("Dbmgr::reqCreateAccount: registerName is empty.\n");
13         return;
14     }
15
16     pInterfacesAccountHandler_->createAccount(pChannel, registerName, password, datas, ACCOUNT_TYPE(uatype));
17     numCreatedAccount_++;
18 }

然后点开createAccount,因为一般情况下配置文件会填写host和port,所以我们进入InterfacesHandler_Dbmgr::createAccount的方法查看

 1 //-------------------------------------------------------------------------------------
 2 bool InterfacesHandler_Dbmgr::createAccount(Network::Channel* pChannel, std::string& registerName,
 3                                           std::string& password, std::string& datas, ACCOUNT_TYPE uatype)
 4 {
 5     std::string dbInterfaceName = Dbmgr::getSingleton().selectAccountDBInterfaceName(registerName);
 6
 7     thread::ThreadPool* pThreadPool = DBUtil::pThreadPool(dbInterfaceName);
 8     if (!pThreadPool)
 9     {
10         ERROR_MSG(fmt::format("InterfacesHandler_Dbmgr::createAccount: not found dbInterface({})!\n",
11             dbInterfaceName));
12
13         return false;
14     }
15
16     // 如果是email,先查询账号是否存在然后将其登记入库
17     if(uatype == ACCOUNT_TYPE_MAIL)
18     {
19         pThreadPool->addTask(new DBTaskCreateMailAccount(pChannel->addr(),
20             registerName, registerName, password, datas, datas));
21
22         return true;
23     }
24
25     pThreadPool->addTask(new DBTaskCreateAccount(pChannel->addr(),
26         registerName, registerName, password, datas, datas));
27
28     return true;
29 }

可以看到,这里是用了一个异步任务队列的形式,进行的数据库写入,点开DBTaskCreateAccount,事实上邮件账号的原理是一样的

我们简单的看下DBTaskCreateAccount这个类,头文件

 1 /**
 2     创建一个账号到数据库
 3 */
 4 class DBTaskCreateAccount : public DBTask
 5 {
 6 public:
 7     DBTaskCreateAccount(const Network::Address& addr, std::string& registerName, std::string& accountName,
 8         std::string& password, std::string& postdatas, std::string& getdatas);
 9     virtual ~DBTaskCreateAccount();
10     virtual bool db_thread_process();
11     virtual thread::TPTask::TPTaskState presentMainThread();
12
13     static bool writeAccount(DBInterface* pdbi, const std::string& accountName,
14         const std::string& passwd, const std::string& datas, ACCOUNT_INFOS& info);
15
16 protected:
17     std::string registerName_;
18     std::string accountName_;
19     std::string password_;
20     std::string postdatas_, getdatas_;
21     bool success_;
22
23 };

CPP文件

  1 //-------------------------------------------------------------------------------------
  2 DBTaskCreateAccount::DBTaskCreateAccount(const Network::Address& addr,
  3                                          std::string& registerName,
  4                                          std::string& accountName,
  5                                          std::string& password,
  6                                          std::string& postdatas,
  7                                         std::string& getdatas):
  8 DBTask(addr),
  9 registerName_(registerName),
 10 accountName_(accountName),
 11 password_(password),
 12 postdatas_(postdatas),
 13 getdatas_(getdatas),
 14 success_(false)
 15 {
 16 }
 17
 18 //-------------------------------------------------------------------------------------
 19 DBTaskCreateAccount::~DBTaskCreateAccount()
 20 {
 21 }
 22
 23 //-------------------------------------------------------------------------------------
 24 bool DBTaskCreateAccount::db_thread_process()
 25 {
 26     ACCOUNT_INFOS info;
 27     success_ = DBTaskCreateAccount::writeAccount(pdbi_, accountName_, password_, postdatas_, info) && info.dbid > 0;
 28     return false;
 29 }
 30
 31 //-------------------------------------------------------------------------------------
 32 bool DBTaskCreateAccount::writeAccount(DBInterface* pdbi, const std::string& accountName,
 33                                        const std::string& passwd, const std::string& datas, ACCOUNT_INFOS& info)
 34 {
 35     info.dbid = 0;
 36     if(accountName.size() == 0)
 37     {
 38         return false;
 39     }
 40
 41     // 寻找dblog是否有此账号, 如果有则创建失败
 42     // 如果没有则向account表新建一个entity数据同时在accountlog表写入一个log关联dbid
 43     EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi->name());
 44     KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
 45     KBE_ASSERT(pTable);
 46
 47     ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
 48     if(pModule == NULL)
 49     {
 50         ERROR_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): not found account script[{}], create[{}] error!\n",
 51             DBUtil::accountScriptName(), accountName));
 52
 53         return false;
 54     }
 55
 56     if(pTable->queryAccount(pdbi, accountName, info) && (info.flags & ACCOUNT_FLAG_NOT_ACTIVATED) <= 0)
 57     {
 58         if(pdbi->getlasterror() > 0)
 59         {
 60             WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): queryAccount error: {}\n",
 61                 pdbi->getstrerror()));
 62         }
 63
 64         return false;
 65     }
 66
 67     bool hasset = (info.dbid != 0);
 68     if(!hasset)
 69     {
 70         info.flags = g_kbeSrvConfig.getDBMgr().accountDefaultFlags;
 71         info.deadline = g_kbeSrvConfig.getDBMgr().accountDefaultDeadline;
 72     }
 73
 74     DBID entityDBID = info.dbid;
 75
 76     if(entityDBID == 0)
 77     {
 78         // 防止多线程问题, 这里做一个拷贝。
 79         MemoryStream copyAccountDefMemoryStream(pTable->accountDefMemoryStream());
 80
 81         entityDBID = EntityTables::findByInterfaceName(pdbi->name()).writeEntity(pdbi, 0, -1,
 82                 &copyAccountDefMemoryStream, pModule);
 83     }
 84
 85     KBE_ASSERT(entityDBID > 0);
 86
 87     info.name = accountName;
 88     info.email = accountName + "@0.0";
 89     info.password = passwd;
 90     info.dbid = entityDBID;
 91     info.datas = datas;
 92
 93     if(!hasset)
 94     {
 95         if(!pTable->logAccount(pdbi, info))
 96         {
 97             if(pdbi->getlasterror() > 0)
 98             {
 99                 WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): logAccount error:{}\n",
100                     pdbi->getstrerror()));
101             }
102
103             return false;
104         }
105     }
106     else
107     {
108         if(!pTable->setFlagsDeadline(pdbi, accountName, info.flags & ~ACCOUNT_FLAG_NOT_ACTIVATED, info.deadline))
109         {
110             if(pdbi->getlasterror() > 0)
111             {
112                 WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): logAccount error:{}\n",
113                     pdbi->getstrerror()));
114             }
115
116             return false;
117         }
118     }
119
120     return true;
121 }
122
123 //-------------------------------------------------------------------------------------
124 thread::TPTask::TPTaskState DBTaskCreateAccount::presentMainThread()
125 {
126     DEBUG_MSG(fmt::format("Dbmgr::reqCreateAccount: {}.\n", registerName_.c_str()));
127
128     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
129     (*pBundle).newMessage(LoginappInterface::onReqCreateAccountResult);
130     SERVER_ERROR_CODE failedcode = SERVER_SUCCESS;
131
132     if(!success_)
133         failedcode = SERVER_ERR_ACCOUNT_CREATE_FAILED;
134
135     (*pBundle) << failedcode << registerName_ << password_;
136     (*pBundle).appendBlob(getdatas_);
137
138     if(!this->send(pBundle))
139     {
140         ERROR_MSG(fmt::format("DBTaskCreateAccount::presentMainThread: channel({}) not found.\n", addr_.c_str()));
141         Network::Bundle::reclaimPoolObject(pBundle);
142     }
143
144     return thread::TPTask::TPTASK_STATE_COMPLETED;
145 }

这个类里面,最重要的函数是 virtual bool db_thread_process();,DBTaskCreateAccount继承自DBTask,DBTask继承自DBTaskBase,在DBTaskBase中有一个process函数,会调用db_thread_process(),然后记录执行所花的时间,而DBTaskBase继承自TPTask,TPTask又是Task的子类,众多Task组成了一个队列Tasks,Tasks是一个任务队列,不停地调用子类的process方法,接收外部任务的入队请求,并且自动的处理任务。因为嵌套太深,这里就不详细列了,但是代码写的真的很优秀,推荐代码控们去看一下。如果C++基础不深看不懂也没关系,反正记住结论,db_thread_process,是子类真正做事情的地方。

并且,这个类里,presentMainThread这个函数,是持久化执行完的回调调用,在这里持久化结束以后调用的函数就是onReqCreateAccountResult

回归正题,db_thread_process执行到logAccount,这个函数进行了具体的写入。根据数据库类型的不一样,这里具体调用的方法也不一样,我们看mysql的

 1 //-------------------------------------------------------------------------------------
 2 bool KBEAccountTableMysql::logAccount(DBInterface * pdbi, ACCOUNT_INFOS& info)
 3 {
 4     std::string sqlstr = "insert into kbe_accountinfos (accountName, password, bindata, email, entityDBID, flags, deadline, regtime, lasttime) values(";
 5
 6     char* tbuf = new char[MAX_BUF > info.datas.size() ? MAX_BUF * 3 : info.datas.size() * 3];
 7
 8     mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
 9         tbuf, info.name.c_str(), info.name.size());
10
11     sqlstr += "\"";
12     sqlstr += tbuf;
13     sqlstr += "\",";
14
15     mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
16         tbuf, info.password.c_str(), info.password.size());
17
18     sqlstr += "md5(\"";
19     sqlstr += tbuf;
20     sqlstr += "\"),";
21
22     mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
23         tbuf, info.datas.data(), info.datas.size());
24
25     sqlstr += "\"";
26     sqlstr += tbuf;
27     sqlstr += "\",";
28
29     mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
30         tbuf, info.email.c_str(), info.email.size());
31
32     sqlstr += "\"";
33     sqlstr += tbuf;
34     sqlstr += "\",";
35
36     kbe_snprintf(tbuf, MAX_BUF, "%" PRDBID, info.dbid);
37     sqlstr += tbuf;
38     sqlstr += ",";
39
40     kbe_snprintf(tbuf, MAX_BUF, "%u", info.flags);
41     sqlstr += tbuf;
42     sqlstr += ",";
43
44     kbe_snprintf(tbuf, MAX_BUF, "%" PRIu64, info.deadline);
45     sqlstr += tbuf;
46     sqlstr += ",";
47
48     kbe_snprintf(tbuf, MAX_BUF, "%" PRTime, time(NULL));
49     sqlstr += tbuf;
50     sqlstr += ",";
51
52     kbe_snprintf(tbuf, MAX_BUF, "%" PRTime, time(NULL));
53     sqlstr += tbuf;
54     sqlstr += ")";
55
56     SAFE_RELEASE_ARRAY(tbuf);
57
58     // 如果查询失败则返回存在, 避免可能产生的错误
59     if(!pdbi->query(sqlstr.c_str(), sqlstr.size(), false))
60     {
61         ERROR_MSG(fmt::format("KBEAccountTableMysql::logAccount({}): sql({}) is failed({})!\n",
62                 info.name, sqlstr, pdbi->getstrerror()));
63
64         return false;
65     }
66
67     return true;
68 }

至此,注册流程持久化部分完成,回头继续我们的注册流程,查看持久化处理完之后的回调onReqCreateAccountResult

 1 //-------------------------------------------------------------------------------------
 2 void Loginapp::onReqCreateAccountResult(Network::Channel* pChannel, MemoryStream& s)
 3 {
 4     SERVER_ERROR_CODE failedcode;
 5     std::string accountName;
 6     std::string password;
 7     std::string retdatas = "";
 8
 9     s >> failedcode >> accountName >> password;
10     s.readBlob(retdatas);
11
12     // 把请求交由脚本处理
13     SCOPED_PROFILE(SCRIPTCALL_PROFILE);
14     PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
15                                         const_cast<char*>("onCreateAccountCallbackFromDB"),
16                                         const_cast<char*>("sHy#"),
17                                         accountName.c_str(),
18                                         failedcode,
19                                         retdatas.c_str(), retdatas.length());
20
21     if(pyResult != NULL)
22     {
23         Py_DECREF(pyResult);
24     }
25     else
26     {
27         SCRIPT_ERROR_CHECK();
28     }
29
30     DEBUG_MSG(fmt::format("Loginapp::onReqCreateAccountResult: accountName={}, failedcode={}.\n",
31         accountName.c_str(), failedcode));
32
33     PendingLoginMgr::PLInfos* ptinfos = pendingCreateMgr_.remove(accountName);
34     if(ptinfos == NULL)
35         return;
36
37     Network::Channel* pClientChannel = this->networkInterface().findChannel(ptinfos->addr);
38     if(pClientChannel == NULL)
39         return;
40
41     pClientChannel->extra("");
42
43     Network::Bundle* pBundle = Network::Bundle::createPoolObject();
44     (*pBundle).newMessage(ClientInterface::onCreateAccountResult);
45     (*pBundle) << failedcode;
46     (*pBundle).appendBlob(retdatas);
47
48     pClientChannel->send(pBundle);
49     SAFE_RELEASE(ptinfos);
50 }

,服务器端给客户端发消息了,我们来看客户端怎么处理的

 1         /*
 2             账号创建返回结果
 3         */
 4         public void Client_onCreateAccountResult(MemoryStream stream)
 5         {
 6             UInt16 retcode = stream.readUint16();
 7             byte[] datas = stream.readBlob();
 8
 9             Event.fireOut("onCreateAccountResult", new object[]{retcode, datas});
10
11             if(retcode != 0)
12             {
13                 Dbg.WARNING_MSG("KBEngine::Client_onCreateAccountResult: " + username + " create is failed! code=" + retcode + "!");
14                 return;
15             }
16
17             Dbg.DEBUG_MSG("KBEngine::Client_onCreateAccountResult: " + username + " create is successfully!");
18         }

至此,注册流程完毕。

事实上,KBE大部分的系统消息流程不会这么麻烦,在python层很简单的几行代码就完成一个系统。只不过因为KBE注册登录是C++内嵌代码的原因,所以才会格外复杂。对于对引擎内部机制不关心的人来说,这篇文章完全可以不看。也不会影响工作的效率和速度,必定基本上所有的代码都是python来写的。

我之所以写这篇文章,也是希望通过写这篇文章,让自己对KBE引擎底层的逻辑处理有一个系统的了解和记录。作为一个服务器主程,不能底层一点也改不了,这是我的初衷。

登录流程比注册流程要简单很多,可以仿照本文的阅读流程读一遍。

留一点练习题吧,假如我们需要在账号信息中中新加一个字段,设备唯一标识码,应该怎么做?

我是青岛远硕信息科技发展有限公司的Peter,如果转载的话,请保留这段文字。

转载于:https://www.cnblogs.com/lsm19870508/p/6905535.html

KBEngine warring项目源码阅读(一) 项目简介和注册相关推荐

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

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

  2. 健身android源码,Android项目源码运动健身项目

    [实例简介] 本项目是健身项目Android应用,本软件包含健身项目和计时运动,更多的了解运动前和运动后需要注意的事项和禁忌,也包含了许多健康的健身习惯的文档,应该说这个应用太强大了,用到技术,项目中 ...

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

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

  4. ExcelToHtmlTable转换算法:将Excel转换成Html表格并展示(项目源码+详细注释+项目截图)...

    功能概述 Excel2HtmlTable的主要功能就是把Excel的内容以表格的方式,展现在页面中. Excel的多个Sheet对应页面的多个Tab选项卡. 转换算法的难点在于,如何处理行列合并,将E ...

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

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

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

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

  7. openedge-function模块浅析——百度BIE边缘侧openedge项目源码阅读(3)

    前言 中断了一段时间,发现前面分析的hub模块的源码拉错分枝了(对,我就是个菜鸡),不过大致流程差不多,有时间改一下.这次分析openedge-function模块,openedge-function ...

  8. 安卓实训项目源码_综合性项目:在线学习考试系统

    此项目是本人耗费大量的业余时间独立开发,制作该开源项目的原因: 巩固Java8.Spring Boot.MyBatis等后端方面的知识 巩固HTML.JavaScript.Vue.ElementUI等 ...

  9. 基于java开发的健身器材电商管理系统.rar(含项目源码前后端项目)

    功能:健身器材信息管理.健身器材分类管理.公告管理.个人中心.订单管理.健身器材入库管理.健身器材出库管理.收获评价管理.系统管理等 前端页面展示 后端管理: 源码下载地址:>>>

最新文章

  1. Activity Service 数据相互操作
  2. response返回一段html,iframe调用后台方法通过response返回html代码
  3. Spring进行表单验证
  4. rabbitmq 同步策略_RabbitMQ(三):消息持久化策略
  5. unity 开发总结
  6. 微软面试题目(一) 计算两个日期之间的天数
  7. markdown mysql高亮_修改博客园markdown编辑器代码高亮风格的方法
  8. win10电脑黑屏只有鼠标箭头_win7开机黑屏只有鼠标怎么办,我来教你解决
  9. win7下装ubuntu14.04双系统
  10. 观点对立,无关虚拟现实技术
  11. db2与mysql语法区别_db2和mysql语法的区别是什么
  12. VMware安装VMware tools步骤
  13. 最新的紫猫编程学院从零开始学脚本值得学习吗
  14. 必收藏的九大塑料注塑成型技术及其特点
  15. 解决微信公众号注册提示“邮箱已被占用”(亲测)
  16. Ubuntu 20.04 上安装使用 ibus-rime(超实用)
  17. 银行招聘计算机考试时间,五大银行招聘考试时间是什么时候?
  18. 乐山市计算机学校灵异事件,我也来说个以前上学时候的灵异事件
  19. RobotStudio 项目移植问题——无法创建系统
  20. 【全开源+免费更新】doodoo.js创建项目教程

热门文章

  1. CAD/CAM 软件架构总结
  2. 博升机器人_乐博乐博机器人
  3. ffmpeg 从现有视频中截取一段
  4. 对于GISer身份的思考
  5. 亿道丨三防平板丨加固平板丨三防工业平板丨航空航天应用
  6. python s append_Python语句序列s=[1,2,3,4];s.append([5,6]);print(len(s))的运行结果是______。...
  7. 录屏软件电脑版免费哪个好?4款免费屏幕录制软件下载
  8. Windows 当前所接的屏幕检测
  9. 不错的软件测试学习网站
  10. iOS直播(流媒体)基础原理篇