1.简单介绍

2.环境搭建

3.FQA

4.代码执行流程

当服务端成功启动,客户端链接服务端后进入demo中的游戏界面,demo中的功能包括注册、登录、角色管理、战斗、场景等等。

对于新接触kbengine的人,看见客户端的代码后会觉得很迷茫,有些无从下手。(本人unity和c++都是小白,所以更加难以入手)

那么如果想使用kbengine框架做游戏业务逻辑上的扩展,就必须先知道代码的运行顺序,都执行了哪些方法,做了什么工作,以方便我们在基础上做更改和添加新的功能。网上很多大牛在介绍kbengine时候都介绍的很详尽,服务端各模块的介绍都很专业,但可惜技术水平原因,大部分都看不懂,有些甚至找不到,所以我的暂时目的是将框架搭建起,并根据官方的api文档在项目的基础上进行新功能的添加,底层的东西暂时不去理会,只关注python和客户端unity c#的部分。

本人使用的是git上的kbengine_unity3d_demo

下面简单介绍一下 kbengine中的代码结构。

客户端

之前搭建环境时,已经知道这两个文件夹,plugins中是对服务端的对应类(包括网络传输,数据处理,综合逻辑等等),script是自己的逻辑端代码。

Assets/scripts:

服务端:

代码执行流程(以注册为例):

点击createAccount(注册账号)时,首先执行UI.CS (Assets/scripts/u3d_scripts)中的onLonginUI方法。

其中首先判断点击的是登录还是注册按钮,当点击注册按钮时将进入第二个if语块判断信息完善程度,然后执行 createAccount方法:

该方法使用KBEngine.Event.fireIn方法,将触发逻辑端的"createAccount"方法

这里介绍一下kbengine中使用触发条件的个函数( u3d_scripts表现层 kbe_scripts业务逻辑层 )
.KBEngine.Event.fireIn 表现层触发业务逻辑层的方法。
2.KBEngine.Event.fireOut 逻辑层触发服务端的方法。
.Event.registerOut 表现层监听逻辑层的方法。
..Event.registerIn 逻辑层监听服务端的方法。

客户端Kbengine.cs (Assets/Plugins)将接收到前端触发的"createAccount"方法:

并在类内做方法实现:

在方法执行过程中,1的部分表示提交服务器的处理路径,2为提交当前的网络请求。

在1中,Loginapp_resCreateAccount参数,表示,该请求,提交至服务端loginapp,在loginapp.cpp中,有方法reqCreateAccount函数来处理本次请求:

其中_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)
{
WARNING_MSG(fmt::format("Loginapp::_createAccount({}): not available!\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;
}

在方法中,将调用python脚本来做服务端的业务处理逻辑,代码位置于 (\kbengine_demos_assets\scripts\login\kbmain.py)

业务逻辑处理完成后,会调用数据库处理的服务端app(dbmgr)来做相应的数据库操作并返回数据。

至此请求部分执行完成,请求后 服务端会在不同阶段返回相应的状态,在UI.cs中进行返回结果的监听并及时做出处理结果的反应。

kbengine Unity3d demo 代码执行流程(4)的更多相关文章

  1. 第一章 Java代码执行流程

    说明:本文主要参考自<分布式Java应用:基础与实践> 1.Java代码执行流程 第一步:*.java-->*.class(编译期) 第二步:从*.class文件将其中的内容加载到内 ...

  2. debian内核代码执行流程(三)

    接续<debian内核代码执行流程(二)>未完成部分 下面这行输出信息是启动udevd进程产生的输出信息: [ ]: starting version 175是udevd的版本号. 根据& ...

  3. debian内核代码执行流程(二)

    继续上一篇文章<debian内核代码执行流程(一)>未完成部分. acpi_bus_init调用acpi_initialize_objects,经过一系列复杂调用后输出下面信息: [ IN ...

  4. Java 代码执行流程

    Java 代码执行流程 类加载过程 加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载 类加载时机:代码使用到这个类时 验证阶段 &qu ...

  5. Java中异常发生时代码执行流程

    异常与错误: 异常: 在Java中程序的错误主要是语法错误和语义错误,一个程序在编译和运行时出现的错误我们统一称之为异常,它是VM(虚拟机)通知你的一种方式,通过这种方式,VM让你知道,你(开发人员) ...

  6. debian内核代码执行流程(一)

    本文根据debian开机信息来查看内核源代码. 系统使用<debian下配置dynamic printk以及重新编译内核>中内核源码来查看执行流程. 使用dmesg命令,得到下面的开机信息 ...

  7. 当try-catch-finally代码块遇上return,代码执行流程是怎样

    这里打算用一个Java读取文件内容的例子来测试,文件存在,不抛异常,文件不存在,则抛出FileNotFoundException: Java读取文件代码如下: /** * 根据路径和文件名获取内容 * ...

  8. PHP代码执行流程

    怎么样?有点了解了么.说实话,单看这个,我本人是有点懵的,不过,不要怕.咱们来慢慢地看下. 首先,在网上找的信息说PHP代码执行的顺序是这样的,第一步是词法分析,第二步是语法分析,第三步是转化为opc ...

  9. pdfium 代码执行流程

    1.FPDF_InitLibrary(NULL); CPDF_CustomAccess::CPDF_CustomAccess(FPDF_FILEACCESS* pFileAccess) {     i ...

随机推荐

  1. LINUX常用命令-系统配置篇(二)

    学到一定程度了就会关注系统方面的一些配置,只是就需要相关的命令了.现在把Linux查看系统配置常用命令列出来 # uname -a # 查看内核/操作系统/CPU信息# head -n 1 /etc/ ...

  2. ThinkPHP中ajax提交数据

    最近在做项目时遇到了一些需要从页面用ajax提交数据到后台的操作,无奈本人技术有限,网上苦寻,研究了一下ajax和thinkPHP的结合,黄天不负苦心人,终于搞定了. 闲话少叙,进入正题:我需要从页面 ...

  3. JSP中的Attribute和InitParameter

    属性:Attribute类型:应用/上下文,请求,会话(ServletContext,HttpServletRequest/ServletRequest,HttpSession)设置方法:setAtt ...

  4. J2SE知识点摘记(十七)

    1.        Applet Applet的生命周期分为四个阶段,各阶段分别由init,start,stop和destroy四种方法来具体体现. public void init() 此方法通知A ...

  5. ReentrantLock(重入锁)以及公平性

    ReentrantLock(重入锁)以及公平性 标签(空格分隔): java NIO 如果在绝对时间上,先对锁进行获取的请求一定被先满足,那么这个锁是公平的,反之,是不公平的,也就是说等待时间最长的线 ...

  6. FIDO联盟:我们将杀死密码

    前不久发布的三星S5与iPhone 5S一样,配备了指纹识别技术.但更为重要的是,这一识别器可以与PayPal关连,进而与多种支付系统相连.通过这一过程,你很可能会摆脱密码,用指纹就可以畅游网络.当然 ...

  7. Delphi 的接口机制——接口操作的编译器实现过程(2)

    接口对象的内存空间 假设我们定义了如下两个接口 IIntfA 和 IIntfB,其中 ProcA 和 ProcB 将实现为静态方法,而 VirtA 和 VirtB 将以虚方法实现: IIntfA =  ...

  8. delphi中覆盖最大化消息(WM_GETMINMAXINFO)

    unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; ...

  9. iOS 处理键盘遮挡TextField、TextView问题

    之前处理键盘遮挡问题都是在每一个控制器进行单独处理,这样做真的是非常的费事,今天在做项目的时候就想到自己封装一个,记录一下这个“跌宕起伏”的过程. 思路是这样的:计算文本编辑控件Frame与键盘Fra ...

  10. 对于System.Net.Http的学习(一)——System.Net.Http 简介(转)

    最新在学习System.Net.Http的知识,看到有篇文章写的十分详细,就想转过来,自己记录下.原地址是http://www.cnblogs.com/chillsrc/p/3439215.html? ...