请选择 进入手机版 | 继续访问电脑版

NoahFrame

 找回密码
 Register Now
搜索
热搜: redis mysql tutorial
查看: 2995|回复: 0

第五章 NF分布式服务器解决方案--游戏世界的基础管理

[复制链接]

30

主题

111

帖子

632

积分

Administrator

Rank: 9Rank: 9Rank: 9

积分
632
发表于 2016-12-30 09:42:51 | 显示全部楼层 |阅读模式
NF(https://github.com/ketoo/NoahGameFrame)全称为 NoahFrame/NoahGameFrame。


NF最早为客户端设计,后来随着时代的变化,而为自己又转为服务器开发,故在吸收了众多引擎的优点后(包含Ogre的插件模式&模块化管理机制,Bigworld的数据管理&配置机制,类似MYGUI的接口层次设计),经过多年演化和实践,变成了一套游戏开发J解决方案。方案中包含开源的服务器架构,网络库(站在libevent的肩膀上),和unity3d的demo源码。现在NF已经在多个公司的多个项目中使用,其中包含知名产品 《全民无双》。


关键词


NoahGameFrame/NoahFrame/NF
集群/负载均衡/分布式
网关服务器 GateServer 心跳 多线程/线程池 开源网络框架/模型
一致性hash算法/ConsistentHash
游戏开发中的设计模式/数据结构
Socket Nagle/粘包/开源游戏服务器/ Game Server





用actor的人说,万物皆actor;
面向接口的人说,IOP才是未来;
在面向对象编程的世界里,万物皆对象;

在面向数据编程的世界里,万物皆数据;

phper说,你们都是渣渣。


这里不区别哪种面向XX更牛逼和哪种语言更牛逼,因为他们大多是递进关系,在不同的场所他们有着不同的作用,使用者本身的使用水平反映了其对系统的抽象理解而非模型。当然,万物皆对象这一点大家都是认同的(不认同的请绕道),而在对象是基础上,如何设计新型的数据结构,如何组织这些对象,对象和对象之间如何通信,如何控制这些对象等等,均是一门新的学问。比如最具备广泛群里基础的理论就是设计模式。在NF世界里,面向对象是基础,也是容纳万物的基础;对象的控制,均通过各种接口模块控制;而对象的业务逻辑驱动,又是通过数据驱动模式来组合,下面就来说说NF的基础对象管理。

在NF中,无需再声明任何业务对象,比如NPC,比如Player,比如Item等,只要你想的到,NF的基础对象类就能容纳下,此类名字叫:NFCObject(NFIObject),内部包含属性管理器,表管理器等内容。


而为什么NF的世界中无需其他业务对象呢,之前的文章有写过,因为我们有通用的属性管理类。通常来说任何一个对象类(Class)定义了关于某一类事物的抽象特点,比如事物的属性和它可以做到的事情(它的行为)。因此我们拥有通用的属性管理集后,NFCObject(NFIObject)就可以用来抽象任何业务对象类,而不会存在表达不了对象属性的问题。


那么余下的某一类对象的行为,在NF的世界中,都是通过Module来做到,Module的设计是以行为为中心,以数据为依托。Module的内容大部分都是心跳函数,事件回调,属性回调之类关于数据的操作和数据变更的反馈,以此驱动整个逻辑的运转。而且在Module中,通常没有他自己的成员数据,为什么呢?因为所有的数据都来自于NFCObject(NFIObject)自身,Module类似工厂的流水处理,根据NFIObject本身的数据,做出不同的反应。

那么在NF中,这么多同一类型的对象,如何管理,如何操作,肯定是一个问题,因此我们提供了NFIKernelModule接口类,提供了关于的NFIObject的创建,销毁以及操作Property和Record数据,以及添加数据回调等一系列的接口,而NFIObject类本身又提供了Property和Record数据的各种操作接口。

NFIKernelModule的子类,操作NFIObject,仍旧需要通过调用NFIObject类接口实现。因为NF是面向数据编程,数据的筹备工作早已经做好。因此业务逻辑中,无需对添加Property数据,因此则NFIKernelModule中无AddProperty类似相关接口,其中NF源码中的Tutorial2和Tutorial3工程对本章内容有较好的体验,可以结合那2个Tutorial来观看此文。


KernelModule中NFIObject的创建和销毁:



  1. virtual NF_SHARE_PTR<NFIObject> GetObject(const NFGUID& ident) = 0;
  2.     virtual NF_SHARE_PTR<NFIObject> CreateObject(const NFGUID& self, const int nSceneID, const int nGroupID, const std::string& strClassName, const std::string& strConfigIndex, const NFIDataList& arg) = 0;

  3.     virtual bool DestroyObject(const NFGUID& self) = 0;
  4.     virtual bool DestroyAll() = 0;
复制代码

下面我们来说说,对象的创建和销毁流程,以及怎样的对象才能创建。
创建对象调用的函数是CreateObject函数,我们来看看函数的实现:


  1. NF_SHARE_PTR<NFIObject> NFCKernelModule::CreateObject(const NFGUID& self, const int nSceneID, const int nGroupID, const std::string& strClassName, const std::string& strConfigIndex, const NFIDataList& arg)
  2. {
  3.     NF_SHARE_PTR<NFIObject> pObject;
  4.     NFGUID ident = self;

  5.     //1步骤,确认场景是否存在
  6.     NF_SHARE_PTR<NFCSceneInfo> pContainerInfo = m_pSceneModule->GetElement(nSceneID);
  7.     if (!pContainerInfo)
  8.     {
  9.         m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NFGUID(0, nSceneID), "There is no scene", nSceneID, __FUNCTION__, __LINE__);
  10.         return pObject;
  11.     }

  12.     //1步骤,确认场景副本是否存在
  13.     if (!pContainerInfo->GetElement(nGroupID))
  14.     {
  15.         m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NFGUID(0, nSceneID), "There is no group", nGroupID, __FUNCTION__, __LINE__);
  16.         return pObject;
  17.     }


  18.     //给予GUID
  19.     if (ident.IsNull())
  20.     {
  21.         ident = m_pUUIDModule->CreateGUID();
  22.     }
  23.     //1步骤,确认自己是否存在(怕创建错了或者重复了之类)
  24.     if (GetElement(ident))
  25.     {
  26.         m_pLogModule->LogObject(NFILogModule::NLL_ERROR_NORMAL, ident, "The object has Exists", __FUNCTION__, __LINE__);
  27.         return pObject;
  28.     }

  29.     //2步骤,获取VirtualClass的静态配置的指针,并为对象添加这些Propert,Record等内容
  30.     //3步骤,添加通用的观察者回调函数
  31.     NF_SHARE_PTR<NFIPropertyManager> pStaticClassPropertyManager = m_pLogicClassModule->GetClassPropertyManager(strClassName);
  32.     NF_SHARE_PTR<NFIRecordManager> pStaticClassRecordManager = m_pLogicClassModule->GetClassRecordManager(strClassName);
  33.     NF_SHARE_PTR<NFIComponentManager> pStaticClasComponentManager = m_pLogicClassModule->GetClassComponentManager(strClassName);
  34.     if (pStaticClassPropertyManager && pStaticClassRecordManager && pStaticClasComponentManager)
  35.     {

  36.         pObject = NF_SHARE_PTR<NFIObject>(NF_NEW NFCObject(ident, pPluginManager));
  37.         //是否是应该晚点等到事件2时才加入容器,这样能保证进入容器的对象都是有完整数据的,否则因为协程的原因,其他对象找到他时他却没数据或者部分数据
  38.         AddElement(ident, pObject);
  39.         pContainerInfo->AddObjectToGroup(nGroupID, ident, strClassName == "Player" ? true : false);

  40.         NF_SHARE_PTR<NFIPropertyManager> pPropertyManager = pObject->GetPropertyManager();
  41.         NF_SHARE_PTR<NFIRecordManager> pRecordManager = pObject->GetRecordManager();
  42.         NF_SHARE_PTR<NFIComponentManager> pComponentManager = pObject->GetComponentManager();

  43.         //默认属性
  44.         NF_SHARE_PTR<NFIProperty> pStaticConfigPropertyInfo = pStaticClassPropertyManager->First();
  45.         while (pStaticConfigPropertyInfo.get())
  46.         {
  47.             pPropertyManager->AddProperty(ident,
  48.                                           pStaticConfigPropertyInfo->GetKey(),
  49.                                           pStaticConfigPropertyInfo->GetType(),
  50.                                           pStaticConfigPropertyInfo->GetPublic(),
  51.                                           pStaticConfigPropertyInfo->GetPrivate(),
  52.                                           pStaticConfigPropertyInfo->GetSave(),
  53.                                           pStaticConfigPropertyInfo->GetIndex(),
  54.                                           pStaticConfigPropertyInfo->GetView(),
  55.                                           pStaticConfigPropertyInfo->GetRelationValue());

  56.             //通用回调,方便NET同步
  57.             pObject->AddPropertyCallBack(pStaticConfigPropertyInfo->GetKey(), this, &NFCKernelModule::OnPropertyCommonEvent);

  58.             pStaticConfigPropertyInfo = pStaticClassPropertyManager->Next();
  59.         }

  60.         NF_SHARE_PTR<NFIRecord> pConfigRecordInfo = pStaticClassRecordManager->First();
  61.         while (pConfigRecordInfo)
  62.         {
  63.             pRecordManager->AddRecord(ident,
  64.                                       pConfigRecordInfo->GetName(),
  65.                                       pConfigRecordInfo->GetInitData(),
  66.                                       pConfigRecordInfo->GetKeyState(),
  67.                                       pConfigRecordInfo->GetInitDesc(),
  68.                                       pConfigRecordInfo->GetTag(),
  69.                                       pConfigRecordInfo->GetRelatedRecord(),
  70.                                       pConfigRecordInfo->GetRows(),
  71.                                       pConfigRecordInfo->GetPublic(),
  72.                                       pConfigRecordInfo->GetPrivate(),
  73.                                       pConfigRecordInfo->GetSave(),
  74.                                       pConfigRecordInfo->GetView(),
  75.                                       pConfigRecordInfo->GetIndex());

  76.             //通用回调,方便NET同步
  77.             pObject->AddRecordCallBack(pConfigRecordInfo->GetName(), this, &NFCKernelModule::OnRecordCommonEvent);

  78.             pConfigRecordInfo = pStaticClassRecordManager->Next();
  79.         }

  80.         std::string strSrciptComponentName;
  81.         NF_SHARE_PTR<NFIComponent> xComponent = pStaticClasComponentManager->First(strSrciptComponentName);
  82.         while (!strSrciptComponentName.empty())
  83.         {
  84.             pComponentManager->AddComponent(strSrciptComponentName, xComponent);

  85.             strSrciptComponentName.clear();
  86.             NF_SHARE_PTR<NFIComponent> xComponent = pStaticClasComponentManager->Next(strSrciptComponentName);
  87.         }
  88.         //////////////////////////////////////////////////////////////////////////
  89.         //配置属性
  90.         NF_SHARE_PTR<NFIPropertyManager> pConfigPropertyManager = m_pElementInfoModule->GetPropertyManager(strConfigIndex);
  91.         NF_SHARE_PTR<NFIRecordManager> pConfigRecordManager = m_pElementInfoModule->GetRecordManager(strConfigIndex);

  92.         if (pConfigPropertyManager.get() && pConfigRecordManager.get())
  93.         {
  94.             NF_SHARE_PTR<NFIProperty> pConfigPropertyInfo = pConfigPropertyManager->First();
  95.             while (pConfigPropertyInfo.get())
  96.             {

  97.                 pPropertyManager->SetProperty(pConfigPropertyInfo->GetKey(), pConfigPropertyInfo->GetValue());

  98.                 pConfigPropertyInfo = pConfigPropertyManager->Next();
  99.             }
  100.         }

  101.         DoEvent(ident, strClassName, CLASS_OBJECT_EVENT::COE_CREATE_NODATA, arg);

  102.         //传入的属性赋值
  103.         for (int i = 0; i < arg.GetCount() - 1; i += 2)
  104.         {
  105.             const std::string& strPropertyName = arg.String(i);
  106.             if ("ConfigID" != strPropertyName
  107.                 && "ClassName" != strPropertyName
  108.                 && "SceneID" != strPropertyName
  109.                 && "GroupID" != strPropertyName)
  110.             {
  111.                 NF_SHARE_PTR<NFIProperty> pArgProperty = pStaticClassPropertyManager->GetElement(strPropertyName);
  112.                 if (pArgProperty)
  113.                 {
  114.                     switch (pArgProperty->GetType())
  115.                     {
  116.                         case TDATA_INT:
  117.                             pObject->SetPropertyInt(strPropertyName, arg.Int(i + 1));
  118.                             break;
  119.                         case TDATA_FLOAT:
  120.                             pObject->SetPropertyFloat(strPropertyName, arg.Float(i + 1));
  121.                             break;
  122.                         case TDATA_STRING:
  123.                             pObject->SetPropertyString(strPropertyName, arg.String(i + 1));
  124.                             break;
  125.                         case TDATA_OBJECT:
  126.                             pObject->SetPropertyObject(strPropertyName, arg.Object(i + 1));
  127.                             break;
  128.                         default:
  129.                             break;
  130.                     }
  131.                 }
  132.             }
  133.         }

  134.         //3步骤,进入对象需要要进入的场景和副本
  135.         pObject->SetPropertyString("ConfigID", strConfigIndex);
  136.         pObject->SetPropertyString("ClassName", strClassName);
  137.         pObject->SetPropertyInt("SceneID", nSceneID);
  138.         pObject->SetPropertyInt("GroupID", nGroupID);

  139.         //4步骤,出发对象创建流程时间,做业务逻辑驱动
  140.         DoEvent(ident, strClassName, COE_CREATE_LOADDATA, arg);
  141.         DoEvent(ident, strClassName, COE_CREATE_BEFORE_EFFECT, arg);
  142.         DoEvent(ident, strClassName, COE_CREATE_EFFECTDATA, arg);
  143.         DoEvent(ident, strClassName, COE_CREATE_AFTER_EFFECT, arg);
  144.         DoEvent(ident, strClassName, COE_CREATE_HASDATA, arg);
  145.     }

  146.     return pObject;
  147. }
复制代码


从上面顺序,我们可以清楚的得知对象创建的一下几个必要流程:


1:确认场景,副本,和对象ID是否存在
2:获取对象类所有配置过的静态Property,Record,Component配置,并为对象添加这些Property,Record,Component
3:使对象在正确的场景和副本中,并为所有的Property,Record,Component等数据添加公共的观察者,以便广播,二次管理之类的业务需要
4:触发对象创建的事件流程(此时对象已经全部创建好了,但是是个空壳子),以便这些事件中可以为此对象赋值,增加Property等回调函数之类


NFIObject的数据操作接口(NFIKernelModule)主要分为2大类操作,set操作和get操作,NF所有对于对象的数据操作均是这2大类。其中这2大类操作方式,又是针对2类数据来操作,分别是Property和Record数据,请注意区分下面的SetXXXInt GetXXXInt这样的接口。
  1. class NFIKernelModule
  2. {
  3.     virtual bool FindProperty(const NFGUID& self, const std::string& strPropertyName) = 0;

  4.     virtual bool SetPropertyInt(const NFGUID& self, const std::string& strPropertyName, const NFINT64 nValue) = 0;
  5.     virtual bool SetPropertyFloat(const NFGUID& self, const std::string& strPropertyName, const double dValue) = 0;
  6.     virtual bool SetPropertyString(const NFGUID& self, const std::string& strPropertyName, const std::string& strValue) = 0;
  7.     virtual bool SetPropertyObject(const NFGUID& self, const std::string& strPropertyName, const NFGUID& objectValue) = 0;

  8.     virtual NFINT64 GetPropertyInt(const NFGUID& self, const std::string& strPropertyName) = 0;
  9.     virtual double GetPropertyFloat(const NFGUID& self, const std::string& strPropertyName) = 0;
  10.     virtual const std::string& GetPropertyString(const NFGUID& self, const std::string& strPropertyName) = 0;
  11.     virtual const NFGUID& GetPropertyObject(const NFGUID& self, const std::string& strPropertyName) = 0;

  12.     virtual NF_SHARE_PTR<NFIRecord> FindRecord(const NFGUID& self, const std::string& strRecordName) = 0;
  13.     virtual bool ClearRecord(const NFGUID& self, const std::string& strRecordName) = 0;

  14.     virtual bool SetRecordInt(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol, const NFINT64 nValue) = 0;
  15.     virtual bool SetRecordFloat(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol, const double dwValue) = 0;
  16.     virtual bool SetRecordString(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol, const std::string& strValue) = 0;
  17.     virtual bool SetRecordObject(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol, const NFGUID& objectValue) = 0;

  18.     virtual bool SetRecordInt(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag, const NFINT64 value) = 0;
  19.     virtual bool SetRecordFloat(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag, const double value) = 0;
  20.     virtual bool SetRecordString(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag, const std::string& value) = 0;
  21.     virtual bool SetRecordObject(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag, const NFGUID& value) = 0;

  22.     virtual NFINT64 GetRecordInt(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol) = 0;
  23.     virtual double GetRecordFloat(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol) = 0;
  24.     virtual const std::string& GetRecordString(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol) = 0;
  25.     virtual const NFGUID& GetRecordObject(const NFGUID& self, const std::string& strRecordName, const int nRow, const int nCol) = 0;

  26.     virtual NFINT64 GetRecordInt(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag) = 0;
  27.     virtual double GetRecordFloat(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag) = 0;
  28.     virtual const std::string& GetRecordString(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag) = 0;
  29.     virtual const NFGUID& GetRecordObject(const NFGUID& self, const std::string& strRecordName, const int nRow, const std::string& strColTag) = 0;
  30. }
复制代码




NFIObject的数据回调接口添加(NFIKernelModule)就2个,分别针对Property和 Record:

  1.     template<typename BaseType>
  2.     bool AddRecordCallBack(const NFGUID& self, const std::string& strRecordName, BaseType* pBase, int (BaseType::*handler)(const NFGUID&, const RECORD_EVENT_DATA&, const NFIDataList::TData&, const NFIDataList::TData&))
  3.     {
  4.         NF_SHARE_PTR<NFIObject> pObject = GetObject(self);
  5.         if (pObject.get())
  6.         {
  7.             return pObject->AddRecordCallBack(strRecordName, pBase, handler);
  8.         }

  9.         return false;
  10.     }

  11.     template<typename BaseType>
  12.     bool AddPropertyCallBack(const NFGUID& self, const std::string& strPropertyName, BaseType* pBase, int (BaseType::*handler)(const NFGUID&, const std::string&, const NFIDataList::TData&, const NFIDataList::TData&))
  13.     {
  14.         NF_SHARE_PTR<NFIObject> pObject = GetObject(self);
  15.         if (pObject.get())
  16.         {
  17.             return pObject->AddPropertyCallBack(strPropertyName, pBase, handler);
  18.         }

  19.         return false;
  20.     }
复制代码


至此,所有的组队对象的控制接口,就是这么多。

NF是不是超简单?属性控制是不是超简单?
NF项目为开源的分布式服务器解决方案,其中包含了网络库,actor库,以及数据驱动等新技术,能大幅提升开发效率节省开发周期以及提高程序的稳定性。

NF项目为开源的分布式服务器解决方案,其中包含了网络库,actor库,以及数据驱动等新技术,能大幅提升开发效率节省开发周期以及提高程序的稳定性。
项目地址 https://github.com/ketoo/NoahGameFrame
如感觉对您有帮助,请给与star,同时也邀请广大同行参与开发和维护,作者QQ 342006,交流QQ群 341159815。
欢迎转载,转载请注明来源,本文版权归作者所有!


回复

使用道具 举报

您需要登录后才可以回帖 登录 | Register Now

本版积分规则

 

GMT+8, 2018-8-15 13:52 , Processed in 0.068987 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表