游戏好友系统与邮件系统实现
好友系统实现:
每个玩家在内存上都会有自己的一个map记录他的好友信息。
这个结构体大致如下:
struct FriendData { uint32_t rid; //目前我们之间的状态 uint32_t status; //记录对方的data uint32_t peerRid; uint32_t peerName; uint32_t peerLevel; uint32_t peerVip; .... .... }; map<uint32_t, FriendData> m_myFriend;
假设A玩家添加了B玩家位他的好友,那么除了A玩家的内存上要增加B玩家的数据外,B玩家也应该会有A玩家的数据。
一个好的设计原则是:彼此不操作对方的内存数据。
从内存上的实现会需要以下几个静态变量(只需要一份即可):
//发送好友申请的玩家数据,这样对方同意好友申请的时候才知道发起者的数据 struct RequestSenderInfo { //发起好友请求的玩家的数据 uint32_t rid; string name; uint32_t level; ... ... }; //我目前收到的好友申请 static map<uint32_t, vector<RequestSenderInfo>> m_applyList; //我发起的好友申请后收到的回应,其中FriendData就是回应者的数据; static map<uint32_t, vector<FriendData>> m_responseList; //我目前收到的好友删除申请。 static map<uint32_t, vector<uint32_t>> m_deleteList;
假设A玩家给B玩家发起了一条好友申请:
1.A玩家会操作m_applyList,将B玩家的Rid作为Key,打包自己A玩家的数据放入m_applyList中;
2.B玩家通过定时器或者前端请求的方式去查找m_applyList知道B玩家有了好友申请。
1.B玩家拒绝A玩家的好友申请:B玩家会操作m_responseList以A玩家作为Key,将拒绝的结果打包在FriendData的status中放入m_responseList;
2.B玩家同意A玩家的好友申请:B玩家会操作m_responseLIst以A玩家作为Key,将自己B玩家的数据和同意的结果打包进FriendData中放入m_responseList
3.A玩家通过定时器或者前端请求的方式去查找m_responseList知道了B玩家的是接受还是拒绝,以此来是否将B玩家放入自己的m_myFriend中;
当B玩家从好友列表中删除A玩家后:
1.B玩家会操作m_deleteList,以A玩家的Rid为Key,将自己B玩家的Rid放入m_deleteList中;
2.A玩家通过定时器或者前端请求的方式去查找m_deleteList发现了B玩家删除了自己,自己A玩家也从m_myFriend中删除B玩家。
以上就是好友系统的内存操作,简洁明了。A玩家不操作B玩家的内存数据,同理B玩家也不操作A玩家的内存数据,因此无需关心对方的数据是否已经从数据库载入内存;
数据库表的结构如下:
当B玩家同意好友的时候往数据库写两条数据。
一条是A玩家中B玩家的数据,一条是B玩家中A玩家的数据。这个设计感觉不是特别好,但是目前没有想出特别好的方法;
邮件系统实现:
邮件系统的实现有几个问题需要解决:
一、邮件Id的唯一性
二、如果每个玩家的内存单独维护一个邮件Id。就会遇到一个问题:排行榜发奖的时候,如果玩家的数据不在内存上,如何知道这个玩家邮件的Id。就要存在Load这个玩家档的情况。
三、全服邮件如何发给每一个玩家
我的实现是:普通邮件采用全服唯一邮件mailId的形式。系统邮件采用另一个全服唯一邮件sysMailId的形式。
系统邮件在数据库的存储位置是:以rid=服务器id的形式分数据库、指定sysMailId字段的形式存储到数据库表内;
数据结构设计为:
struct DataMail { uint32_t rid uint32_t mailId; uint32_t sysMailId; uint32_t readTs; uint32_t sendTime; char send[19]; char title[100]; char text[512]; char attachments[512]; }; static uint32_t nextSysMailId; static vector<DataMail> vSystemMail; static uint32_t nextMailId; map<uint32_t, DataMail> m_userMail;
对于问题一如何做到全服唯一的普通邮件mailId:
进程一启动的时候不可能加载本服所有邮件然后对最后一封邮件mailId++的形式来拿到最大的mailId;因此会在另一个表Attr表内rid=本服Id且type=xx存一个nextMailId,每个玩家在新增一封邮件的时候都会修改这个值。
如果发的是普通邮件(玩家通关某个关卡),会设置Rid、mailId且sysMailId=0存入数据库中;
如果普通邮件的发放玩家不在内存上时,由于普通邮件采用的是全服唯一的mailId,因此依然能指定Rid和mailId存入数据库内;
如果通过后台发的是全服(更新补偿、服务器异常补偿)邮件,会单独将rid设置为本服(例如1服为1),mailId和sysMailId相等存到数据库中;
玩家如何拿到这份全服邮件:
玩家登录Load档完自己邮件的数据以后会遍历系统邮件,判断自己是否有sysMailId的邮件,如果没有则走普通邮件的流程给自己的数据库表中添加一封邮件;
邮件数据库表的设计结构为: