#!/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()