/* 应用层的广播 与之前已在底层实现的广播不同 底层广播用于无例外的全服通知 但有许多虽非全服通知,但通知人数仍很多的消息,例如地图内通知或全战盟通知。 对于这类通知,如果使用全服通知,可以达到通知目地,但最终发出去的多余消息太多了, 而如果在欲发消息处一次大量遍历通知,则mapsrv-->gatesrv的通信也有大量重复的嫌疑,同时mapsrv的大量同时遍历也不妥当。 基本思路:1、mapsrv只用一个消息通知gate此类广播消息 2、gatesrv上此类广播消息由各个玩家稍后发送的其它消息分散稍带出去 与在底层实现的广播差异:应用层到底层仍有重复存在冗余。 其它:由于要发送不同类型的包,以及有加密/非加密等各种情形,本实现实际只优化处理了linux下加密时的广播包(NewSendPlayerPkgCrypt)。 即,windows下,直接发送给指定目标(地图内或同战盟) linux下,只有已进入加密通信状态玩家才会检测应用层广播(没进加密通信状态的玩家也不应该收到任何类型广播包) 或者???修改DealPkgBase,直接利用DealMapSrvMsg mapsrv(或其它srv)发消息MGAppBrocast{Type:mapbro|unionbro, TypeInfo:mapid|unionid, NormalMGTypeCmd:normalMGInfo's wCmd, normalMGInfo:char[]} gatesrv收到此消息后,直接保存MGAppBrocast pPlayer发正常消息时进行稍带检测,若符合条件,则直接调normalMGInfo的处理函数 这样做的话,原来的处理函数要再附带一个notiPlayer,这样可以省下重新Find self的开销并与其它普通消息的处理分开 同时,这也是因为做不到与普通消息完全共用处理函数,因为广播消息中的playerid没法填。 或者,这样的话就索性为每个有广播可能的消息都另写一个处理函数,同时另建命令字<-->处理函数hash表??? by dzj, 10.07.20 */ #include "Utility.h" #include "PkgProc/SrvProtocol.h" class CPlayer; //广播消息; struct AppBroMsg { static const unsigned int APPBRO_EXPIRE_TIME = 5000;//存在时间5秒,5秒内没有来取的人,则此消息不再发送; AppBroMsg() { StructMemSet( toBroMsg, 0, sizeof(toBroMsg) ); expireTime = 0;//过期时间 msgID = 0;//自身编号; } inline bool SetAppBroMsg( const MGAppBroCast& inBroMsg, unsigned int inMsgID ) { StructMemCpy( toBroMsg, &inBroMsg, sizeof(toBroMsg) ); expireTime = GetTickCount() + APPBRO_EXPIRE_TIME; msgID = inMsgID; return true; } inline bool IsExpire( const unsigned int inCurTime ) { unsigned int passed = inCurTime-expireTime; return passed < (1000*60);//两个都为无符号数; } inline unsigned int GetMsgID() { return msgID; } const MGAppBroCast& GetMGAppBro() { return toBroMsg; }; inline bool IsSendInfoAccord( unsigned short mapid, unsigned int unionid ) { if ( AppBroInfo::APPBRO_MAP == toBroMsg.broMsgInfo.broType ) { return (mapid == toBroMsg.broMsgInfo.mapID); } else { return (unionid == toBroMsg.broMsgInfo.unionID); } } MGAppBroCast toBroMsg;//待广播消息; unsigned int expireTime;//过期时间 unsigned int msgID;//自身编号; };//struct AppBroMsg //待广播消息容器; //以数组形式循环存放各类待广播消息,数组大小设为定期清除时间内可能到来的消息最大数量,如果超过最大大小,则忽略同时记日志; //所有各类广播消息分别编号,每次检查过后都将玩家的已广播号设为最新值,在检测函数内部,则首先比较已广播号,这样可以快速略过大部分无需广播的情形; //另外,各广播消息进数组时即设置过期时间,定期清除; class CAppBroMsgs { public: CAppBroMsgs( unsigned int innerSize, AppBroInfo::APPBRO_TYPE inBroType ) { m_broType = inBroType; m_innerSize = innerSize; m_arrBroMsgs = NEW AppBroMsg[m_innerSize]; m_stPos = 0;//有效元素起始位置 m_stMsgID = 0;//起始位置元素的ID号 m_endMsgID = 0;//最新有效元素ID号; m_endPos = 0; } ~CAppBroMsgs() { m_stPos = 0;//有效元素起始位置 m_stMsgID = 0;//起始位置元素的ID号 m_endPos = 0; if ( NULL != m_arrBroMsgs ) { delete [] m_arrBroMsgs; } } public: //添加一个新的待广播消息; bool AddNewBroMsg( const MGAppBroCast& broMsg ) { if ( broMsg.broMsgInfo.broType != m_broType ) { D_ERROR( "AddNewBroMsg, broType不一致(%d--%d)\n", broMsg.broMsgInfo.broType, m_broType ); return false; } if ( GetValidEleNum() >= (m_innerSize-1) ) { //最多存m_innerSize-1个元素; CheckExpiredMsg();//尝试释放一次; if ( GetValidEleNum() >= (m_innerSize-1) ) { //仍然放不下,则添加失败; D_ERROR( "AddNewBroMsg失败,消息类型%d\n", broMsg.broMsgCmd ); return true; } } ++m_endMsgID;//1-~40亿间循环; m_arrBroMsgs[m_endPos].SetAppBroMsg( broMsg, m_endMsgID ); ++m_endPos; if ( m_endPos >= m_innerSize ) { m_endPos = 0; } return true; }//bool AddNewBroMsg( const MGAppBroCast& broMsg ) //遍历所有有效元素,删去所有过期元素,修改m_stPos与m_stMsgID; bool CheckExpiredMsg() { if ( GetValidEleNum() <=0 ) { return true;//无待广播消息; } unsigned int curTime = GetTickCount(); //遍历直至找到一个未过期元素时停止; if ( m_endPos >= m_stPos ) { //m_stPos至m_endPos; unsigned int i = m_stPos; for ( i; i= m_stPos ) { return (m_endPos-m_stPos); } else { return (m_innerSize-m_stPos)+m_endPos; } } private: unsigned int m_innerSize;//内部容量大小,初始化后不再改变; AppBroInfo::APPBRO_TYPE m_broType;//广播类型; private: AppBroMsg* m_arrBroMsgs; unsigned int m_stPos;//有效元素起始位置 unsigned int m_stMsgID;//起始位置元素的ID号 unsigned int m_endMsgID;//最新有效元素ID号; unsigned int m_endPos; }; class CAppBrocast { private: static const unsigned int APPBRO_ARRSIZE = 10; public: static bool InitAppBrocast() { m_pMapBroMsgs = NEW CAppBroMsgs( APPBRO_ARRSIZE, AppBroInfo::APPBRO_MAP );//地图内广播消息数组; m_pUnionBroMsgs = NEW CAppBroMsgs( APPBRO_ARRSIZE, AppBroInfo::APPBRO_UNION );//战盟内广播消息数组; return true; } static bool ReleaseAppBrocast() { if ( NULL != m_pMapBroMsgs ) { delete m_pMapBroMsgs; } m_pMapBroMsgs = NULL; if ( NULL != m_pUnionBroMsgs ) { delete m_pUnionBroMsgs; } m_pUnionBroMsgs = NULL; return true; } static bool AddAppBrocastMsg( const MGAppBroCast& broMsg ) { if ( ( NULL == m_pMapBroMsgs ) || ( NULL == m_pUnionBroMsgs ) ) { D_ERROR( "AddAppBrocastMsg, ( NULL == m_pMapBroMsgs ) || ( NULL == m_pUnionBroMsgs )\n" ); return false; } if ( AppBroInfo::APPBRO_MAP == broMsg.broMsgInfo.broType ) { m_pMapBroMsgs->AddNewBroMsg( broMsg ); } else if ( AppBroInfo::APPBRO_UNION == broMsg.broMsgInfo.broType ) { m_pUnionBroMsgs->AddNewBroMsg( broMsg ); } else { D_ERROR( "AddAppBrocastMsg, 不可能错误,unknown m_broType %d\n", broMsg.broMsgInfo.broType ); return false; } return true; } static bool CheckPlayerBrocast( CPlayer* pPlayer ) { if ( NULL == pPlayer ) { D_ERROR( "CheckPlayerBrocast, NULL == pPlayer\n" ); return false; } if ( ( NULL == m_pMapBroMsgs ) || ( NULL == m_pUnionBroMsgs ) ) { D_ERROR( "AddAppBrocastMsg, ( NULL == m_pMapBroMsgs ) || ( NULL == m_pUnionBroMsgs )\n" ); return false; } m_pMapBroMsgs->CheckPlayerBroMsg( pPlayer ); m_pUnionBroMsgs->CheckPlayerBroMsg( pPlayer ); return true; } static CAppBroMsgs* m_pMapBroMsgs;//地图内广播消息数组; static CAppBroMsgs* m_pUnionBroMsgs;//战盟内广播消息数组; };