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

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

项目截图:

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

注册:

流程图:

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

 void reg_ok()
{
log_label.obj.text = "请求连接服务器...";
log_label.obj.color = UnityEngine.Color.green; if(reg_username.input.text == "" || reg_username.input.text.Length > )
{
log_label.obj.color = UnityEngine.Color.red;
log_label.obj.text = "用户名或者邮箱地址不合法, 最大长度限制30个字符。";
Common.WARNING_MSG("ui::reg_ok: invalid username!");
return;
} if(reg_password.input.text.Length < || reg_password.input.text.Length > )
{
log_label.obj.color = UnityEngine.Color.red;
log_label.obj.text = "密码不合法, 长度限制在6~16位之间。";
Common.WARNING_MSG("ui::reg_ok: invalid reg_password!");
return;
} if(reg_password.input.text != reg_passwordok.input.text)
{
log_label.obj.color = UnityEngine.Color.red;
log_label.obj.text = "二次输入密码不匹配。";
Common.WARNING_MSG("ui::reg_ok: reg_password != reg_passwordok!");
return;
} KBEngine.Event.fireIn("createAccount", reg_username.input.text, reg_passwordok.input.text, System.Text.Encoding.UTF8.GetBytes("kbengine_unity_warring"));
log_label.obj.text = "连接成功,等待处理请稍后...";
}

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

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

         void installEvents()
{
Event.registerIn("createAccount", this, "createAccount");
Event.registerIn("login", this, "login");
Event.registerIn("reloginBaseapp", this, "reloginBaseapp");
Event.registerIn("resetPassword", this, "resetPassword");
Event.registerIn("bindAccountEmail", this, "bindAccountEmail");
Event.registerIn("newPassword", this, "newPassword"); // 内部事件
Event.registerIn("_closeNetwork", this, "_closeNetwork");
}

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

点开进去

         /*
创建账号,通过loginapp
*/
public void createAccount_loginapp(bool noconnect)
{
if(noconnect)
{
reset();
_networkInterface.connectTo(_args.ip, _args.port, onConnectTo_createAccount_callback, null);
}
else
{
Bundle bundle = Bundle.createObject();
bundle.newMessage(Message.messages["Loginapp_reqCreateAccount"]);
bundle.writeString(username);
bundle.writeString(password);
//string imei = 'AET89766-124';
//bundle.writeString(imei);
bundle.writeBlob(KBEngineApp.app._clientdatas);
bundle.send(_networkInterface);
}
}

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

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

 //-------------------------------------------------------------------------------------
void Loginapp::reqCreateAccount(Network::Channel* pChannel, MemoryStream& s)
{
std::string accountName, password, datas; s >> accountName >> password;
s.readBlob(datas); if(!_createAccount(pChannel, accountName, password, datas, ACCOUNT_TYPE(g_serverConfig.getLoginApp().account_type)))
return;
}

点开_createAccount

 //-------------------------------------------------------------------------------------
bool Loginapp::_createAccount(Network::Channel* pChannel, std::string& accountName,
std::string& password, std::string& datas, ACCOUNT_TYPE type)
{
AUTO_SCOPED_PROFILE("createAccount"); ACCOUNT_TYPE oldType = type; if(!g_kbeSrvConfig.getDBMgr().account_registration_enable)
{
ERROR_MSG(fmt::format("Loginapp::_createAccount({}): not available! modify kbengine[_defs].xml->dbmgr->account_registration.\n",
accountName)); std::string retdatas = "";
Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_ACCOUNT_REGISTER_NOT_AVAILABLE;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} accountName = KBEngine::strutil::kbe_trim(accountName);
password = KBEngine::strutil::kbe_trim(password); if(accountName.size() > ACCOUNT_NAME_MAX_LENGTH)
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: accountName too big, size={}, limit={}.\n",
accountName.size(), ACCOUNT_NAME_MAX_LENGTH)); return false;
} if(password.size() > ACCOUNT_PASSWD_MAX_LENGTH)
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: password too big, size={}, limit={}.\n",
password.size(), ACCOUNT_PASSWD_MAX_LENGTH)); return false;
} if(datas.size() > ACCOUNT_DATA_MAX_LENGTH)
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: bindatas too big, size={}, limit={}.\n",
datas.size(), ACCOUNT_DATA_MAX_LENGTH)); return false;
} std::string retdatas = "";
if(shuttingdown_ != SHUTDOWN_STATE_STOP)
{
WARNING_MSG(fmt::format("Loginapp::_createAccount: shutting down, create {} failed!\n", accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_IN_SHUTTINGDOWN;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} PendingLoginMgr::PLInfos* ptinfos = pendingCreateMgr_.find(const_cast<std::string&>(accountName));
if(ptinfos != NULL)
{
WARNING_MSG(fmt::format("Loginapp::_createAccount: pendingCreateMgr has {}, request create failed!\n",
accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_BUSY;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} {
// 把请求交由脚本处理
SERVER_ERROR_CODE retcode = SERVER_SUCCESS;
SCOPED_PROFILE(SCRIPTCALL_PROFILE); PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
const_cast<char*>("onRequestCreateAccount"),
const_cast<char*>("ssy#"),
accountName.c_str(),
password.c_str(),
datas.c_str(), datas.length()); if(pyResult != NULL)
{
if(PySequence_Check(pyResult) && PySequence_Size(pyResult) == )
{
char* sname;
char* spassword;
char *extraDatas;
Py_ssize_t extraDatas_size = ; if(PyArg_ParseTuple(pyResult, "H|s|s|y#", &retcode, &sname, &spassword, &extraDatas, &extraDatas_size) == -)
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: {}.onReuqestLogin, Return value error! accountName={}\n",
g_kbeSrvConfig.getLoginApp().entryScriptFile, accountName)); retcode = SERVER_ERR_OP_FAILED;
}
else
{
accountName = sname;
password = spassword; if (extraDatas && extraDatas_size > )
datas.assign(extraDatas, extraDatas_size);
else
SCRIPT_ERROR_CHECK();
}
}
else
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: {}.onReuqestLogin, Return value error, must be errorcode or tuple! accountName={}\n",
g_kbeSrvConfig.getLoginApp().entryScriptFile, accountName)); retcode = SERVER_ERR_OP_FAILED;
} Py_DECREF(pyResult);
}
else
{
SCRIPT_ERROR_CHECK();
retcode = SERVER_ERR_OP_FAILED;
} if(retcode != SERVER_SUCCESS)
{
Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
}
else
{
if(accountName.size() == )
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: accountName is empty!\n")); retcode = SERVER_ERR_NAME;
Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
}
}
} if(type == ACCOUNT_TYPE_SMART)
{
if (email_isvalid(accountName.c_str()))
{
type = ACCOUNT_TYPE_MAIL;
}
else
{
if(!validName(accountName))
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: invalid accountName({})\n",
accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_NAME;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} type = ACCOUNT_TYPE_NORMAL;
}
}
else if(type == ACCOUNT_TYPE_NORMAL)
{
if(!validName(accountName))
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: invalid accountName({})\n",
accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_NAME;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
}
}
else if (!email_isvalid(accountName.c_str()))
{
/*
std::string user_name, domain_name;
user_name = regex_replace(accountName, _g_mail_pattern, std::string("$1") );
domain_name = regex_replace(accountName, _g_mail_pattern, std::string("$2") );
*/
WARNING_MSG(fmt::format("Loginapp::_createAccount: invalid mail={}\n",
accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_NAME_MAIL;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} DEBUG_MSG(fmt::format("Loginapp::_createAccount: accountName={}, passwordsize={}, type={}, oldType={}.\n",
accountName.c_str(), password.size(), type, oldType)); ptinfos = new PendingLoginMgr::PLInfos;
ptinfos->accountName = accountName;
ptinfos->password = password;
ptinfos->datas = datas;
ptinfos->addr = pChannel->addr();
pendingCreateMgr_.add(ptinfos); Components::COMPONENTS& cts = Components::getSingleton().getComponents(DBMGR_TYPE);
Components::ComponentInfos* dbmgrinfos = NULL; if(cts.size() > )
dbmgrinfos = &(*cts.begin()); if(dbmgrinfos == NULL || dbmgrinfos->pChannel == NULL || dbmgrinfos->cid == )
{
ERROR_MSG(fmt::format("Loginapp::_createAccount: create({}), not found dbmgr!\n",
accountName)); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
SERVER_ERROR_CODE retcode = SERVER_ERR_SRV_NO_READY;
(*pBundle) << retcode;
(*pBundle).appendBlob(retdatas);
pChannel->send(pBundle);
return false;
} pChannel->extra(accountName); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(DbmgrInterface::reqCreateAccount);
uint8 uatype = uint8(type);
(*pBundle) << accountName << password << uatype;
(*pBundle).appendBlob(datas);
dbmgrinfos->pChannel->send(pBundle);
return true;
}

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

我们打开dbmgr,找到reqCreateAccount函数

 //-------------------------------------------------------------------------------------
void Dbmgr::reqCreateAccount(Network::Channel* pChannel, KBEngine::MemoryStream& s)
{
std::string registerName, password, datas;
uint8 uatype = ; s >> registerName >> password >> uatype;
s.readBlob(datas); if(registerName.size() == )
{
ERROR_MSG("Dbmgr::reqCreateAccount: registerName is empty.\n");
return;
} pInterfacesAccountHandler_->createAccount(pChannel, registerName, password, datas, ACCOUNT_TYPE(uatype));
numCreatedAccount_++;
}

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

 //-------------------------------------------------------------------------------------
bool InterfacesHandler_Dbmgr::createAccount(Network::Channel* pChannel, std::string& registerName,
std::string& password, std::string& datas, ACCOUNT_TYPE uatype)
{
std::string dbInterfaceName = Dbmgr::getSingleton().selectAccountDBInterfaceName(registerName); thread::ThreadPool* pThreadPool = DBUtil::pThreadPool(dbInterfaceName);
if (!pThreadPool)
{
ERROR_MSG(fmt::format("InterfacesHandler_Dbmgr::createAccount: not found dbInterface({})!\n",
dbInterfaceName)); return false;
} // 如果是email,先查询账号是否存在然后将其登记入库
if(uatype == ACCOUNT_TYPE_MAIL)
{
pThreadPool->addTask(new DBTaskCreateMailAccount(pChannel->addr(),
registerName, registerName, password, datas, datas)); return true;
} pThreadPool->addTask(new DBTaskCreateAccount(pChannel->addr(),
registerName, registerName, password, datas, datas)); return true;
}

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

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

 /**
创建一个账号到数据库
*/
class DBTaskCreateAccount : public DBTask
{
public:
DBTaskCreateAccount(const Network::Address& addr, std::string& registerName, std::string& accountName,
std::string& password, std::string& postdatas, std::string& getdatas);
virtual ~DBTaskCreateAccount();
virtual bool db_thread_process();
virtual thread::TPTask::TPTaskState presentMainThread(); static bool writeAccount(DBInterface* pdbi, const std::string& accountName,
const std::string& passwd, const std::string& datas, ACCOUNT_INFOS& info); protected:
std::string registerName_;
std::string accountName_;
std::string password_;
std::string postdatas_, getdatas_;
bool success_; };

CPP文件

 //-------------------------------------------------------------------------------------
DBTaskCreateAccount::DBTaskCreateAccount(const Network::Address& addr,
std::string& registerName,
std::string& accountName,
std::string& password,
std::string& postdatas,
std::string& getdatas):
DBTask(addr),
registerName_(registerName),
accountName_(accountName),
password_(password),
postdatas_(postdatas),
getdatas_(getdatas),
success_(false)
{
} //-------------------------------------------------------------------------------------
DBTaskCreateAccount::~DBTaskCreateAccount()
{
} //-------------------------------------------------------------------------------------
bool DBTaskCreateAccount::db_thread_process()
{
ACCOUNT_INFOS info;
success_ = DBTaskCreateAccount::writeAccount(pdbi_, accountName_, password_, postdatas_, info) && info.dbid > ;
return false;
} //-------------------------------------------------------------------------------------
bool DBTaskCreateAccount::writeAccount(DBInterface* pdbi, const std::string& accountName,
const std::string& passwd, const std::string& datas, ACCOUNT_INFOS& info)
{
info.dbid = ;
if(accountName.size() == )
{
return false;
} // 寻找dblog是否有此账号, 如果有则创建失败
// 如果没有则向account表新建一个entity数据同时在accountlog表写入一个log关联dbid
EntityTables& entityTables = EntityTables::findByInterfaceName(pdbi->name());
KBEAccountTable* pTable = static_cast<KBEAccountTable*>(entityTables.findKBETable("kbe_accountinfos"));
KBE_ASSERT(pTable); ScriptDefModule* pModule = EntityDef::findScriptModule(DBUtil::accountScriptName());
if(pModule == NULL)
{
ERROR_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): not found account script[{}], create[{}] error!\n",
DBUtil::accountScriptName(), accountName)); return false;
} if(pTable->queryAccount(pdbi, accountName, info) && (info.flags & ACCOUNT_FLAG_NOT_ACTIVATED) <= )
{
if(pdbi->getlasterror() > )
{
WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): queryAccount error: {}\n",
pdbi->getstrerror()));
} return false;
} bool hasset = (info.dbid != );
if(!hasset)
{
info.flags = g_kbeSrvConfig.getDBMgr().accountDefaultFlags;
info.deadline = g_kbeSrvConfig.getDBMgr().accountDefaultDeadline;
} DBID entityDBID = info.dbid; if(entityDBID == )
{
// 防止多线程问题, 这里做一个拷贝。
MemoryStream copyAccountDefMemoryStream(pTable->accountDefMemoryStream()); entityDBID = EntityTables::findByInterfaceName(pdbi->name()).writeEntity(pdbi, , -,
&copyAccountDefMemoryStream, pModule);
} KBE_ASSERT(entityDBID > ); info.name = accountName;
info.email = accountName + "@0.0";
info.password = passwd;
info.dbid = entityDBID;
info.datas = datas; if(!hasset)
{
if(!pTable->logAccount(pdbi, info))
{
if(pdbi->getlasterror() > )
{
WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): logAccount error:{}\n",
pdbi->getstrerror()));
} return false;
}
}
else
{
if(!pTable->setFlagsDeadline(pdbi, accountName, info.flags & ~ACCOUNT_FLAG_NOT_ACTIVATED, info.deadline))
{
if(pdbi->getlasterror() > )
{
WARNING_MSG(fmt::format("DBTaskCreateAccount::writeAccount(): logAccount error:{}\n",
pdbi->getstrerror()));
} return false;
}
} return true;
} //-------------------------------------------------------------------------------------
thread::TPTask::TPTaskState DBTaskCreateAccount::presentMainThread()
{
DEBUG_MSG(fmt::format("Dbmgr::reqCreateAccount: {}.\n", registerName_.c_str())); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(LoginappInterface::onReqCreateAccountResult);
SERVER_ERROR_CODE failedcode = SERVER_SUCCESS; if(!success_)
failedcode = SERVER_ERR_ACCOUNT_CREATE_FAILED; (*pBundle) << failedcode << registerName_ << password_;
(*pBundle).appendBlob(getdatas_); if(!this->send(pBundle))
{
ERROR_MSG(fmt::format("DBTaskCreateAccount::presentMainThread: channel({}) not found.\n", addr_.c_str()));
Network::Bundle::reclaimPoolObject(pBundle);
} return thread::TPTask::TPTASK_STATE_COMPLETED;
}

这个类里面,最重要的函数是 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的

 //-------------------------------------------------------------------------------------
bool KBEAccountTableMysql::logAccount(DBInterface * pdbi, ACCOUNT_INFOS& info)
{
std::string sqlstr = "insert into kbe_accountinfos (accountName, password, bindata, email, entityDBID, flags, deadline, regtime, lasttime) values("; char* tbuf = new char[MAX_BUF > info.datas.size() ? MAX_BUF * : info.datas.size() * ]; mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
tbuf, info.name.c_str(), info.name.size()); sqlstr += "\"";
sqlstr += tbuf;
sqlstr += "\","; mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
tbuf, info.password.c_str(), info.password.size()); sqlstr += "md5(\"";
sqlstr += tbuf;
sqlstr += "\"),"; mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
tbuf, info.datas.data(), info.datas.size()); sqlstr += "\"";
sqlstr += tbuf;
sqlstr += "\","; mysql_real_escape_string(static_cast<DBInterfaceMysql*>(pdbi)->mysql(),
tbuf, info.email.c_str(), info.email.size()); sqlstr += "\"";
sqlstr += tbuf;
sqlstr += "\","; kbe_snprintf(tbuf, MAX_BUF, "%" PRDBID, info.dbid);
sqlstr += tbuf;
sqlstr += ","; kbe_snprintf(tbuf, MAX_BUF, "%u", info.flags);
sqlstr += tbuf;
sqlstr += ","; kbe_snprintf(tbuf, MAX_BUF, "%" PRIu64, info.deadline);
sqlstr += tbuf;
sqlstr += ","; kbe_snprintf(tbuf, MAX_BUF, "%" PRTime, time(NULL));
sqlstr += tbuf;
sqlstr += ","; kbe_snprintf(tbuf, MAX_BUF, "%" PRTime, time(NULL));
sqlstr += tbuf;
sqlstr += ")"; SAFE_RELEASE_ARRAY(tbuf); // 如果查询失败则返回存在, 避免可能产生的错误
if(!pdbi->query(sqlstr.c_str(), sqlstr.size(), false))
{
ERROR_MSG(fmt::format("KBEAccountTableMysql::logAccount({}): sql({}) is failed({})!\n",
info.name, sqlstr, pdbi->getstrerror())); return false;
} return true;
}

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

 //-------------------------------------------------------------------------------------
void Loginapp::onReqCreateAccountResult(Network::Channel* pChannel, MemoryStream& s)
{
SERVER_ERROR_CODE failedcode;
std::string accountName;
std::string password;
std::string retdatas = ""; s >> failedcode >> accountName >> password;
s.readBlob(retdatas); // 把请求交由脚本处理
SCOPED_PROFILE(SCRIPTCALL_PROFILE);
PyObject* pyResult = PyObject_CallMethod(getEntryScript().get(),
const_cast<char*>("onCreateAccountCallbackFromDB"),
const_cast<char*>("sHy#"),
accountName.c_str(),
failedcode,
retdatas.c_str(), retdatas.length()); if(pyResult != NULL)
{
Py_DECREF(pyResult);
}
else
{
SCRIPT_ERROR_CHECK();
} DEBUG_MSG(fmt::format("Loginapp::onReqCreateAccountResult: accountName={}, failedcode={}.\n",
accountName.c_str(), failedcode)); PendingLoginMgr::PLInfos* ptinfos = pendingCreateMgr_.remove(accountName);
if(ptinfos == NULL)
return; Network::Channel* pClientChannel = this->networkInterface().findChannel(ptinfos->addr);
if(pClientChannel == NULL)
return; pClientChannel->extra(""); Network::Bundle* pBundle = Network::Bundle::createPoolObject();
(*pBundle).newMessage(ClientInterface::onCreateAccountResult);
(*pBundle) << failedcode;
(*pBundle).appendBlob(retdatas); pClientChannel->send(pBundle);
SAFE_RELEASE(ptinfos);
}

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

         /*
账号创建返回结果
*/
public void Client_onCreateAccountResult(MemoryStream stream)
{
UInt16 retcode = stream.readUint16();
byte[] datas = stream.readBlob(); Event.fireOut("onCreateAccountResult", new object[]{retcode, datas}); if(retcode != )
{
Dbg.WARNING_MSG("KBEngine::Client_onCreateAccountResult: " + username + " create is failed! code=" + retcode + "!");
return;
} Dbg.DEBUG_MSG("KBEngine::Client_onCreateAccountResult: " + username + " create is successfully!");
}

至此,注册流程完毕。

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

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

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

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

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

最新文章

  1. Linux安装Go语言
  2. Create a SQL Server Database on a network shared drive
  3. EF之ExecuteSqlCommand更新出现无效的解决方案
  4. [MySQL Reference Manual] 5 MySQL 服务管理
  5. oracle练习题后15个
  6. struts2视频学习笔记 21(输入校验的流程)
  7. 使用DateAdd方法向指定日期添加一段时间间隔,使用TimeSpan对象获取时间间隔
  8. ViewPager 嵌套Listview 让Listview响应 ViewPager 左右滑事件
  9. python import 自己的包
  10. 02.零成本实现WEB性能测试-基于APACHE JMETER
  11. Tree on the level UVa122
  12. Kubernetes容器上下文环境
  13. PAT 1077 Kuchiguse
  14. Nginx 中 FastCGI 配置示例
  15. flask上下文全局变量,程序上下文、请求上下文、上下文钩子
  16. 【链表】Swap Nodes in Pairs(三指针)
  17. JDK5.0特性,使用ProcessBuilder执行本地命令
  18. leetcode16 3-Sum
  19. Ensemble Learning
  20. mysql 中显示 table 的基本信息

热门文章

  1. centos6.5安装dubbo管控台教程(四)
  2. 判断Java对象死亡的两种常用算法
  3. jquery 选择器的总结
  4. 【mysql-索引+存储过程+函数+触发器-更新。。。】
  5. SuperSocket框架中BinaryRequestInfo协议的使用
  6. Reboot
  7. elasticsearch 基础特点
  8. bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
  9. Listener、Filter、Servlet的创建及初始化顺序
  10. Jupyter导出PDF从入门到绝望(已解决)