KBEngine warring项目源码阅读(一) 项目简介和注册(转)
转自(http://www.cnblogs.com/lsm19870508/p/6905535.html)
首先介绍下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 ©AccountDefMemoryStream, 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,如果转载的话,请保留这段文字。

浙公网安备 33010602011771号