#pragma once /******************************************** * * 排行榜类,主要导出给脚本使用 * *********************************************/ class CRankingMgr; class CRanking; #define RANKING_ITEM_CHECUSUM 0xFFEECC11 #define RANKING_CHECUSUM 0xFFEECC00 /// 排行榜项,每项表示一个用户的分值,每个排行榜由多个排行榜项组成 class CRankingItem { friend class CRanking; public: CRankingItem(CRanking *pRank) { nColCount = 0; sCd = NULL; nId = 0; nPoint = 0; nMaxCol = 0; pParent =pRank; nParam = 0; nParam1 = 0; nChecuSum = RANKING_ITEM_CHECUSUM; } /// 设置父亲的节点的指针,用于查找位置 VOID SetParent(CRanking *pRank) { if(pParent != NULL) return; pParent = pRank; } /// 是否是合法的 bool IsValid() {return nChecuSum ==RANKING_ITEM_CHECUSUM;} #define CHECK_RANK_ITEM_VALID() \ if(nChecuSum !=RANKING_ITEM_CHECUSUM) {\ OutputMsg(rmError,"%s,%d,ranking error",__FUNCTION__,__LINE__);\ for(int i=0; i< nColCount;i ++)\ {\ if(sCd[i])\ {\ OutputMsg(rmError,"column %d:%s",i,sCd[i]);\ }\ }\ return; } #define CHECK_RANK_ITEM_VALID_RETSTR() \ if( nChecuSum != RANKING_ITEM_CHECUSUM) {OutputMsg(rmError, "%s,%d,ranking get index[%d] str ret error", __FUNCTION__, __LINE__, nIndex);\ return "-"; } void Destroy() { CHECK_RANK_ITEM_VALID() if (sCd) { for (int i = 0; i < nColCount; i++) { if(sCd[i]) { CRankingMgr::m_pAllocator->FreeBuffer(sCd[i]); sCd[i]=NULL; } } CRankingMgr::m_pAllocator->FreeBuffer(sCd); nColCount = 0; sCd = NULL; nId = 0; nPoint = 0; nMaxCol = 0; } } virtual ~CRankingItem() { Destroy(); nChecuSum =0; pParent =NULL; } /// 设置列数 void SetColCount(INT_PTR nCount) { CHECK_RANK_ITEM_VALID() if (nCount > nMaxCol) { INT_PTR nSize = sizeof(char*)*nCount; char** ss = (char**)CRankingMgr::m_pAllocator->AllocBuffer(nSize); ZeroMemory(ss,nSize); if (sCd) { memcpy(ss,sCd,sizeof(char*)*nMaxCol); CRankingMgr::m_pAllocator->FreeBuffer(sCd); } sCd = ss; nMaxCol = (BYTE)nCount; } nColCount = (BYTE)nCount; } void SetSub(INT_PTR nIndex, LPCSTR sData); void SetSub(INT_PTR nIndex, unsigned int nValue); /// 在指定的列前增加一列.-1表示在最后插入.bUnique列名是否唯一,true则唯一 void AddColumn(LPCSTR sData,INT_PTR nIndex=-1, bool bUnique=false); /// 设置列的标题 void SetColumnTitle(LPCSTR sData, INT_PTR nIndex); /// 删除某列的数据 void RemoveColumn(INT_PTR nIndex); void SetId(unsigned int id); inline unsigned int GetId() { return nId;} inline int GetPoint() { return nPoint;} inline int GetParam() { return nParam;} inline int GetParam1() { return nParam1;} void SetPoint(int point); void SetParam(int param); void SetParam1(int param); /// 获取这个节点在管理器里的索引 int GetIndex(); //inline void SetIndex(int index) {CHECK_RANK_ITEM_VALID() nIndex = index;} inline int GetColCount() { return nColCount;} /// 获取某列的数据 inline LPCSTR GetSubData(INT_PTR nIndex) { CHECK_RANK_ITEM_VALID_RETSTR() if (sCd && nIndex >=0 && nIndex < nColCount) { return sCd[nIndex]?sCd[nIndex]:"-"; }else { return "-"; } } private: unsigned int nId; ///< 唯一值,可以是角色id或者帮派id等 int nPoint; ///< 分数,排行榜根据这个排序 int nParam; //额外参数 int nParam1; //额外参数1 char** sCd; ///< 每列的数据内容,每列都是字符串类型,主要用于显示 BYTE nColCount; ///< 列的数目 BYTE nMaxCol; ///< 最大列 //int nIndex; ///< 自身在排行榜的位置,0是第一位 CRanking * pParent; ///< 获得其指针 int nChecuSum; ///< 校验码 DECLARE_OBJECT_COUNTER(CRankingItem) }; /// 排行榜类 class CRanking: public CBaseScriptExportObject { public: struct tagIndex { public: int id; CRankingItem* pItem; }; /// 排行可见类型 enum RankVisibleType { enVisible_All, ///< 世界所有玩家 enVisible_SameCamp, ///< 同阵营玩家 enVisible_SameGuild, ///< 同公会玩家 enVisible_SameTeam, ///< 同队伍 }; /// 排行可见参数 struct RankVisibleParam { union { int nCampId; ///< 阵营Id unsigned int nGuildId; ///< 公会id int nTeamId; ///< 队伍Id }data; }; public: CRanking():m_Title(this){} CRanking(LPCSTR sName,INT_PTR nMax,bool boDisp, INT_PTR nMaxBroadCount = 0); virtual ~CRanking(); #define CHECK_RANK_VALID() \ if(m_nCheckSum != RANKING_CHECUSUM) { OutputMsg(rmError,"rank=%s,%s,%d,CRanking is error !!!",m_szFile,__FUNCTION__,__LINE__);\ return; } const static int s_nDur = 120 * 60 * 1000; ///< 存盘,120分钟只存1次 public: //继承基类,释放资源,如果引用计数器没到0,不能释放内存 //virtual int release(); virtual void destroy(); /* * Comments:从文件读取排行榜数据,如果原来已经读取过一次,会全部清除掉;文件名根据m_szName * @Return bool:成功读取返回true */ bool Load(LPCSTR sFile = nullptr); /// 排行榜是否合法 bool IsValid() { if(m_nCheckSum != RANKING_CHECUSUM) { //OutputMsg(rmError,"rank=%s,name=%s,CRanking is error !",m_szFile,m_szName); return false; } return true; } /* * Comments:增加一列 * Param LPCSTR sTitle:标题 * Param INT_PTR nIndex:在本索引前插入,-1表示最后增加 * @Return bool: */ void AddColumn(LPCSTR sTitle, INT_PTR nIndex, bool bUnique=false); /// 获取一个子节点的位置 inline int GetNodeIndex(CRankingItem * pItem) { for(int i=0;i & GetList(){return m_ItemList;} //获取当前排行榜的行数 inline int GetItemCount() {return (int)m_ItemList.count();} /* * Comments:删除指定的id * Param INT_PTR nId: * @Return void: */ void RemoveId(INT_PTR nId); ////设置上榜的最多人数,要重新排序 //void SetRankMax(INT_PTR nCount); /* * Comments:根据索引获取某一项,用户改变列字符串的数值,不能在这里改变分值 * Param INT_PTR nIndex:在排行榜的位置 * @Return CRankingItem*:位置不正确会返回NULL */ CRankingItem* GetItem(INT_PTR nIndex); /* * Comments:把该项插到排行榜中,向上更新位置 * Param CRankingItem * pItem: * @Return INT_PTR:返回在排行榜中的位置 */ //INT_PTR UpdateUpPos(CRankingItem* pItem); /* * Comments:把该项插到排行榜中,向下更新位置 * Param CRankingItem * pItem: * @Return INT_PTR:返回在排行榜中的位置 */ //INT_PTR UpdateDownPos(CRankingItem* pItem); /* * Comments:获取某个id在排行榜中的位置,第一位是0,不在这个 * Param INT_PTR nId: * @Return INT_PTR: */ INT_PTR GetIndexFromId(UINT_PTR nId); /* * Comments:根据id获取指针和索引 * Param INT_PTR nId:ID * Param INT_PTR & idIndex:返回的索引 * @Return CRankingItem*:返回的数据指针 */ CRankingItem* GetPtrFromId(INT_PTR nId,INT_PTR& idIndex); /* * Comments:根据id获取指针 * Param INT_PTR nId:数据ID * @Return CRankingItem*:数据的指针 */ CRankingItem* GetPtrFromId(UINT_PTR nId); //在榜外找个最大的,上榜 //CRankingItem* FindMaxItem(); inline LPCSTR GetName(){return m_szName;} inline int GetMax() { return m_nMaxBroadCount > 0 ? m_nMaxBroadCount: m_RankMax; } inline int GetColCount() { return m_Title.GetColCount();} /* * Comments:设置某列是否显示在客户端 * Param INT_PTR nIndex:列数 * Param bool disp:是否显示 * @Return void: */ void SetColDisplay(INT_PTR nIndex, bool disp); /* * Comments:判断该列是否显示 * Param INT_PTR nIndex:列索引 * @Return bool:显示返回true */ bool isColDisplay(INT_PTR nIndex); //是否发到客户端显示 inline bool IsDisplay(CActor *pActor) { bool bVisible = true; switch (m_rankVisibleType) { case enVisible_All: bVisible = true; break; case enVisible_SameCamp: { bVisible =true; break; } break; // 这些暂时还没实现! 以后需要的时候,按照上面的方式实现下就行了! case enVisible_SameGuild: case enVisible_SameTeam: break; } return m_Display && bVisible; } //设置在客户端显示的名称 inline void SetDisplayName(LPCSTR sName) { if (sName) { _asncpytA(m_szCnName,sName); } else { m_szCnName[0] = 0; } } //获取在客户端显示的名称 inline LPCSTR GetDisplayName() { return m_szCnName; } //获取标题 inline CRankingItem& GetTitle() { return m_Title; } inline LPCSTR GetIdTitle() { return m_idTitle;} inline void SetIdTitle(LPCSTR sName) { if (sName) { _asncpytA(m_idTitle,sName); } else { m_idTitle[0] = 0; } } inline LPCSTR GetPointTitle() { return m_pointTitle; } inline void SetPointTitle(LPCSTR sName) { if (sName) { _asncpytA(m_pointTitle,sName); } else { m_pointTitle[0] = 0; } } inline void SetRankVisibleInfo(RankVisibleType vt, RankVisibleParam param) { m_rankVisibleType = vt; m_rankVisibleParam = param; } //是否有数据改变 inline bool IsModify() { return m_boModify; } //设置是否改变的标记 inline void SetModify( bool flag=true) {m_boModify =flag;} inline void SetEmptySave(bool flag=false) { m_bRmptySave = flag;} //更新索引中的id //void UpdateTagId(int nOldId,int nNewId); /* * Comments:根据索引进行交换位置,!!!!!如果是根据分数排名的是无法交换的,将失败 * Param INT_PTR nPos1:位置1 * Param INT_PTR nPos2:位置2 * @Return bool:成功返回true,否则返回false */ bool SwapItem(INT_PTR nPos1,INT_PTR nPos2 ); //将1个项插队 bool JumpQueue(CRankingItem *pItem,INT_PTR nNewPos); //设置属性排行榜配置数据 nRankPropIndex:属性配置表对应的配置索引 从0开始 void SetRankPropConfig(int nRankPropIndex); /* * Comments:更新带属性的排行榜的属性 * Param CRankingItem * pItem:指针 * Param INT_PTR nIndex:位置 * Param bool bClear:是否是删除排行榜里的项 * @Return void: */ void UpdateRankProp(CRankingItem *pItem,INT_PTR nIndex, bool bClear=false); //把属性与排行榜关联起来 void AtachRankProp(LPCSTR sName); //把属性与排行榜去掉关联 void DetachhRankProp(LPCSTR sName); /* * Comments:找一个插入的位置 * Param int nPoint:分数点 * @Return INT_PTR:寻找一个插入的位置 */ INT_PTR GetInsertPos(int nPoint); //是否排序好了,默认的是按正常的降序排列的,特殊时候,不需要排序的,这里要做1个标记 inline bool isRanked() { return m_isRanked; } //设置是否被排序好了 inline void SetRanked(bool flag) { m_isRanked = flag; } //获取文件的名字 char * GetFileName() { return m_szFile; } void SetSaveFlag(bool flag) { m_canSave = flag; } void PushToPack(CActor* pActor, int nNum, CDataPacket* pDataPack); void PushRankToPack(int nNum, CDataPacket* pDataPack); void PushEspecialRankToPack(int nNum, CDataPacket* pDataPack); private: //保存每项数据到文件中 void SaveItem( CRankingItem* pItem, CStringBuff &buff ); //把新id插入到索引中 //void InsertId( tagIndex &IndexItem ); private: char m_szName[32]; //排行榜的名字,脚本通过这个名字访问排行榜,同时也是用这个名字作为文件保存 char m_szCnName[32]; //排行榜的中文名,主要显示在客户端的 char m_idTitle[32]; //id的标题文字 char m_pointTitle[32]; //分值的标题文字 CRankingItem m_Title; //这个项用来表示标题 CVector m_ItemList; //保存所有的列表 int m_columnCount; //列的个数 //CVector m_index; //索引表,根据id排序,查找时根据二分查找 int m_RankMax; //上榜的最多人数 int m_nMaxBroadCount; //广播给客户端次数 BYTE m_ColDisplay[255]; //表示第几列是否显示在客户端,0表示显示,默认是0,非0表示不显示 bool m_Display; //表示是否显示 bool m_isRanked; //是否被排序好的 RankVisibleParam m_rankVisibleParam; // 排行榜可见类型相关的参数。例如阵营相关的排行,这里存放可见排行的阵营ID RankVisibleType m_rankVisibleType; // 排行榜可见类型 static const TCHAR* const m_sClassName; ///< 不知道有啥用 bool m_boModify; //是否修改过数据 char m_szFile[256]; //保存的文件 bool m_bRmptySave; //当排行榜数据为空时,是否保存排行榜文件 //排行榜属性添加 int m_nRankPropIndex; //属性排行榜配置索引 int m_nRankPropMax; //最大属性排行榜排行名额 int m_nRankPropValue; //属性榜最少值限制 unsigned int m_nCheckSum; //校验和 TICKCOUNT m_saveTick; //上次存盘的时间 bool m_canSave; // 是否存盘的标识 public: void SendCrossServerToPack(int nNum, CDataPacket& pack); };