角色数据存储方法
服务端需要做的一件重要的事情就是保存角色(Role)的数据,以便让他下次登陆时可以还原状态。
初始化数据
在角色首次进入场景时,初始化数据。这一个过程是由客户端发起的,流程大致是:
客户端发起请求 -> 服务端请求从数据库中获取数据 -> 获取数据后初始化Role -> 通知客户端进入场景
尽管这一个过程还做了其他许多的逻辑判断,但其核心内容就是数据的获取,这是我们要关注的地方,其代码流程是:
Scene::RoleEnterSceneAsyn RMIRoleClient::RoleInitAsyn RMIRoleObject::__RoleInit RMIRoleInitBackObjectImpl::RoleInitRet
在成功获取数据后,就可以创建Role了。我们平时写代码的地方通常是在:
-
RMIRoleObject::RoleInit
,从数据库中获取数据 -
RoleOtherInitParam::Serialize
,RoleOtherInitParam::Unserialize
,将数据序列化和反序列化
存储结构为何需要Reset方法?
我们注意到,大多数的存储结构(其命名如SomeParam)都有一个Reset方法。那为什么不使用构造函数列表初始化的方法初始化各个成员呢?这是因为我们使用了一个宏GET_BINARY_DATA来从数据库中初始化二进制类型的数据。而这个宏是约定使用Reset方法来初始化结构体的:
#define GET_BINARY_DATA(DATAADAPTER, TABLE_FIELD, PARAM) \{ \ PARAM.Reset(); \ if (DATAADAPTER.m_data_area[TABLE_FIELD].length > sizeof(PARAM) * 2) \ { \ DATAADAPTER.Free(); \ return -1; \ } \ HexToBin(DATAADAPTER.m_data_area[TABLE_FIELD].vcharp, DATAADAPTER.m_data_area[TABLE_FIELD].length, (char*)&PARAM); \}
在Reset方法中忘记初始化成员将造成严重的错误。这些成员只会被默认初始化一次。
RoleOtherInitParam的用处是什么?
大多数系统数据都打包在RoleOtherInitParam,我们照着里面的代码写一般不会出错,但要理解它的意义。RoleOtherInitParam作用是:
-
当要创建Role时,从数据库中拿数据初始化RoleOtherInitParam,用以初始化Role的各个系统
-
当要存储Role数据时,把各个系统的数据拷贝给RoleOtherInitParam,用以存储至数据库
因为要通过RMI传输数据,所以RoleOtherInitParam提供了序列化的方法。
存储数据
游戏中大概这几种情况会存储角色数据:
-
每隔若干秒(大概几分钟)
-
切换场景时
-
登出时
其代码流程是:
RMIRoleClient::RoleSaveAsyn RMIRoleObject::__RoleSave ... RMIRoleSaveBackObjectImplRole::RoleSaveRet
只存储修改数据
有一些老一点的系统,比如背包系统,采用的是这样的存储方式:每一个物品都是一条数据库记录,每一个物品都有几个用来记录它的状态(structcommon::CHANGE_STATE)的变量。只有状态发生改变的物品才会存储。
这样的系统需要正确设置状态,编程难度较大。现在已经很少使用这样的方式了,因此暂不做介绍。
存储全部数据
采用这种方式存储的系统每当角色存储时,都把自己的所有数据拷贝出来,刷新到数据库中。其代码流程大致是:
Role::Save Role::GetRoleOtherInitParam SomeSystem::GetInitParam
采用这种方法的优点是只需要一个简单的接口就可以完成数据的存储的准备工作。
存储数据流程图
数据初始化的流程就刚好相反。