kbengine http是tcp还是udpp

此笔记是本人在开发及研读的过程中记录下来的由于没整理,会看得有些吃力请读者视能力而读,个人理解如有问题,悉心接受部分引用KBEngine官网的一些句段,感谢kbe

下列符号解释 =:继承 ==:等同 +:与上具有子关系

+此区域上的玩家entity:代表玩家 +Witness(对象):监视周围的玩家entity,将发生的事件消息同步给客户端 +AOI(兴趣范围):默认500M +属性数据:只读如果某个属性对于客户端是可见的,那么该属性必须是可以存在 Ghost的例如:当前的武器、等级、名稱 +范围:默认500M,可配置大于等于玩家的AOI +负载平衡:CellApp会告诉它们的Cell的边界应该在哪里 +新建的玩家Entity加入到正确的Cell上 +一个服务器组一个Mgr实例

Machine:監视服务器进程信息,每个服务器机器上有一个machine

  • +作用:启动/停止服务器进程通知服务器群组各个进程的存活状态,监视机器的使用状态:cpu/内存/带宽

ObjectPool:对象池一些对象频繁的被创建,例如:moneystreambundle,tcppacket等等这个对象池通过服务端峰值有效的预估提前创建一些对象缓存起来,在鼡到的时候直接从对象池中提取

SmartPoolObject:智能池对象创建一个智能池对象,与相应的对象池绑定只要此对象析构时,那么会调用绑定的对象池进行回收

Components:组件类记录当前组件信息

ServerApp:App基类,每个app都需继承这个类基本的C++框架模块 都会存在这个类,其中有继承通道超时操作继承通道取消注册操作,

Resmgr:资源类存有一切环境信息,例如bin或者资源目录和一些已打开文件

  • +EndPoint:对端,可以理解成与对端的会话信息及与对端网络的解决方案硬封装 创建socket,绑定监听,recvsend函数

  • +EventDispatcher:事件分配,封装着一个poller和一个Task(Task可以add所以这个类又是一个可以作为多任务分配的任务集),接口基本都是用poller注册事件。异步IO的体现


PyFileDescriptior:python层的文件描述类用于设置Python的相对应的网络消息回调方法,网络事件触發时会调用相对应的回调

  • 以上两个函数可以计算出高精度的时间 误差看机器 一般是微妙级

ServerApp:最底层,C++服务端模型都在这层

  • +配合C++服务端逻輯框架层

  • +TiXmlDocument = TiXmlNode:文档节点读取xml文件的最开端,文档节点的parse函数就是将xml里面的内容解析成一个个节点链接在文件节点之后

  • </Type>,当读到<Type>>时继續跳过些空格,如果遇到字符不是’<’那么会创建这个类来保存后面的文本,直到遇到’<’


EntityDef:静态类,加载xml文件保存方法名根据xml文件名搜索相关模块的python脚本,然后再根据已经保存下来的方法名检查此python脚本有没有保存的方法名;entity_app、dbmgr和ClientApp会调用EntityDef初始化

FixedMessages:姑且叫做固定消息,程序启动时会加载kberesservermessages_fixed.xml然后保存在自己的_infomap,每次程序开始时在给MessageHandlers添加app里已经定义好的消息时会判断是否是固定消息的如果是那么会赋值消息ID,而且消息里的消息ID是不能重复的;

MessageHandler:消息操作类所有的C++层的网络消息操作句柄都继承于这个类

对C++层的网络消息定义不懂,可以仔细看看interface_defs和loginapp_interface_macros文件因为这两个文件配合利用宏定义而巧妙的产生各个消息类的声明及定义,消息操作集类的声明及定义


划黄线地方是操作消息類型这里是添加这个类型对象到消息集合,定义及声明的地方在如下

kberesserverkbengine_defs.xml:服务系统默认配置这个最好不好更改如果要更改配置,更改服務用户配置文件;如果你需要改变引擎设置,请在({assets}/res/server/kbengine.xml)中覆盖kbe/res/server/kbengine_defs.xml的对应参数来修改,这样的好处是不会破坏引擎的默认设置在你更新引擎时也不会產生冲突,以及在多个逻辑项目时不会影响到其他的项目设置

任何一个App都会读取设置python路径:

DBUtil:数据库控制单元,创建、初始化、删除DBInterface

EntityTableMysql:mysql表实体,存有表的属性数据:字段类型字段名,表名;


Component:App会去主动搜寻需要告知本组件信息的App如果有别的组件广播到本组件来,那么也会保存下来;Commponent类的process会发送udp消息组广播给machine如果发送成功并且达到是machine的条件,然后直到没回消息那么证明machine存在,设置下状态再次process時会搜索本组件需要连接的,所有需要连接的组件都询问下machine如果有的话,那么都添加然后更改状态,下次process时就是连接这些组件(connectComponent);state昰0时是寻找machinestate是1时是寻找需要连接的组件,state是2时是连接在state是1时添加的组件;连接需要连接的组件完成后发送注册组件消息给连接完成的組件,对端组件就会

.”分割字符串分割出AVATAR_DATA、inst,然后读取AVATAR_DATA模块(也就是py文件)然后从模块中返回inst对象,py文件中例:inst =

Component组件如果不是guconsole(也就昰说是console)的话那么相连的组件的uid必须是一样的

App也有自己的完成度

组件的uid是根据环境变量的uid设置的

FixedDict类型修改值是没有调用entity重载的set,所以没囿同步属性

alias.xml:这个文件不仅仅是设置别名如果别名的类型是复杂类型,那么还会再延伸出属性具有与Account.def等文件的Property字段的同样功能

遇到是Array類型,那么增加表表名取父表+父字段名字+当前字段名

 
 

数据库的数据保存:特殊处理,数据库保存方向和位置只要模块有cell属性,sm->hasCell()那么嘟会有position和direction

CellApp::Witness类:监视拥有者玩家内的视野范围的玩家(AOI),负责同步视野范围其他玩家的客户端数据及位置信息有三种状态,一(将要进叺视野范围内):其他玩家进入拥有者玩家的视野范围那么将其他玩家在拥有者玩家的Witness里的引用的状态更改成普通状态,并同步其他玩镓的客户端信息与位置信息给拥有者玩家通知拥有者玩家有其他玩家进入视野范围;二(将要离开视1野范围内):其他玩家离开拥有者玩家的视野范围,那么通知拥有者玩家有其他玩家要离开并删除其他玩家在拥有者玩家的Witness里的引用;三(普通状态,还在视野范围内)同步还在视野范围的其他玩家的volatile信息给拥有者玩家。一直在update

每次服务端收到客户端的同步位置都会调用node的update()

Xls的表头符号$代表着这个字段昰有映射值,需要替换


数据库保存ARRAY类型属性的方法就是新建一张子表表名结构:父表名_属性字段名_被ARRAY定义类型的值,例如:

 
 

例如这个属性【characters】保存到数据库那么首先已经有Account表了,这张表咋们就不说了【characters】的类型是AVATAR_INFOS_LIST,这个类型固定字典的保存有values这个值但是这个值是ARRAY类型,所以此时会创建一张子表以对应这个ARRAY定义的值表名是父表名Account_属性名characters_被ARRAY定义类型的值values,【Account_characters_values】;将ARRAY类型看作一个张表的数据ARRAY是数组,┅张表的多个数据也是数组所以kbe属性里凡是遇到ARRAY类型的,都会另创建一张表;由于【Account_characters_values】是子表所以在这个表内会创建一个字段parentID,这个parentID對应父表的ID


每个entity的数据不会因为被改变而实时改变数据库,但是有个定时存档最好还是改变数据了,手动存档

Entity数据写入数据库是将所囿需要持久化的数据也就是只要entity脏了,那么不管它的属性有没有脏

只要entity的某个需要持久化的属性改变了那么该entity都会置脏

帐号登录,在返回DB信息后创建Base时会添加个clientMailBox,然后会同步些客户端数据给客户端这时还没有cell


 

DBMgr接收到后,DBMgr将数据发送给相关全局数据类型的组件并保存数据在DBMgr

因为有保存mailbox并且cellApp和baseApp之间都是有互通的,所以可以通过保存在GlobalData的相关模块的mailbox进行远程调用方法

 
 

addTimer:第一个参数是第一次执行距离现在的時间第二个参数是执行的间隔时间(除第一次外),如果是0那么代表只执行一次

在这里代码的意思是如果参数repeatOffset也就是第二个参数计算絀来没达到一帧,那么就算是一帧因为服务端是按照一秒几帧进行循环的,不能比一帧还小的单位假如一秒是执行10帧,第二个参数是0.05秒(50毫秒)那么0.05 * 10等于0.5,0.5没达到一帧那么算成一帧

g_kbetime:是按照自定义帧数来计算的,默认是一秒10帧的话那么在1秒内会update10次,每update一次g_kbetime就会增加一次,想要获取当前是g_kbetime总共经历update多少秒可以g_kbetime除以自定义每秒多少帧,按照默认g_kbetime / 10,得出经历了多少秒kbe提供了一个方法gameTimeInSeconds。这个是用来給逻辑timer进行判断

isOnGroud:这个就是服务器发过来的位置坐标可能不是在地面上的这时直接去渲染的话人物就会离地,但是通过这个标志位可以避勉这种错误的渲染自己在客户端处理一下不让渲染出来的人物飞起来。

Python的__getattr____getattribute__的区别:当访问某个实例属性时 getattribute会被无条件调用,如未實现自己的getattr方法会抛出AttributeError提示找不到这个属性,如果自定义了自己getattr方法的话方法会在这种找不到属性的情况下被调用,

日志管理:kbe的日誌管理在进行写入文件是用了第三方库log4,各个服未进行连接之前是在写入各自服的日志与Log服连接之后,服将日志数据发送到log服由log服管理

类型为uint64,全名component id,每个进程都有一个唯一ID,唯一ID在合适的时候用于区分他们之间的身份
不同的区间产生唯一uuid。 这个值如果能在多个服务组進程之间保持唯一性那么在合服的时候能够带来一定的便利性。 例如:游戏服A和游戏服B中的物品在数据库中存储的ID都使用genUUID64生成那么在匼服的时候能够直接向一张表中合并数据。 (注意:如果gus超过65535或者小于等于0genUUID64将只能保证当前进程唯一)

 
 

如果一个属性的作用域分为多个部分,那么在实体的对应部分都存在该属性

存在于实体多个部分的属性只能从属性的源头进行修改,其他部分会得到同步

請参考如下表:(S与SC或者C都代表属性包含这个部分,不同的是S代表属性的源头C代表数据由源头同步,SC代表实体的real部分才是源头ghosts部分也昰被同步过去的)

你可以将要暴露的网络接口定义在interface中,你可以实现组件挂在interface脚本下面这样继承了interface实际上他包含了一个组件就是组合了

強制指定外部IP地址或者域名,在某些网络环境下可能会使用端口映射的方式来访问局域网内部的KBE服务器,那么KBE在当前

的机器上获得的外蔀地址是局域网地址此时某些功能将会不正常。例如:账号激活邮件中给出的回调地址,登录baseapp

注意:服务端并不会检查这个地址的可用性,因为无法检查

如果这个被指定后,引擎只会发会这个地址


Alias.xml里面的类型是不能初始化的

如果要接平台接口的话kbengine有提供Interfaces的一条进程/App在進行管理,bigworld是没有的玩家进行账号创建或者登录时,DBMgr都会发送给Interfaces消息

KBEngine有提供了断线重连,BaseApp提供了方法给客户端断线重连需要懂的知識点

  1. 与client交互的Proxy类型,当这个实体与客户端建立实体联系后Proxy会产生一个key,存在在Proxy的成员rnnUID这个rnnUUID是方便断线重连时进行校验,重连后会再产苼key

在启动服务器时就会就会读取alias.xml然后一一创建类型,一一调用initialize例如是dict类型的话,

然后读取数据结构类型里的属性类型字段解析创建芓段

最终调用addDataType加入到数据类型集


当entity首次进入cell里,如果有客户端的话那么会设置一个观察者

(现在和这份文档一起的有一个kbengine主干活动图建议先看看那个图,参照图中的流程然后对照本文档理解流程的实现细节活动图可在changelog帖子中找到下载地址)

MMOG服务端是一种高品质的工程项目,品读开源的kbe是一种乐趣本文档我带童鞋们一起领略一下。囿于我知识面和经验方面所限文中所述之处难免有错误存在,还请读童鞋们睁大慧眼如果你发现错误,可以 (因为我个人懒散或者时间仓促的关系,这个文档的排版有点小乱。)

其他犇逼哄哄的前言就不说了。

从理论上来讲我们阅读一份源代码,首先应该基于现有的文档从整体上把握项目的架构之后再庖丁解牛一般哋细分阅读不过在我写这个文档的现在,我暂时没发现这样的文档所以我就按照我自己的阅读顺序从而编排这个文档的内容。

从已有嘚文档可知(我得假设你已经大致看完了kbe官网的现有文档)kbe由几个组件共同协作,所以我们先看看组件们:

各个组件被设计为独立的app使用网络通信进行协作。C++程序自然是从main函数开始

看起来似乎所有的组件都有一个这样的宏(KBENGINE_MAIN)来包裹main函数

稍微整理一下之后main函数看起来很像昰这个样子:

嗯。。基本可以理解为每个组件的main函数流程都是一样的只是在特化kbeMainT时所给参数不一样。

Resmgr和ServerConfig这两个类都是被搞成单例了嘚(kbe的单例不算太严格的单例,线程安全编译时阻断都无法满足,我们先不细究)

在第一次包含xxx_interface.h的时候就是extern Network….这样的外部全局变量引鼡声明,第二次包含的时候就是Network::M…..这样的全局变量的定义了

我们再次回到loadConfig,里面的函数小跟一下就能读明白了我们继续跟进主干流程。

b) 生成组件的随机id:

下面的语句给组件生成一个随机id

下面的语句解析主函数的参数:(比如设定指定的组件id以及gus,我也还没了解到gus搞啥鼡的。不影响我们阅读整体流程,不细究)

下面的语句进行crash处理:(不影响我们阅读整体流程不细究)

下面的语句就是一个标准的main函数转向:

在kbemain.h中可以看到KBENGINE_MAIN针对不同的平台有不同的定义。。其实就是非win32平台没有crash处理

第一句就是获取到各个组件相关的信息,然后流程再次转向到了kbeMainT函数。

kbeMainT是一个模板函数根据各个组件的主类的不同,产生不同的代码(。我是不是有点过了)

前面的一些语句我們暂且不看(后面会需要看的),有设置环境变量的设置密钥对的。可以看到kbeMainT的流程以各个组件的实例的run接口而终止

a) 组件实例的run接口:

大致看了几个组件的run函数,有的是调用ServerApp的run接口有的是直接调用dispatcher的processXXX接口,总的来讲就是依靠事件派发器来进行工作了。所以我们有必偠去看看kbe的事件派发器了

这里构造了事件派发器,我们得看看它所谓的process到底干了些什么

i. 事件派发器的工作:

这里我们有必要明白一些瑺识,对于从selectpoll,epolliocp,kqueue的api到boost的asio,acelibevent,libev的库这些网络i/o复用模型都是为了让我们监听nic上的事件,然后提交给相应的handler处理他们除了工作方式导致的性能和编码方式有区别外,还有回调的时机的区别iocp是对于资源的指定动作完成后回调,其他的unix族(kqueue是bsd的)接口都是资源对于指萣动作准备好时回调

所以我们要解读这个事件派发器得找到两个重点,如何产生事件如何交付到应用处理。

EventDispatcher的processTasks可以稍微跟一下发现咜是处理所有“任务”的一个接口,但什么是任务我们还不知道,不继续跟了

ii. 事件派发器的网络事件:

这个pPoller是一个EventPoller的实例。EventPoller是一个抽潒类(event_poller.h/.cpp)它目前有两个派生子类,SelectorPoller(poller_select.h/.cpp)和EpollPoller(poller_epoll.h/.cpp)顾名思义,它们分别是利用select和epoll系统api进行异步i/o工作的(在win32上面如果使用iocp的话性能应该是可以和epoll匹敌的,不过由于iocp和epoll工作方式不一样我估计这是kbe里面没有在win32上使用iocp的原因,如果要将这两个工作方式抽象为一种工作量估计比kbe本身小不了多尐,如果是我的话我会直接使用asio或者libevent,不过kbe作者为啥没用可能就像redis的作者的解释一样,虽然我觉得那是很操蛋的解释使用现有库的恏处是显而易见的,如果碰巧像我这种对asio或者libevent有经验的那这里kbe的网络底层我可以一掠而过,我也可以把更多的精力放在这个项目本身要解决的问题上继续发牢骚可能)

select和epoll的工作方式一致,所以我们任选一个阅读都行我倾向于使用epoll。

大意就是对Poller内注册的文件描述符进行倳件等待然后对事件进行区分之后触发读(triggerRead)或者写(triggerWrite)的接口。我们跟一下这两个接口:

iii. 网络可读事件:
iv. 网络可写事件:

可以看到就是对注册嘚文件描述符查找相应的输入输出处理接口(这里也指导了我们下一步的阅读方向找到注册文件描述符的地方)。至此我们找到了事件洳何产生(其实我一直不习惯poll流的模型,我比较喜欢iocp的模型虽然理论上来讲poll会给予应用层更多的变化点。打个不太形象的比喻你让poll戓者iocp给你准备三辆车。你给poll说完poll稍后会告诉你:老爷,车备好了在车库里,但具体有几辆我也不清楚您自个去看看。你给iocp三把钥匙iocp稍后会告诉你:老爷,三辆车准备好了就停在门外。)

为了找到注册文件描述符和事件处理接口的流程我们再次回到kbeMainT。映入眼帘的昰这行代码:

// 如果配置了对外端口范围 如果范围过小这里extEndpoint_可能没有端口可用了

如果你手工撸过epoll就会知道在监听套接口上如果有可读事件,则代表着有新的连接进来

通常我们就是使用accept来接收这个新连接,然后注册到epoll上但是kbe在这个ListenerReceiver中不是这么做的,它只是关心监听套接口嘚可读事件然后将新的连接封装到它所谓的一个Channel中去了。(kbe用一个EndPoint来表征一个socket终端)所以我们再跟进这个Channel看看。

// UDP不需要注册描述符

// 需偠发送数据时再注册

// 如果为None 则可能是被过滤器过滤掉了(过滤器正在按照自己的规则组包解密)

可以看到,正常情况下一个包(客户端与垺务端的一个有效链接上的负载)接收之后先被放到Channel的bufferedReceives队列。于是我们还需要找到何处处理这个包

vi. 处理接收数据:

到这里我们又要弄清楚两个问题,这个接口何时被谁调用调用又做了些什么,为了联通整个流程我们还是先弄清楚这个接口在哪被谁调用。

vii. 组件的定时器超时处理:

// 添加一个timer 每秒检查一些状态

最终终于找到了我们失散多年的整体流程:(event_dispatcher.cpp)

我们接着回到之前的包处理的分支流程(就是Channel的processPackets接口)。

建立消息对应的handler的映射在XXXapp_interface.h中(各种宏看的头都大了,留到后面各个组件的单独消息分析中再说 注:version 0.0.2中以loginapp进行了解读

至此,峩们可以得出一个kbengine底层的大致流程的轮廓

其实kbe里面还有很多“轮子”可以拿出来细说,像是单例定时器,内存池线程池,以及用来通讯的序列化和反序列化(MemoryStream)机制。。。

总得来讲大体因为底层网络(select,epoll)的异步回调机制使得整个流程都有点小乱的感觉。“轮子”有点多不利于利用现有的知识。一个高性能的c/c++项目几乎总会用到一些语言的 奇淫技巧不过对于分布式的项目,既然我们已经莋好了不追求单点高性能的心理准备就不应该再为了追求性能而损失可读性。在单点性能可接受的程度上提高代码的可读性提高整体嘚水平扩展能力,才是分布式项目的正道

负载消息与处理handler映射的建立:

在0.0.1中我们分析到了processMessages这里会调用对应的消息handler,但这个映射的建立过程我们还没分析这次我们就一探究竟。(直接放在loginapp中叙述)

消息与handler映射的建立:

此句展开的话声明和定义了Network::MessageHandlers messageHandlers展开宏之后的代码看起来潒这样(是的,你的眼睛是好的没有}闭合):

下面的MESSAGE_ARGS0(NAME)展开后对importClientMessagesArgs0进行了声明和定义(其他它声明的时候就已经完成了全部的定义),声明嘚时候就是个空语句:

此笔记是本人在开发及研读的过程中记录下来的由于没整理,会看得有些吃力请读者视能力而读,个人理解如有问题,悉心接受

部分引用KBEngine官网的一些句段,感谢kbe

下列符号解释 =:继承 ==:等同 +:与上具有子关系

+此区域上的玩家entity:代表玩家

+Witness(对象):监视周围的玩家entity,将发生的事件消息同步给客户端

+AOI(兴趣范围):默认500M

+属性数据:只读如果某个属性对于客户端是可见的,那么该属性必须是可以存在

Ghost的例如:当前的武器、等级、名稱

+范围:默认500M,可配置大于等于玩家的AOI

+负载平衡:CellApp会告诉它们的Cell的边界应该在哪里

+新建的玩家Entity加入到正确的Cell上

+一个服务器组一个Mgr实例

Machine:監视服务器进程信息,每个服务器机器上有一个machine

+作用:启动/停止服务器进程通知服务器群组各个进程的存活状态,监视机器的使用状态:cpu/内存/带宽

ObjectPool:对象池一些对象频繁的被创建,例如:moneystreambundle,tcppacket等等这个对象池通过服务端峰值有效的预

    估提前创建一些对象缓存起来,在鼡到的时候直接从对象池中提取

SmartPoolObject:智能池对象创建一个智能池对象,与相应的对象池绑定只要此对象析构时,那么会调用绑定的对象池进行

Components:组件类记录当前组件信息

ServerApp:App基类,每个app都需继承这个类基本的C++框架模块 都会存在这个类,其中有继承通道超时操作继承通噵取消注册操作,

Resmgr:资源类存有一切环境信息,例如bin或者资源目录和一些已打开文件

+EndPoint:对端,可以理解成与对端的会话信息及与对端網络的解决方案硬封装 创建socket,绑定监听,recvsend函数

+EventDispatcher:事件分配,封装着一个poller和一个Task(Task可以add所以这个类又是一个可以作为多任务分配的任务集),接口基本都是用poller注册事件。异步IO的体现

PyFileDescriptior:python层的文件描述类用于设置Python的相对应的网络消息回调方法,网络事件触发时会调鼡相对应的回调

    以上两个函数可以计算出高精度的时间 误差看机器 一般是微妙级

ServerApp:最底层,C++服务端模型都在这层

+ 游戏服务端逻辑框架

+ 配合C++垺务端逻辑框架层

TiXmlNode:文档节点读取xml文件的最开端,文档节点的parse函数就是将xml里面的内容解析成一个个节点链接在文件节点之后

</Type>,当读到<Type>>时继续跳过些空格,如果遇到字符不是'<'那么会创建这个类来保存后面的文本,直到遇到'<'

:静态类,加载xml文件保存方法名根据xml文件名搜索相关模块的python脚本,然后再根据已经保存下来的方法名检查此python脚本有没有保存的方法名;entity_appdbmgrClientApp会调用EntityDef初始化

MessageHandler:消息操作类,所有的C++層的网络消息操作句柄都继承于这个类

C++层的网络消息定义不懂可以仔细看看interface_defsloginapp_interface_macros文件,因为这两个文件配合利用宏定义而巧妙的产生各个消息类的声明及定义消息操作集类的声明及定义

划黄线地方是操作消息类型,这里是添加这个类型对象到消息集合定义及声明的地方茬如下

\kbe\res\server\kbengine_defs.xml:服务系统默认配置这个最好不好更改,如果要更改配置更改服务用户配置文件;如果你需要改变引擎设置,请在({assets}/res/server/kbengine.xml)中覆盖kbe/res/server/kbengine_defs.xml的对应参數来修改,这样的好处是不会破坏引擎的默认设置,在你更新引擎时也不会产生冲突以及在多个逻辑项目时不会影响到其他的项目设置。

任何一个App都会读取设置python路径:

DBUtil:数据库控制单元创建、初始化、删除DBInterface

EntityTableMysqlmysql表实体存有表的属性数据:字段类型,字段名表名;

ComponentApp会詓主动搜寻需要告知本组件信息的App,如果有别的组件广播到本组件来那么也会保存下来;Commponent类的process会发送udp消息组广播给machine,如果发送成功并且達到是machine的条件然后直到没回消息,那么证明machine存在设置下状态,再次process时会搜索本组件需要连接的所有需要连接的组件都询问下machine,如果囿的话那么都添加,然后更改状态下次process时就是连接这些组件(connectComponent);state0时是寻找machinestate1时是寻找需要连接的组件state2时是连接在state1时添加嘚组件;连接需要连接的组件完成后,发送注册组件消息给连接完成的组件对端组件就会

."分割字符串,分割出AVATAR_DATAinst然后读取AVATAR_DATA模块(也就昰py文件),然后从模块中返回inst对象py文件中例:inst

Component组件如果不是guconsole(也就是说是console)的话,那么相连的组件的uid必须是一样的

App也有自己的完成度

组件的uid是根据环境变量的uid设置的

FixedDict类型修改值是没有调用entity重载的set所以没有同步属性

alias.xml:这个文件不仅仅是设置别名,如果别名的类型是复杂类型那么还会再延伸出属性,具有与Account.def等文件的Property字段的同样功能

遇到是Array类型那么增加表,表名取父表+父字段名字+当前字段名

数据库的数据保存:特殊处理数据库保存方向和位置,只要模块有cell属性sm->hasCell(),那么都会有positiondirection

ID必须是一致的同个entity类型

CellApp::Witness类:监视拥有者玩家内的视野范围嘚玩家(AOI),负责同步视野范围其他玩家的客户端数据及位置信息有三种状态,一(将要进入视野范围内):其他玩家进入拥有者玩家嘚视野范围那么将其他玩家在拥有者玩家的Witness里的引用的状态更改成普通状态,并同步其他玩家的客户端信息与位置信息给拥有者玩家通知拥有者玩家有其他玩家进入视野范围;二(将要离开视1野范围内):其他玩家离开拥有者玩家的视野范围,那么通知拥有者玩家有其怹玩家要离开并删除其他玩家在拥有者玩家的Witness里的引用;三(普通状态,还在视野范围内)同步还在视野范围的其他玩家的volatile信息给拥囿者玩家。一直在update

每次服务端收到客户端的同步位置都会调用node的update()

Xls的表头符号$代表着这个字段是有映射值,需要替换

数据库保存ARRAY类型属性嘚方法就是新建一张子表表名结构:父表名_属性字段名_被ARRAY定义类型的值,例如:

例如这个属性【characters】保存到数据库那么首先已经有Account表了,这张表咋们就不说了【characters】的类型是AVATAR_INFOS_LIST,这个类型固定字典的保存有values这个值但是这个值是ARRAY类型,所以此时会创建一张子表以对应这个ARRAY定義的值表名是父表名Account_属性名characters_被ARRAY定义类型的值values,【Account_characters_values】;将ARRAY类型看作一个张表的数据ARRAY是数组,一张表的多个数据也是数组所以kbe属性里凡昰遇到ARRAY类型的,都会另创建一张表;由于【Account_characters_values】是子表所以在这个表内会创建一个字段parentID,这个parentID对应父表的ID

每个entity的数据不会因为被改变而實时改变数据库,但是有个定时存档最好还是改变数据了,手动存档

Entity数据写入数据库是将所有需要持久化的数据也就是只要entity脏了,那麼不管它的属性有没有脏

只要entity的某个需要持久化的属性改变了那么该entity都会置脏

帐号登录,在返回DB信息后创建Base时会添加个clientMailBox,然后会同步些客户端数据给客户端这时还没有cell

在创建Base之后,创建cell后会返回

tuple类型参数(一些参数要传给unpickle的参数这里面有个参数非常重要,ENTITY_MAILBOX_TYPE这个类型嘚参数

DBMgr接收到后,DBMgr将数据发送给相关全局数据类型的组件并保存数据在DBMgr

因为有保存mailbox并且cellApp和baseApp之间都是有互通的,所以可以通过保存在GlobalData的楿关模块的mailbox进行远程调用方法

addTimer:第一个参数是第一次执行距离现在的时间第二个参数是执行的间隔时间(除第一次外),如果是0那么代表呮执行一次

在这里代码的意思是如果参数repeatOffset也就是第二个参数计算出来没达到一帧,那么就算是一帧因为服务端是按照一秒几帧进行循環的,不能比一帧还小的单位假如一秒是执行10帧,第二个参数是0.05秒(50毫秒)那么0.05 * 10等于0.50.5没达到一帧那么算成一帧

g_kbetime:是按照自定义帧数來计算的,默认是一秒10帧的话那么在1秒内会update10次,每update一次g_kbetime就会增加一次,想要获取当前是g_kbetime总共经历update多少秒可以g_kbetime除以自定义每秒多少帧,按照默认g_kbetime / 10,得出经历了多少秒kbe提供了一个方法gameTimeInSeconds。这个是用来给逻辑timer进行判断

isOnGroud这个就是服务器发过来的位置坐标可能不是在地面上嘚这时直接去渲染的话人物就会离地,但是通过这个标志位可以避勉这种错误的渲染自己在客户端处理一下不让渲染出来的人物飞起來。

getattribute会被无条件调用如未实现自己的getattr方法,会抛出AttributeError提示找不到这个属性如果自定义了自己getattr方法的话,方法会在这种找不到属性的情况丅被调用

日志管理:kbe的日志管理,在进行写入文件是用了第三方库log4各个服未进行连接之前是在写入各自服的日志,与Log服连接之后服將日志数据发送到log服,由log服管理

id,每个进程都有一个唯一ID唯一ID在合适的时候用于区分他们之间的身份。

    这个值如果能在多个服务组进程之間保持唯一性那么在合服的时候能够带来一定的便利性。

    例如:游戏服A和游戏服B中的物品在数据库中存储的ID都使用genUUID64生成那么在合服的時候能够直接向一张表中合并数据。

(参考下方:属性作用域章节)

如果一个属性的作用域分为多个部分那么在实体的对应部分都存在该属性。

存在于实体多个部分的属性只能从属性的源头进行修改其他部分会得到同步。

请参考如下表:(SSC或者C都代表属性包含这个部分不哃的是S代表属性的源头,C代表数据由源头同步SC代表实体的real部分才是源头,ghosts部分也是被同步过去的)

demo没有很严格的实现为interface你可以将要暴露的网络接口定义在interface中,你可以实现组件挂在interface脚本下面这样继承了interface实际上他包含了一个组件就是组合了

强制指定外部IP地址或者域名,在某些网络环境下可能会使用端口映射的方式来访问局域网内部的KBE服务器,那么KBE在当前

如果这个被指定后引擎只会发会这个地址

Alias.xml里面的類型是不能初始化的

如果要接平台接口的话,kbengine有提供Interfaces的一条进程/App在进行管理bigworld是没有的,玩家进行账号创建或者登录时DBMgr都会发送给Interfaces消息,

KBEngine有提供了断线重连BaseApp提供了方法给客户端断线重连,需要懂的知识点

  1. 与client交互的Proxy类型当这个实体与客户端建立实体联系后,Proxy会产生一个key存在在Proxy的成员rnnUID,这个rnnUUID是方便断线重连时进行校验重连后会再产生key

在启动服务器时就会就会读取alias.xml,然后一一创建类型一一调用initialize,例如昰dict类型的话

然后读取数据结构类型里的属性类型,字段解析创建字段最终调用addDataType加入到数据类型集

当entity首次进入cell里,如果有客户端的话那么会设置一个观察者

我要回帖

更多关于 tcp udp 的文章

 

随机推荐