#!/usr/bin/python # -*- coding: utf-8 -*- # @todo: 区服总览(当前服生态) import CommFunc import DBOperate import ConfigParser import CTGOKSort import operator import datetime import time import os import copy import mylog import logging cfg = ConfigParser.ConfigParser() cfg.read("../../InterfaceConfig.php") ServerPath = cfg.get("ServerInfo", "ServerPath") class CrossPlayer(): def __init__(self): self.playerLV = 0 self.fightPower = 0 return def CrossServerViewInfo(argvDict): ''' 按跨服功能分区查询: 1、活跃玩家人数(选取时间内上线过)总合 2、活跃玩家人数的等级分布(1~299,之后按每50级一个跨段) ''' crossMapDict = {(32010, 32011, 32012, 32013, 32014, 32015, 32016, 32017, 32018, 32019):_(u"跨服天梯"), (32020, 32021, 32022, 32023, 32024, 32025, 32026, 32027, 32028, 32029):_(u"蓬莱仙境"), (32080, 32081, 32082, 32083, 32084, 32085, 32086, 32087, 32088, 32089):_(u"魔化之地"), (32070, 32071, 32072, 32073, 32074, 32075, 32076, 32077, 32078, 32079, 33000, 33001, 33002, 33003, 33004, 33005, 33006, 33007, 33008, 33009):_(u"跨服排位赛"), (32060, 32061, 32062, 32063, 32064, 32065, 32066, 32067, 32068, 32069):_(u"古神战场"), } loginZonePlayerDict = {k:{} for k in crossMapDict.keys()} startDate = argvDict.get("startDate", "") endDate = argvDict.get("endDate", "") CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc_CrossServerViewInfo, parseLineFunc_CrossServerViewInfo, loginZonePlayerDict) spaceStr = "  "; printStr = "" printStr += "%s:%s%s" % (_(u"开始日期"), spaceStr, startDate); printStr += "%s~%s" % (spaceStr, spaceStr) if endDate: printStr += "%s" % (endDate) else: printStr += _(u"今天") printStr += "
" for mapKey, zonePlayerDict in loginZonePlayerDict.items(): mapName = crossMapDict.get(mapKey, str(mapKey)) printStr += __PrintCrossFuncViewInfo(zonePlayerDict, mapName, spaceStr) printStr += "
" print printStr return def __PrintCrossFuncViewInfo(pkZonePlayerDict, funcName, spaceStr): printStr = "" printStr += "
【%s】
" % funcName dataList = [] tdList = ["zoneID", "accIDCount"] topFPCount = 10 # 前10战力 lvRangeList = [300] + range(350, 1000, 50) lvTitleList = [] accIDTotal = 0 lvTotalCountDict = {} topFPPlayerList = [] for zoneID, playerDict in pkZonePlayerDict.items(): playerList = playerDict.values() playerList.sort(key=operator.attrgetter("fightPower"), reverse=True) accIDCount = len(playerDict) # 总人数 lvCountDict = {} # 等级分布人数 fightPowerList = [] for player in playerList: playerLV = player.playerLV for lv in lvRangeList: if playerLV < lv: lvCountDict[lv] = lvCountDict.get(lv, 0) + 1 break if len(fightPowerList) < topFPCount: fightPowerList.append(player) topFPPlayerList.extend(fightPowerList) for lv in lvRangeList: if lv in lvCountDict: continue if lv > max(lvCountDict): break lvCountDict[lv] = 0 for lv, count in lvCountDict.items(): if lv not in lvTitleList: lvTitleList.append(lv) lvTotalCountDict[lv] = lvTotalCountDict.get(lv, 0) + count accIDTotal += accIDCount tableInfo = {"zoneID":zoneID, "accIDCount":accIDCount, "topFightPower":getSortDataStr(fightPowerList, "fightPower", spaceStr)} tableInfo.update(lvCountDict) dataList.append(tableInfo) lvTitleList.sort() tdList.extend(lvTitleList) tdList.extend(["topFightPower"]) # 最后一行汇总信息 topFPPlayerList.sort(key=operator.attrgetter("fightPower"), reverse=True) totalInfo = {"zoneID":_(u"总"), "accIDCount":accIDTotal, "topFightPower":getSortDataStr(topFPPlayerList[:topFPCount], "fightPower", spaceStr)} totalInfo.update(lvTotalCountDict) dataList.append(totalInfo) styleInfo = {"zoneID":{"title":_(u"分区ID")}, "accIDCount":{"title":_(u"总活跃")}, "topFightPower":{"align":"left", "title":_(u"战力榜")}, } for lv in lvTitleList: styleInfo[lv] = {"title":"LV < %s" % lv} printStr += CommFunc.editTableHtml(dataList, tdList, "", styleInfo, printCount=False) # printStr += "

" return printStr def checkDrFileNeedParseFunc_CrossServerViewInfo(drFileName, *parseArgs, **kv): ''' 检查流向是否需要处理 @param drFileName: 流向文件名 xxx_日期.txt @param *parseArgs: 自定义参数 @return: isNeed, checkNeedParseRetInfo ''' isNeed = drFileName.startswith("LogInOut_") return isNeed, None def parseLineFunc_CrossServerViewInfo(drName, dateStr, checkNeedParseRetInfo, line, *parseArgs, **kv): ''' 解析流向行内容 @param drName: 流向名 @param dateStr: 对应日期字符串 @param checkNeedParseRetInfo: checkDrFileNeedParseFunc 返回的内容 @param line: 本行内容 @param *parseArgs: 自定义参数 ''' loginZonePlayerDict = parseArgs[0] drDict = eval(line) # 统计累计登录 if drName == "LogInOut": if drDict["Type"] != "login": return if "RegCrossZoneID" not in drDict: return accID = drDict["AccID"] mapID = drDict["mapID"] zonePlayerDict = None for mapKey in loginZonePlayerDict.keys(): if mapID in mapKey: zonePlayerDict = loginZonePlayerDict[mapKey] break if zonePlayerDict == None: mapKey = (mapID,) loginZonePlayerDict[mapKey] = {} zonePlayerDict = loginZonePlayerDict[mapKey] #playerName = drDict["Name"] playerLV = drDict["PlayerLV"] fightPower = drDict["fightPower"] zoneID = drDict["RegCrossZoneID"] if zoneID not in zonePlayerDict: zonePlayerDict[zoneID] = {} if accID not in zonePlayerDict[zoneID]: player = CrossPlayer() zonePlayerDict[zoneID][accID] = player player = zonePlayerDict[zoneID][accID] player.playerLV = playerLV player.fightPower = fightPower return def getServerViewInfo(argvDict): ## 非跨服服务器总览 onlineMaxInfo = {} loginAccIDList = [] orderTypeCTGOKInfo = {} createRoleInfo = [0, 0] # [创角人数, 首登人数] newCTGAccIDInfo = {} startDate = argvDict.get("startDate", "") endDate = argvDict.get("endDate", "") CommFunc.loopMainServerDR(cfg, startDate, endDate, argvDict, checkDrFileNeedParseFunc_ServerViewInfo, parseLineFunc_ServerViewInfo, onlineMaxInfo, loginAccIDList, orderTypeCTGOKInfo, createRoleInfo, newCTGAccIDInfo) # 以下榜单数据有一定延迟,非实时排行数据,存档时间5分钟 # 合并数据 - 战力榜 topOrderCount = CommFunc.toInt(argvDict.get("topOrderCount"), 3) dboper = DBOperate.DBOper(ServerPath) col = dboper.db["tagDBPlayer"] spec = {} fields = {'_id':0, "FightPower":1, "FightPowerEx":1} fightPowerRet = col.find(spec, fields).sort([("FightPowerEx", -1), ("FightPower", -1)]).limit(topOrderCount) # 合并数据 - 等级榜 fields = {'_id':0, "LV":1} LVRet = col.find(spec, fields).sort([("LV", -1)]).limit(topOrderCount) # 实际排行 - 战力 col = dboper.db["tagDBBillboard"] spec = {"Type":0} fields = {'_id':0, "CmpValue":1, "CmpValue2":1} billboard_FightPowerRet = col.find(spec, fields).sort([("CmpValue", -1), ("CmpValue2", -1)]).limit(topOrderCount) isFightPowerExVer = False # 战力是否超过20E的版本 for fpData in copy.deepcopy(billboard_FightPowerRet): if fpData["CmpValue2"] > 0: # 理论上只要有数据中比较值2大于0就是超过20E的版本,比较值2为求余亿的部分 isFightPowerExVer = True break # 实际排行 - 等级 spec = {"Type":4} fields = {'_id':0, "CmpValue":1} billboard_LVRet = col.find(spec, fields).sort([("CmpValue", -1)]).limit(topOrderCount) # 服务器其他信息 openServerTimeStr, worldLV, serverDay, isMixServer, mixServerDay = "", 0, 0, 0, 0 openServerTiemServerDay = 0 zoneID = 0 col = dboper.db["tagDBGameServerEventTrig"] spec = {"EventID":{"$in":["InitOpenServerTime", "WorldAverageLv", "ServerDay", "IsMixServer", "MixServerDay", "CrossPKZoneID"]}} #fields = {} eventRet = col.find(spec) for eventInfo in eventRet: eventName = eventInfo["EventID"] eventValue = eventInfo["IsEvent"] if eventName == "InitOpenServerTime": openServerTime = eventValue openServerTimeStr = "" if not openServerTime else time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(openServerTime)) if openServerTimeStr: openServerDatetime = datetime.datetime.strptime(openServerTimeStr.split(" ")[0], "%Y-%m-%d") openServerTiemServerDay = (datetime.datetime.today() - openServerDatetime).days + 1 elif eventName == "WorldAverageLv": worldLV = eventValue elif eventName == "ServerDay": serverDay = eventValue + 1 elif eventName == "IsMixServer": isMixServer = eventValue if not isMixServer: mixServerDay = 0 elif eventName == "MixServerDay": if isMixServer: mixServerDay = eventValue + 1 elif eventName == "CrossPKZoneID": zoneID = eventValue # 关闭 dboper.close() spaceStr = "  "; printStr = "" printStr += "%s:%s%s" % (_(u"开始日期"), spaceStr, startDate); printStr += "%s~%s" % (spaceStr, spaceStr) if endDate: printStr += "%s" % (endDate) else: printStr += _(u"今天") printStr += "
" printStr += "%s:%s%s%s人;%s %s:%s%s%s人;%s
" \ % (_(u"创角人数"), spaceStr, createRoleInfo[0], spaceStr, spaceStr * 2, _(u"首登人数"), spaceStr, createRoleInfo[1], spaceStr, spaceStr * 2 ) printStr += "%s:%s%s%s人;%s %s:%s%s%s人;%s %s:%s%s
" \ % (_(u"最高在线"), spaceStr, onlineMaxInfo.get("OnLinePlayerCount", 0) - onlineMaxInfo.get("tjgOnlineCnt", 0), spaceStr, spaceStr * 2, _(u"额外脱机"), spaceStr, onlineMaxInfo.get("tjgOnlineCnt", 0), spaceStr, spaceStr * 2, _(u"时间"), spaceStr, onlineMaxInfo.get("time", "")) allCtgAccIDList = [] printStr += "%s:%s%s%s人
" % (_(u"累计登录"), spaceStr, len(loginAccIDList), spaceStr) for payOrderType, ctgOKInfo in orderTypeCTGOKInfo.items(): orderTypeName = CommFunc.getPayOrderTypeName(payOrderType) ctgOKTotal, ctgAccIDList = ctgOKInfo if payOrderType not in CTGOKSort.PayByMoneyOrderTypeList: ctgOKTotal = CommFunc.coinToY(ctgOKTotal, CommFunc.getPayOrderCoinRate(payOrderType)) ctgPlayerCount = len(ctgAccIDList) ctgAvgValue = "%.2f" % (ctgOKTotal / ctgPlayerCount) if ctgPlayerCount else 0 printStr += "%s:%s%s%s%s;%s %s:%s%s%s;%s %s:%s%s%s%s
" \ % (_(u"累计充值"), spaceStr, ctgOKTotal, spaceStr, orderTypeName, spaceStr * 2, _(u"人数"), spaceStr, ctgPlayerCount, spaceStr, spaceStr * 2, _(u"人均"), spaceStr, ctgAvgValue, spaceStr, orderTypeName) for accID in ctgAccIDList: if accID not in allCtgAccIDList: allCtgAccIDList.append(accID) if not orderTypeCTGOKInfo: printStr += "%s:%s 0
" % (_(u"累计充值"), spaceStr) elif len(orderTypeCTGOKInfo) > 1: printStr += "%s:%s%s
" % (_(u"累计充值人数"), spaceStr, len(allCtgAccIDList)) printStr += "%s:" % (_(u"新增充值人数")) newCTGDateList = newCTGAccIDInfo.keys() newCTGDateList.sort() newCTGDateList = newCTGDateList[-7:] for dateStr in newCTGDateList: newCTGCount = len(newCTGAccIDInfo[dateStr]) printStr += "%s%s:%s" % (spaceStr, dateStr, newCTGCount) if not newCTGDateList: printStr += "%s0" % spaceStr printStr += "
" # 战力 printStr += "%s
" % _(u"以下排行榜非实时数据") printStr += "%s:%s%s
" % (_(u"所有角色战力排行"), spaceStr, getSortDataStr(fightPowerRet, "FightPower", spaceStr, "FightPowerEx")) if isFightPowerExVer: printStr += "%s:%s%s
" % (_(u"活跃角色战力排行"), spaceStr, getSortDataStr(billboard_FightPowerRet, "CmpValue2", spaceStr, "CmpValue")) else: printStr += "%s:%s%s
" % (_(u"活跃角色战力排行"), spaceStr, getSortDataStr(billboard_FightPowerRet, "CmpValue", spaceStr)) printStr += "%s:%s%s
" % (_(u"所有角色等级排行"), spaceStr, getSortDataStr(LVRet, "LV", spaceStr)) printStr += "%s:%s%s
" % (_(u"活跃角色等级排行"), spaceStr, getSortDataStr(billboard_LVRet, "CmpValue", spaceStr)) printStr += "%s:%s%s%s %s:%s%s %s:%s%s %s:%s%s %s:%s%s %s(%s)%s" % \ (_(u"开服时间"), spaceStr, openServerTimeStr, spaceStr * 2, _(u"开服天"), serverDay, spaceStr, _(u"是否合服"), isMixServer, spaceStr, _(u"合服天"), mixServerDay, spaceStr, _(u"世界等级"), worldLV, spaceStr, spaceStr * 2, _(u"凌晨0~5点查询可能误差1天"), spaceStr * 2 ) # 跨服PK分区显示样式,不同分区禁止规划合服合在一起 if zoneID: crossPKZoneStyleList = [["#ff0000", "#ffffff"], ["#00ff00", "#ffffff"], ["#ffff00", "#515a6e"], ["#2d8cf0", "#ffffff"], ["#00ffff", "#ffffff"]] bgColor, zoneIDColor = ["", ""] if zoneID > len(crossPKZoneStyleList) else crossPKZoneStyleList[zoneID - 1] printStr += "%s%s%s%s" \ % (bgColor, _(u"跨服PK分区"), spaceStr, zoneIDColor, zoneID, spaceStr) printStr += "
" printStr += "
" print printStr return def getSortDataStr(dataList, showDataKey, spaceStr, showDataKeyEx=None): showStr = "" for i, dataInfo in enumerate(dataList): if i != 0: showStr += ",%s" % (spaceStr) if isinstance(dataInfo, dict): value = dataInfo[showDataKey] if showDataKeyEx != None and showDataKeyEx in dataInfo: value += dataInfo[showDataKeyEx] * 100000000 else: value = getattr(dataInfo, showDataKey) if showDataKeyEx != None and hasattr(dataInfo, showDataKeyEx): value += getattr(dataInfo, showDataKeyEx) * 100000000 showStr += CommFunc.transformNum(value) return showStr def checkDrFileNeedParseFunc_ServerViewInfo(drFileName, *parseArgs, **kv): ''' 检查流向是否需要处理 @param drFileName: 流向文件名 xxx_日期.txt @param *parseArgs: 自定义参数 @return: isNeed, checkNeedParseRetInfo ''' isNeed = (drFileName.startswith("OnLinePlayerCount_") or drFileName.startswith("LogInOut_") or drFileName.startswith("CTGOK_") \ or drFileName.startswith("FirstLogin_") or drFileName.startswith("tagDBCreateRole_")) return isNeed, None def parseLineFunc_ServerViewInfo(drName, dateStr, checkNeedParseRetInfo, line, *parseArgs, **kv): ''' 解析流向行内容 @param drName: 流向名 @param dateStr: 对应日期字符串 @param checkNeedParseRetInfo: checkDrFileNeedParseFunc 返回的内容 @param line: 本行内容 @param *parseArgs: 自定义参数 ''' onlineMaxInfo, loginAccIDList, orderTypeCTGOKInfo, createRoleInfo, newCTGAccIDInfo = parseArgs # 统计最高在线 if drName == "OnLinePlayerCount": drDict = eval(line) #{'OnLinePlayerCount': 2, 'tjgOnlineCnt': 28, 'platformOLDict': 'qkbtgame', 'time': '2021-01-01 00:00:46'} 按平台记录 # ... #{'OnLinePlayerCount': 30, 'tjgOnlineCnt': 28, 'platformOLDict': {}, 'time': '2021-01-01 00:00:46'} 最后一条汇总 if drDict["platformOLDict"] != {}: # 暂只处理汇总的信息 return nowMax = onlineMaxInfo.get("OnLinePlayerCount", 0) - onlineMaxInfo.get("tjgOnlineCnt", 0) drOnlineCount = drDict["OnLinePlayerCount"] drTjCount = drDict["tjgOnlineCnt"] realOnlineCount = drOnlineCount - drTjCount # 实际在线 if realOnlineCount < nowMax: return if realOnlineCount == nowMax and drDict["tjgOnlineCnt"] < onlineMaxInfo.get("tjgOnlineCnt", 0): return onlineMaxInfo.update(drDict) # 统计累计登录 elif drName == "LogInOut": if "logout" in line: return drDict = eval(line) if drDict["Type"] != "login": return accID = drDict["AccID"] if accID in loginAccIDList: return loginAccIDList.append(accID) # 统计充值 elif drName == "CTGOK": drDict = eval(line) payOrderType = drDict.get("payOrderType", CTGOKSort.DefaultOrderType) ctgOKTotal, ctgAccIDList = orderTypeCTGOKInfo.get(payOrderType, [0, []]) if payOrderType in CTGOKSort.PayByMoneyOrderTypeList: orderValue = drDict["orderMoneyValue"] else: if not CTGOKSort.IsRealCTG(drDict): return orderValue = drDict["orderCoin"] ctgOKTotal += orderValue accID = drDict["AccID"] if accID not in ctgAccIDList: ctgAccIDList.append(accID) orderTypeCTGOKInfo[payOrderType] = [ctgOKTotal, ctgAccIDList] ctgRealFirstTimeStr = drDict.get("ctgRealFirstTime", "") if ctgRealFirstTimeStr.startswith(dateStr): newCTGAccIDList = newCTGAccIDInfo.get(dateStr, []) if accID not in newCTGAccIDList: newCTGAccIDList.append(accID) newCTGAccIDInfo[dateStr] = newCTGAccIDList # 创角 elif drName == "tagDBCreateRole": createRoleInfo[0] += 1 # 首登 elif drName == "FirstLogin": createRoleInfo[1] += 1 return def main(): CommFunc.setdefaultencoding() argvDict = CommFunc.parse_args() mylog.InitMyLog(argvDict.get("eventType", "")) CommFunc.gettextInstall(argvDict.get("lang", "")) dbIni = ConfigParser.ConfigParser() dbIni.read(os.path.join(ServerPath, "db\PyMongoDataServer\PyMongoDataServer.ini")) isCrossServer = dbIni.has_option("Merge", "IsMergeServer") and dbIni.getint("Merge", "IsMergeServer") == 1 if isCrossServer: CrossServerViewInfo(argvDict) else: getServerViewInfo(argvDict) return if __name__ == "__main__": try: main() except: CommFunc.printExceptionError()