KBEngine warring项目源码阅读(一) 项目简介和注册
首先介绍下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,如果转载的话,请保留这段文字。