| | |
| | | import IpyGameDataPY
|
| | | import DBDataMgr
|
| | | import CommFunc
|
| | | import DBComm
|
| | |
|
| | | import json
|
| | | import time
|
| | |
|
| | | class BillboardData():
|
| | | ## 榜单数据
|
| | |
|
| | | def __init__(self, dbData=None):
|
| | | self.__dbData = DBStruct.tagDBBillboard() if not dbData else dbData
|
| | | self.__dataDict = DBComm.UserDataDict(self.__dbData, "UserData", "DataLen", True)
|
| | | return
|
| | |
|
| | | def GetType(self): return self.__dbData.BillboardType
|
| | |
| | | def SetCmpValue2(self, cmpValue2): self.__dbData.CmpValue2 = cmpValue2
|
| | | def GetCmpValue3(self): return self.__dbData.CmpValue3
|
| | | def SetCmpValue3(self, cmpValue3): self.__dbData.CmpValue3 = cmpValue3
|
| | | def GetUserData(self): return self.__dbData.UserData
|
| | | def SetUserData(self, userData):
|
| | | if not isinstance(userData, str):
|
| | | if isinstance(userData, dict) or isinstance(userData, list):
|
| | | userData = json.dumps(userData, ensure_ascii=False)
|
| | | else:
|
| | | userData = ""
|
| | | userData = userData.replace(" ", "")
|
| | | self.__dbData.UserData = userData
|
| | | self.__dbData.DataLen = len(self.__dbData.UserData)
|
| | | return
|
| | | def GetTime(self): return self.__dbData.Time
|
| | | def SetTime(self, updTime): self.__dbData.Time = updTime
|
| | | def GetUserDict(self): return self.__dataDict.GetData()
|
| | | def GetUserData(self): return self.__dataDict.ToString()
|
| | | def SetUserData(self, value): self.__dataDict.SetData(value)
|
| | | def GetBuffer(self): return self.__dbData.getBuffer()
|
| | | def Clear(self): return self.__dbData.clear()
|
| | | def Copy(self, setType=None):
|
| | |
| | | copyData.SetCmpValue(self.GetCmpValue())
|
| | | copyData.SetCmpValue2(self.GetCmpValue2())
|
| | | copyData.SetCmpValue3(self.GetCmpValue3())
|
| | | copyData.SetTime(self.GetTime())
|
| | | return copyData
|
| | |
|
| | | TempBillData = BillboardData()
|
| | |
|
| | | class Billboard():
|
| | | ## 某个排行榜类
|
| | |
|
| | |
| | | self.__billboardList = [] # [BillboardData, ...]
|
| | | self.__idIndexDict = {} # {id:index, ...}
|
| | | self.__idOrderDict = {} # {id:名次, ...}
|
| | | self.__orderRuleList = None # 指定排名所需值规则列表 [[order, needCmpValue], ...]
|
| | | self.__sortDelay = False # 是否需要延迟排序
|
| | | self.__orderRuleList = None # 指定排名所需值规则列表 [[order, needCmpValue], ...]
|
| | | |
| | | # 【层级模式】机器人填充数据模式,一般如果机器人数据量比较大的情况下实用该模式,如群英榜
|
| | | # 层级模式下,因为机器人数量一般比较大,比如1万,为了节约内存占用及遍历排序等效率,填充的机器人数据不插入实际的 __billboardList 里
|
| | | self.__orderRuleByLayer = False # 按固定层级模式,第1层即第1名,CmpValue直接使用名次值,方便排序用,由功能自行管理好CmpValue值
|
| | | self.__layerIDList = [] # 层级ID列表,顺序即排名,索引0为第1名,ID包含填充的机器人ID,初始的数据由功能按规则设置填充的机器人ID
|
| | | return
|
| | |
|
| | | def GetType(self): return self.__billboardType
|
| | | def GetGroupValue1(self): return self.__groupValue1
|
| | | def GetGroupValue2(self): return self.__groupValue2
|
| | |
|
| | | def ClearData(self):
|
| | | def ClearData(self, drName=""):
|
| | | if not self.__billboardList:
|
| | | return
|
| | | GameWorld.Log("Billboard ClearData billboardType=%s,groupValue1=%s,groupValue2=%s,dataCount=%s" |
| | | % (self.__billboardType, self.__groupValue1, self.__groupValue2, len(self.__billboardList)))
|
| | | if GameWorld.IsCrossServer():
|
| | | self.SaveDRData("Clear")
|
| | | GameWorld.Log("Billboard ClearData billboardType=%s,groupValue1=%s,groupValue2=%s,dataCount=%s,drName=%s" |
| | | % (self.__billboardType, self.__groupValue1, self.__groupValue2, len(self.__billboardList), drName))
|
| | | if drName or GameWorld.IsCrossServer():
|
| | | self.SaveDRData(drName if drName else "Clear")
|
| | | self.__billboardList = [] # [BillboardData, ...]
|
| | | self.__idOrderDict = {} # {id:名次, ...}
|
| | | self.__idIndexDict = {}
|
| | |
| | | def SortData(self):
|
| | | GameWorld.DebugLog("榜单排序: billboardType=%s,groupValue1=%s,groupValue2=%s,dataCount=%s"
|
| | | % (self.__billboardType, self.__groupValue1, self.__groupValue2, len(self.__billboardList)))
|
| | | self.__billboardList.sort(key=lambda b: (b.GetCmpValue(), b.GetCmpValue2(), b.GetCmpValue3()), reverse=True)
|
| | | if self.__orderRuleByLayer:
|
| | | self.__billboardList.sort(key=lambda b: (-b.GetCmpValue(), -b.GetTime()), reverse=True)
|
| | | else:
|
| | | self.__billboardList.sort(key=lambda b: (b.GetCmpValue(), b.GetCmpValue2(), b.GetCmpValue3(), -b.GetTime()), reverse=True)
|
| | | self.__idOrderDict = {} # 排序后重置,下次查询时更新并缓存
|
| | | self.__idIndexDict = {}
|
| | | self.__sortDelay = False
|
| | | if self.__orderRuleByLayer:
|
| | | self.__fixAndFillLayer()
|
| | | return
|
| | |
|
| | | def SetSortDelay(self):
|
| | |
| | | if not self.__sortDelay:
|
| | | return
|
| | | self.SortData()
|
| | | return
|
| | | return True
|
| | |
|
| | | def AddNewBillboardData(self):
|
| | | def AddNewBillboardData(self, dataID):
|
| | | newData = None
|
| | | if self.IsFull():
|
| | | return newData
|
| | |
| | | newData.SetType(self.__billboardType)
|
| | | newData.SetGroupValue1(self.__groupValue1)
|
| | | newData.SetGroupValue2(self.__groupValue2)
|
| | | newData.SetID(dataID)
|
| | | self.AddBillboardData(newData)
|
| | | return newData
|
| | |
|
| | |
| | | "Value5":bData.GetValue5(), "Value6":bData.GetValue6(),
|
| | | "Value7":bData.GetValue7(), "Value8":bData.GetValue8(),
|
| | | "CmpValue":bData.GetCmpValue(), "CmpValue2":bData.GetCmpValue2(),
|
| | | "CmpValue3":bData.GetCmpValue3(), "UserData":bData.GetUserData()}
|
| | | "CmpValue3":bData.GetCmpValue3(), "Time":bData.GetTime(), "UserData":bData.GetUserData()}
|
| | | DataRecordPack.SendEventPack(eventTypeStr, dataDict)
|
| | |
|
| | | return
|
| | |
| | | billboardIndex += 1
|
| | | for order, billboardData in enumerate(self.__billboardList, 1):
|
| | | self.__idIndexDict[billboardData.GetID()] = order - 1
|
| | | # 按层级固定排位的
|
| | | elif self.__orderRuleByLayer:
|
| | | if not self.SortDelayDo():
|
| | | self.__fixAndFillLayer()
|
| | | else:
|
| | | for order, billboardData in enumerate(self.__billboardList, 1):
|
| | | self.__idOrderDict[billboardData.GetID()] = order
|
| | | self.__idIndexDict[billboardData.GetID()] = order - 1
|
| | | return self.__idOrderDict
|
| | |
|
| | | def IsOrderRuleByLayer(self): return self.__orderRuleByLayer
|
| | | def SetOrderRuleByLayer(self, layerRobotIDList):
|
| | | '''设置排名规则为层级模式,即一个名次一个坑位层级,该模式支持机器人填充数据
|
| | | 该模式下 CmpValue 固定为 MaxCount - 名次 + 1
|
| | | @param layerRobotIDList: 填充的机器人ID排名列表
|
| | | '''
|
| | | self.__orderRuleByLayer = True
|
| | | self.__layerIDList = layerRobotIDList
|
| | | self.SortData() # 强制排序处理
|
| | | return
|
| | | def SetLayerIDList(self, layerIDList): self.__layerIDList = layerIDList
|
| | | def GetLayerIDList(self): return self.__layerIDList
|
| | | def __fixAndFillLayer(self):
|
| | | ## 检查修正并填充满最终层级ID顺序
|
| | | maxCount = self.GetMaxCount()
|
| | | layerIDCnt = len(self.__layerIDList)
|
| | | if layerIDCnt < maxCount:
|
| | | self.__layerIDList.extend([0]*(maxCount-layerIDCnt)) # 先用0填充,确保长度一致
|
| | | |
| | | # 先检查修正真实榜单数据的排名值,确保唯一
|
| | | orderIDDict = {}
|
| | | for index, billboardData in enumerate(self.__billboardList):
|
| | | dataID = billboardData.GetID()
|
| | | order = billboardData.GetCmpValue()
|
| | | if order > maxCount:
|
| | | # 超过的默认不上榜,后面的数据不再处理,外层的数据使用替换的模式,故理论上不会有多余的数据,这里暂做个防范
|
| | | break
|
| | | if order <= 0:
|
| | | # 没有名次数据的也不上榜,但先跳过继续处理后面的数据
|
| | | continue
|
| | | |
| | | while 1 <= order <= maxCount:
|
| | | if order not in orderIDDict:
|
| | | orderIDDict[order] = dataID
|
| | | self.__idOrderDict[dataID] = order
|
| | | self.__idIndexDict[dataID] = index
|
| | | break
|
| | | # 重复的数据修正层级比较值,确保唯一
|
| | | order += 1
|
| | | billboardData.SetCmpValue(order)
|
| | | |
| | | for index, layerID in enumerate(self.__layerIDList):
|
| | | order = index + 1
|
| | | dataID = layerID
|
| | | if order in orderIDDict:
|
| | | dataID = orderIDDict[order]
|
| | | self.__layerIDList[index] = dataID
|
| | | |
| | | return
|
| | | |
| | | def SetOrderRuleList(self, orderRuleList):
|
| | | ## 排名所需值规则列表
|
| | | # @param orderRuleList: 排名所需值规则列表 [[order, needCmpValue], ...]
|