ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmPK.py
@@ -18,19 +18,23 @@
import GameWorld
import PlayerControl
import CrossRealmMsg
import CrossRealmPlayer
import DataRecordPack
import ChPyNetSendPack
import CrossRealmPlayer
import PyGameDataStruct
import PlayerDBGSEvent
import PyDataManager
import NetPackCommon
import IpyGameDataPY
import ShareDefine
import PyGameData
import CommFunc
import ChConfig
import datetime
import operator
import random
PKPlayerState_Matching = 0
PKPlayerState_Fighting = 1
import time
class CrossPKPlayer():
    ## 跨服PK玩家类
@@ -42,22 +46,33 @@
        self.playerJob = 0
        self.playerLV = 0
        self.maxHP = 0
        self.maxProDef = 0
        self.fightPower = 0
        self.realmLV = 0
        self.pkScore = 0
        self.danLV = 0
        self.matchTick = 0
        self.prepareOKTick = 0
        self.cWinCount = 0 # 连胜次数
        self.ondayScore = 0 # 过天时的积分
        self.serverGroupID = 0 # 所属服务器ID,一个服务器ID由多个服组成
        self.pkZoneID = 0 # 所属赛区ID,一个赛区由多个服务器ID组成
        self.seasonID = 0 # 赛季ID
        self.notifyMatchRobotTick = 0 # 通知匹配到机器人的tick
        return
    def GetDRInfo(self):
        ## 流向记录信息
        return {"accID":self.accID, "playerID":self.playerID, "fightPower":self.fightPower, "pkScore":self.pkScore,
                "danLV":self.danLV, "matchTick":self.matchTick, "prepareOKTick":self.prepareOKTick, "cWinCount":self.cWinCount,
                "ondayScore":self.ondayScore, "serverGroupID":self.serverGroupID, "ondayScore":self.ondayScore}
    
class CrossPKRoom():
    ## 跨服PK房间类
    
    def __init__(self):
        self.pkZoneID = 0
        self.seasonID = 0
        self.roomID = 0
        self.mapID = 0
        self.openTick = 0 # 开房时间
@@ -65,15 +80,752 @@
        self.roomState = ShareDefine.Def_VsRoom_State_WaitPlayer # 默认状态
        self.roomPlayerIDList = [] # 对战玩家ID列表
        self.readyPlayerIDList = [] # 已经准备好的玩家ID列表
        self.isMapOpen = False # 地图是否已经开启该房间,未开启的房间超时后,本次匹配视为无效,有玩家进地图才会开启副本分线
        self.mapFBOpenTick = 0 # 地图开启该房间tick,未开启的房间超时后,本次匹配视为无效,有玩家进地图才会开启副本分线
        return
    def GetDRInfo(self):
        return {"zoneID":self.pkZoneID, "seasonID":self.seasonID, "roomID":self.roomID, "mapID":self.mapID, "openTick":self.openTick,
                "readyTick":self.readyTick, "roomPlayerIDList":self.roomPlayerIDList, "mapFBOpenTick":self.mapFBOpenTick}
#跨服PK排行榜管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
class CrossPKBillboardManager(object):
    DBKEY_CrossPKFinalBillboardData = "CrossPKFinalBillboard_%s_%s" # 是否收到赛区赛季最终榜单同步,参数(zoneID, seasonID)
    def __init__(self):
        self.__ZoneSeasonDataList = "ZoneSeasonDataList_%s_%s" # 分区赛季排行数据列表 [tagDBCrossPKBillboard, ...]
        self.__ZoneSeasonPlayerOrderDict = "ZoneSeasonPlayerOrderDict_%s_%s" # 分区赛季排行数据字典 {playerID:rank, ...}
        self.__ZoneSeasonList = [] # 分区赛季列表 [(zoneID, seasonID), ...]
        self.__UnSortZoneSeasonTimeDict = {} # 未排序的分区赛季排行 {(zoneID, seasonID):tick, ...}
        return
    def GetCrossPKBillboardInfo(self, zoneID, seasonID, isSort=False):
        ## 获取跨服PK排行榜信息
        # @return: billboardList, orderDict
        listAttrName = self.__ZoneSeasonDataList % (zoneID, seasonID)
        orderDictAttrName = self.__ZoneSeasonPlayerOrderDict % (zoneID, seasonID)
        if not hasattr(self, listAttrName):
            setattr(self, listAttrName, [])
            setattr(self, orderDictAttrName, {})
            self.__ZoneSeasonList.append((zoneID, seasonID))
        billboardList = getattr(self, listAttrName)
        if isSort:
            self.__PKBillboardSort(zoneID, seasonID, billboardList)
        orderDict = getattr(self, orderDictAttrName)
        return billboardList, orderDict
    def __PKBillboardSort(self, zoneID, seasonID, billboardList):
        ## 排序
        if (zoneID, seasonID) not in self.__UnSortZoneSeasonTimeDict:
            return
        self.__UnSortZoneSeasonTimeDict.pop((zoneID, seasonID))
        billboardList.sort(key=operator.attrgetter("PKScore", "Time"), reverse=True)
        orderDict = {}
        for order, billboardData in enumerate(billboardList, 1):
            orderDict[billboardData.PlayerID] = order # 缓存赛季玩家排名
        orderDictAttrName = self.__ZoneSeasonPlayerOrderDict % (zoneID, seasonID)
        setattr(self, orderDictAttrName, orderDict)
        GameWorld.DebugLog("跨服PK榜单排序: zoneID=%s,seasonID=%s,orderDict=%s" % (zoneID, seasonID, orderDict))
        return
    def PKBillboardSortByTime(self, tick):
        ## 定时排序
        if not self.__UnSortZoneSeasonTimeDict:
            return
        sortCD = 60 * 1000 # 超过1分钟强制排序
        for key, updTick in self.__UnSortZoneSeasonTimeDict.items():
            zoneID, seasonID = key
            if tick - updTick < sortCD:
                continue
            self.GetCrossPKBillboardInfo(zoneID, seasonID, True)
        return
    def SetNeedSort(self, zoneID, seasonID):
        if (zoneID, seasonID) not in self.__UnSortZoneSeasonTimeDict:
            self.__UnSortZoneSeasonTimeDict[(zoneID, seasonID)] = GameWorld.GetGameWorld().GetTick()
        return
    ## ==================================== 子服的榜单数据管理 =====================================
    def UpdClientServerPKBillboard(self, zoneID, seasonID, syncBillboardList, isFinalBillboardData):
        ## 先直接清除本服数据,然后直接覆盖更新
        billboardList = self.GetCrossPKBillboardInfo(zoneID, seasonID)[0]
        billboardList = billboardList[:len(syncBillboardList)] # 直接用本服以后的排行数据实例clear后覆盖更新,不足的创建新实例
        orderDict = {} # 排行信息重新更新
        for i, syncData in enumerate(syncBillboardList):
            playerID, playerName, job, fightPower, realmLV, pkScore, danLV, billTime, order = syncData
            if i < len(billboardList):
                billboardData = billboardList[i]
                billboardData.clear()
            else:
                billboardData = PyGameDataStruct.tagDBCrossPKBillboard()
                billboardList.append(billboardData)
            billboardData.ZoneID = zoneID
            billboardData.SeasonID = seasonID
            billboardData.PlayerID = playerID
            billboardData.PlayerName = playerName
            billboardData.Job = job
            billboardData.FightPower = fightPower
            billboardData.RealmLV = realmLV
            billboardData.PKScore = pkScore
            billboardData.DanLV = danLV
            billboardData.Time = billTime
            orderDict[playerID] = order
        listAttrName = self.__ZoneSeasonDataList % (zoneID, seasonID)
        orderDictAttrName = self.__ZoneSeasonPlayerOrderDict % (zoneID, seasonID)
        setattr(self, listAttrName, billboardList)
        setattr(self, orderDictAttrName, orderDict)
        dbIsFinalData = PlayerDBGSEvent.GetDBGSTrig_ByKey(self.DBKEY_CrossPKFinalBillboardData % (zoneID, seasonID))
        isFinalBillboardData = 1 if isFinalBillboardData else 0
        if dbIsFinalData != isFinalBillboardData:
            PlayerDBGSEvent.SetDBGSTrig_ByKey(self.DBKEY_CrossPKFinalBillboardData % (zoneID, seasonID), isFinalBillboardData)
            GameWorld.Log("更新子服榜单数据是否是最终榜单数据!zoneID=%s,seasonID=%s,isFinalBillboardData=%s"
                          % (zoneID, seasonID, isFinalBillboardData))
        return billboardList, orderDict
    def IsFinalBillboardData(self, zoneID, seasonID):
        ## 子服的赛区赛季榜单数据是否最终榜单数据
        return PlayerDBGSEvent.GetDBGSTrig_ByKey(self.DBKEY_CrossPKFinalBillboardData % (zoneID, seasonID))
    ## ===========================================================================================
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for zoneID, seasonID in self.__ZoneSeasonList:
            billboardList = self.GetCrossPKBillboardInfo(zoneID, seasonID)[0]
            for billboardData in billboardList:
                cnt += 1
                savaData += billboardData.getBuffer()
        GameWorld.Log("SaveCrossPKBillboard cnt :%s len=%s" % (cnt, len(savaData)))
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("LoadCrossPKBillboard cnt :%s" % cnt)
        for _ in xrange(cnt):
            billboardData = PyGameDataStruct.tagDBCrossPKBillboard()
            billboardData.clear()
            pos += billboardData.readData(datas, pos, dataslen)
            zoneID = billboardData.ZoneID
            seasonID = billboardData.SeasonID
            billboardList = self.GetCrossPKBillboardInfo(zoneID, seasonID)[0]
            billboardList.append(billboardData)
        # 排序
        for zoneID, seasonID in self.__ZoneSeasonList:
            self.SetNeedSort(zoneID, seasonID)
            billboardList = self.GetCrossPKBillboardInfo(zoneID, seasonID, True)[0]
            GameWorld.Log("    zoneID=%s, seasonID=%s, count=%s" % (zoneID, seasonID, len(billboardList)))
        return pos
def UpdateCrossPKBillboard(zoneID, seasonID, winner, loser, syncClientServer=False):
    ## 更新跨服PK排行榜,跨服服务器结算调用,子服不调用
    curZoneSeasonID = GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonID % zoneID)
    if curZoneSeasonID != seasonID:
        GameWorld.ErrLog("不是当前赛季,不更新排行榜! zoneID=%s,seasonID=%s,curZoneSeasonID=%s" % (zoneID, seasonID, curZoneSeasonID))
        return
    billboardCfg = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKCfg", 1, [])
    if not billboardCfg or len(billboardCfg) != 2:
        GameWorld.ErrLog("跨服竞技场排行榜配置错误!")
        return
    maxCount, danLVLimit = billboardCfg
    if maxCount <= 0:
        return
    maxCount = min(2000, maxCount)
    upBillboardList = []
    if winner:
        if winner.danLV >= danLVLimit:
            upBillboardList.append(winner)
        else:
            GameWorld.DebugLog("    段位不足,无法上榜! winnerDanLV=%s < danLVLimit=%s" % (winner.danLV, danLVLimit), winner.playerID)
    if loser:
        if loser.danLV >= danLVLimit:
            upBillboardList.append(loser)
        else:
            GameWorld.DebugLog("    段位不足,无法上榜! loserDanLV=%s < danLVLimit=%s" % (loser.danLV, danLVLimit), loser.playerID)
    if not upBillboardList:
        return
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    billboardList, orderDict = crossPKBillboardMgr.GetCrossPKBillboardInfo(zoneID, seasonID)
    isUpd = False
    curTime = int(time.time())
    billboardTime = max(0, GameWorld.ChangeTimeStrToNum("2080-01-01 00:00:00") - curTime) # 因为先上榜排前面,所以时间记录值得反减,倒序排
    for pkPlayer in upBillboardList:
        playerID = pkPlayer.playerID
        if playerID in orderDict:
            order = orderDict[playerID]
            billboardData = billboardList[order - 1]
            GameWorld.DebugLog("    已经在榜单上!playerID=%s,order=%s" % (playerID, order), playerID)
        else:
            if len(billboardList) < maxCount:
                newBillboardData = PyGameDataStruct.tagDBCrossPKBillboard()
                billboardList.append(newBillboardData)
                order = len(billboardList)
                GameWorld.DebugLog("    添加新上榜榜单!playerID=%s,order=%s" % (playerID, order), playerID)
            else:
                lastBillboardData = billboardList[-1]
                # 分数如果超过最后一个,则顶掉最后一个
                if lastBillboardData.PKScore >= pkPlayer.pkScore:
                    GameWorld.DebugLog("    PK积分不超过最后一名玩家,无法上榜! pkScore=%s <= lastPKScore=%s"
                                       % (pkPlayer.pkScore, lastBillboardData.PKScore), playerID)
                    continue
                order = orderDict.pop(lastBillboardData.PlayerID, len(billboardList))
                GameWorld.DebugLog("    顶掉最后一个榜单!playerID=%s,lastPlayer=%s,lastScore=%s,order=%s"
                                   % (playerID, lastBillboardData.PlayerID, lastBillboardData.PKScore, order))
                newBillboardData = PyGameDataStruct.tagDBCrossPKBillboard()
                billboardList[-1] = newBillboardData
            billboardData = newBillboardData
            orderDict[playerID] = order
        isUpd = True
        # 更新值
        billboardData.ZoneID = zoneID
        billboardData.SeasonID = seasonID
        billboardData.PlayerID = playerID
        billboardData.PlayerName = pkPlayer.playerName
        billboardData.Job = pkPlayer.playerJob
        billboardData.FightPower = pkPlayer.fightPower
        billboardData.RealmLV = pkPlayer.realmLV
        billboardData.PKScore = pkPlayer.pkScore
        billboardData.DanLV = pkPlayer.danLV
        billboardData.Time = billboardTime
        GameWorld.DebugLog("    更新PK积分排行榜: playerID=%s,pkScore=%s,order=%s" % (playerID, billboardData.PKScore, order), playerID)
    # 战斗结算更新榜单先不排序,只设置需要排序
    if isUpd:
        crossPKBillboardMgr.SetNeedSort(zoneID, seasonID)
    # 如果匹配已经结束,且没有战斗中的房间了,则处理本阶段匹配总结算
    matchState = GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
    if not matchState and not PyGameData.g_crossPKRoomDict:
        GameWorld.Log("匹配已结束,且当前没有PK中的房间了!主动广播子服最新榜单! ")
        syncClientServer = True
    if syncClientServer:
        crossZoneName = GameWorld.GetCrossZoneName()
        zoneIpyData = IpyGameDataPY.GetIpyGameData("CrossZonePK", crossZoneName, zoneID)
        if zoneIpyData:
            serverGroupIDList = zoneIpyData.GetServerGroupIDList()
            SyncPKSyncBillboardToClientServer(zoneID, seasonID, serverGroupIDList)
    return True
#// C0 01 查看跨服竞技场赛季排行榜 #tagCGViewCrossPKBillboard
#
#struct    tagCGViewCrossPKBillboard
#{
#    tagHead        Head;
#    BYTE        ZoneID;    // 赛区ID
#    BYTE        SeasonID;    // 赛季ID
#};
def OnViewCrossPKBillboard(index, clientData, tick):
    ''' 赛区赛季榜单同步规则,这里以多台跨服服务器为考虑
            每台跨服服务器单独管理自己关联的子服赛区榜单
                    每日匹配结束结算时间点主动同步所有子服本台跨服服务器的所有赛区当前赛季榜单,这样能确保所有子服的赛区榜单是最新的
                    匹配期间,子服设置同步CD,如果有玩家查询,则每1分钟可向跨服服务器同步当前赛区赛季最新榜单信息,同步后发送玩家
                    非匹配期间,只要子服有数据则可直接发送给玩家,没有的话向跨服服务器同步后再发送玩家
                    历史赛季,因为每日跨服服务器都有主动广播,所以历史赛季只要子服有数据则可直接发送给玩家,没有的话向跨服服务器同步后再发送玩家
                    赛季结束 或 历史赛季 只要不是最终榜单数据的,则需主动向跨服服务器请求同步数据后再发送玩家
                    不同跨服服务器赛区的话由跨服服务器之间自己同步数据,子服只管向自己所属跨服服务器同步数据即可
            跨服服务器与跨服服务器之间的赛区榜单同步,同步规则类似子服与跨服服务器,相互之间做好数据同步即可
            数据同步后再同步各自所有子服,确保子服也是最新的,可考虑匹配结算结束同步一次即可
            这样玩家查看其它跨服服务器赛区数据可能会延迟一次匹配阶段结算的时间,比如两个小时,这个等以后需要多台跨服服务器时再讨论
    '''
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    if GameWorld.IsCrossServer():
        return
    playerID = curPlayer.GetPlayerID()
    zoneID, seasonID = clientData.ZoneID, clientData.SeasonID
    tickKey = ChConfig.Def_PlayerKey_ViewCrossPKBillboardTick % (zoneID, seasonID)
    if tick - curPlayer.GetDictByKey(tickKey) < 60000:
        GameWorld.DebugLog("玩家查询跨服PK排行榜CD中: zoneID=%s, seasonID=%s" % (zoneID, seasonID), playerID)
        return
    curPlayer.SetDict(tickKey, tick)
    gameWorld = GameWorld.GetGameWorld()
    serverZoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    billboardList = crossPKBillboardMgr.GetCrossPKBillboardInfo(zoneID, seasonID)[0]
    isFinalBillboardData = crossPKBillboardMgr.IsFinalBillboardData(zoneID, seasonID)
    GameWorld.DebugLog("玩家请求查看跨服PK排行榜: zoneID=%s,seasonID=%s,serverZoneID=%s,isFinalBillboardData=%s"
                       % (zoneID, seasonID, serverZoneID, isFinalBillboardData))
    # 本服赛区
    if zoneID == serverZoneID:
        curSeasonID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
        seasonState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    # 其他赛区
    else:
        serverTime = GameWorld.GetServerTime()
        curSeasonID = 0
        seasonState = 0
        crossZoneName = GameWorld.GetCrossZoneName()
        seasonList = IpyGameDataPY.GetIpyGameDataList("CrossRealmPKSeason", crossZoneName, zoneID)
        seasonList = [] if not seasonList else seasonList
        for seasonIpyData in seasonList:
            startDateStr = seasonIpyData.GetStartDate()
            endDateStr = seasonIpyData.GetEndDate()
            endTimeStr = seasonIpyData.GetEndTime()
            startDateTime = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format)
            endDateTime = datetime.datetime.strptime("%s %s:00" % (endDateStr, endTimeStr), ChConfig.TYPE_Time_Format)
            if serverTime < startDateTime:
                break
            if serverTime <= endDateTime:
                curSeasonID = seasonIpyData.GetSeasonID()
                seasonState = 1
            else:
                break
    # 查询当前进行中的赛季
    if seasonID == curSeasonID:
        matchState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
        # 非匹配中的
        if not matchState:
            # 最终数据 or 赛季进行中 直接同步
            if isFinalBillboardData or (seasonState == 1 and billboardList):
                SyncCrossPKBillboard(curPlayer, zoneID, seasonID, billboardList)
                return
        # 正在匹配中的,验证本服数据同步CD,暂定1分钟同步一次
        else:
            syncTickKey = ChConfig.Def_WorldKey_CrossPKBillboardSyncTick % (zoneID, seasonID)
            if matchState and billboardList and tick - gameWorld.GetDictByKey(syncTickKey) < 60000:
                SyncCrossPKBillboard(curPlayer, zoneID, seasonID, billboardList)
                return
            gameWorld.SetDict(syncTickKey, tick)
    # 查询历史赛季的
    else:
        # 如果是最终数据则直接同步
        if isFinalBillboardData and billboardList:
            SyncCrossPKBillboard(curPlayer, zoneID, seasonID, billboardList)
            return
    # 请求查询跨服服务器
    dataMsg = {"ZoneID":zoneID, "SeasonID":seasonID, "ExData":{"EventName":"View", "PlayerID":playerID}}
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKBillboard, dataMsg)
    return
def MapServer_QueryCrossPKSeasonOrder(curPlayer, msgList):
    ## 地图服务器查询玩家赛区赛季PK榜排名
    playerID = curPlayer.GetPlayerID()
    zoneID, seasonID, eventName, eventData = msgList
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    billboardList, orderDict = crossPKBillboardMgr.GetCrossPKBillboardInfo(zoneID, seasonID) # 子服的数据一定是排完序同步过来的,所以这里就不需要再排序了
    if not billboardList:
        # 本服没榜单数据,查询跨服服务器
        # 请求查询跨服服务器
        dataMsg = {"ZoneID":zoneID, "SeasonID":seasonID, "ExData":{"EventName":eventName, "PlayerID":playerID, "EventData":eventData}}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKBillboard, dataMsg)
        return
    order = orderDict.get(playerID, 0)
    sysMsg = str([zoneID, seasonID, eventName, eventData, order])
    curPlayer.MapServer_QueryPlayerResult(0, 0, "CrossPKSeasonOrder", sysMsg, len(sysMsg))
    return
def ClientServerMsg_PKBillboard(serverGroupID, msgData):
    ## 收到子服请求查询PK排行榜信息
    if "GMSetCrossPK" in msgData:
        _GMSetCrossPK(serverGroupID, msgData)
        return
    zoneID = msgData["ZoneID"]
    seasonID = msgData["SeasonID"]
    exData = msgData.get("ExData", {}) # 原数据返回子服
    SyncPKSyncBillboardToClientServer(zoneID, seasonID, [serverGroupID], exData)
    return
def SyncPKSyncBillboardToClientServer(zoneID, seasonID, serverGroupIDList, exData={}):
    ## 同步赛区赛季榜单到子服
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    billboardList, orderDict = crossPKBillboardMgr.GetCrossPKBillboardInfo(zoneID, seasonID, True) # 同步子服前需触发排序
    isFinalBillboardData = False # 是否最终的榜单数据
    gameWorld = GameWorld.GetGameWorld()
    curZoneSeasonID = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonID % zoneID)
    if seasonID < curZoneSeasonID:
        isFinalBillboardData = True # 历史赛季在跨服服务器中的一定是最终榜单数据
    elif seasonID == curZoneSeasonID:
        seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
        if seasonState == 2:
            isFinalBillboardData = True # 当前赛季已经是赛季结算状态的了,代表是最终榜单数据
    syncBillboardList = []
    for billboardData in billboardList:
        playerID = billboardData.PlayerID
        playerName = billboardData.PlayerName
        job = billboardData.Job
        fightPower = billboardData.FightPower
        realmLV = billboardData.RealmLV
        pkScore = billboardData.PKScore
        danLV = billboardData.DanLV
        billTime = billboardData.Time
        order = orderDict.get(playerID, 0)
        syncBillboardList.append([playerID, playerName, job, fightPower, realmLV, pkScore, danLV, billTime, order])
    syncInfo = [zoneID, seasonID, syncBillboardList, exData, isFinalBillboardData]
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKSyncBillboard, syncInfo, serverGroupIDList)
    return
def CrossServerMsg_PKSyncBillboard(syncInfo):
    ## 收到跨服服务器同步的排行榜信息
    zoneID, seasonID, syncBillboardList, exData, isFinalBillboardData = syncInfo
    GameWorld.DebugLog("收到跨服服务器同步的排行榜信息: zoneID=%s,seasonID=%s,billboardDataCount=%s,exData=%s,isFinalBillboardData=%s"
                       % (zoneID, seasonID, len(syncBillboardList), exData, isFinalBillboardData))
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    billboardList, orderDict = crossPKBillboardMgr.UpdClientServerPKBillboard(zoneID, seasonID, syncBillboardList, isFinalBillboardData)
    if not exData:
        return
    eventName = exData.get("EventName")
    eventData = exData.get("EventData")
    queryPlayerID = exData.get("PlayerID", 0)
    if not eventName or not queryPlayerID:
        return
    queryPlayer = GameWorld.GetPlayerManager().FindPlayerByID(queryPlayerID)
    if not queryPlayer:
        return
    if eventName == "View":
        SyncCrossPKBillboard(queryPlayer, zoneID, seasonID, billboardList)
    else:
        order = orderDict.get(queryPlayerID, 0)
        sysMsg = str([zoneID, seasonID, eventName, eventData, order])
        queryPlayer.MapServer_QueryPlayerResult(0, 0, "CrossPKSeasonOrder", sysMsg, len(sysMsg))
    return
def SyncCrossPKBillboard(curPlayer, zoneID, seasonID, billboardList):
    ## 同步给玩家跨服PK榜单
    billboardInfo = ChPyNetSendPack.tagGCCrossRealmPKBillboardInfo()
    billboardInfo.ZoneID = zoneID
    billboardInfo.SeasonID = seasonID
    billboardInfo.PKBillboardList = []
    for billboardData in billboardList:
        billboardInfoData = ChPyNetSendPack.tagGCCrossRealmPKBillboardData()
        billboardInfoData.PlayerID = billboardData.PlayerID
        billboardInfoData.PlayerName = billboardData.PlayerName
        billboardInfoData.NameLen = len(billboardInfoData.PlayerName)
        billboardInfoData.Job = billboardData.Job
        billboardInfoData.FightPower = billboardData.FightPower
        billboardInfoData.RealmLV = billboardData.RealmLV
        billboardInfoData.PKScore = billboardData.PKScore
        billboardInfoData.DanLV = billboardData.DanLV
        billboardInfo.PKBillboardList.append(billboardInfoData)
    billboardInfo.Count = len(billboardInfo.PKBillboardList)
    NetPackCommon.SendFakePack(curPlayer, billboardInfo)
    return
    
################################################################################
def OnPlayerLogin(curPlayer):
def __GetCrossPKZoneSeasonTimeInfo():
    key = "CrossPKZoneSeasonTimeInfo"
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1
    ZoneSeasonTimeInfo = IpyGameDataPY.GetConfigEx(key)
    if ZoneSeasonTimeInfo and ZoneSeasonTimeInfo[0] == openServerDay:
        #GameWorld.DebugLog("已经加载过本日跨服PK赛季处理信息!openServerDay=%s" % openServerDay)
        return ZoneSeasonTimeInfo[1]
    GameWorld.Log("加载跨服PK赛季时间信息: openServerDay=%s" % (openServerDay))
    zoneSeasonTimeDict = {}
    serverTime = GameWorld.GetServerTime()
    crossZoneName = GameWorld.GetCrossZoneName()
    crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return zoneSeasonTimeDict
    for zoneIpyData in crossZoneList:
        zoneID = zoneIpyData.GetZoneID()
        # 规定每个赛区的赛季时间需按顺序配置
        seasonIpyDataList = IpyGameDataPY.GetIpyGameDataList("CrossRealmPKSeason", crossZoneName, zoneID)
        if not seasonIpyDataList:
            continue
        for i, seasonIpyData in enumerate(seasonIpyDataList):
            seasonID = seasonIpyData.GetSeasonID()
            startDateStr = seasonIpyData.GetStartDate()
            endDateStr = seasonIpyData.GetEndDate()
            endTimeStr = seasonIpyData.GetEndTime()
            startDateTime = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format)
            endDateTime = datetime.datetime.strptime("%s %s:00" % (endDateStr, endTimeStr), ChConfig.TYPE_Time_Format)
            # 最后一个赛季
            if i == len(seasonIpyDataList) - 1:
                nextSeasonIpyData = None
            else:
                nextSeasonIpyData = seasonIpyDataList[i + 1]
            if serverTime < startDateTime:
                GameWorld.Log("    赛季未开始! zoneID=%s,seasonID=%s,startDateStr=%s,endDateStr=%s" % (zoneID, seasonID, startDateStr, endDateStr))
                break
            elif startDateTime <= serverTime <= endDateTime:
                pass
            elif endDateTime < serverTime:
                if nextSeasonIpyData:
                    nestStartDateStr = nextSeasonIpyData.GetStartDate()
                    nextStartDateTime = datetime.datetime.strptime("%s 00:00:00" % (nestStartDateStr), ChConfig.TYPE_Time_Format)
                    if serverTime >= nextStartDateTime:
                        GameWorld.Log("    赛季已过期! zoneID=%s,seasonID=%s,startDateStr=%s,endDateStr=%s" % (zoneID, seasonID, startDateStr, endDateStr))
                        continue
                else:
                    pass
            else:
                continue
            nextSeasonID = 0 if not nextSeasonIpyData else nextSeasonIpyData.GetSeasonID()
            zoneSeasonTimeDict[zoneID] = [seasonIpyData]
            GameWorld.Log("    赛季活动中! zoneID=%s,seasonID=%s,startDateStr=%s,endDateStr=%s,nextSeasonID=%s" % (zoneID, seasonID, startDateStr, endDateStr, nextSeasonID))
            break
    ZoneSeasonTimeInfo = IpyGameDataPY.SetConfigEx(key, [openServerDay, zoneSeasonTimeDict])
    GameWorld.Log("跨服PK赛季时间信息加载完毕!")
    GameWorld.Log("=============================================================")
    return ZoneSeasonTimeInfo[1]
def OnMinuteProcess():
    ## 每分钟处理
    if not GameWorld.IsCrossServer():
        __OnLoginNotifyPKOverInfo(curPlayer)
        return
    zoneSeasonTimeDict = __GetCrossPKZoneSeasonTimeInfo()
    if not zoneSeasonTimeDict:
        return
    gameWorld = GameWorld.GetGameWorld()
    serverTime = GameWorld.GetServerTime()
    crossZoneName = GameWorld.GetCrossZoneName()
    for zoneID, seasonTimeInfo in zoneSeasonTimeDict.items():
        zoneIpyData = IpyGameDataPY.GetIpyGameData("CrossZonePK", crossZoneName, zoneID)
        if not zoneIpyData:
            continue
        curSeasonIpyData = seasonTimeInfo[0]
        if not curSeasonIpyData:
            continue
        seasonID = curSeasonIpyData.GetSeasonID()
        startDateStr = curSeasonIpyData.GetStartDate()
        endDateStr = curSeasonIpyData.GetEndDate()
        endTimeStr = curSeasonIpyData.GetEndTime()
        startDateTime = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format)
        endDateTime = datetime.datetime.strptime("%s %s:00" % (endDateStr, endTimeStr), ChConfig.TYPE_Time_Format)
        seasonState = 0
        if startDateTime <= serverTime < endDateTime:
            seasonState = 1
        elif serverTime >= endDateTime:
            seasonState = 2
        zoneSeasonIDDictName = ChConfig.Def_WorldKey_CrossPKZoneSeasonID % zoneID
        seasonStateDictName = ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID
        dictSeasonID = gameWorld.GetDictByKey(zoneSeasonIDDictName)
        curSeasonState = gameWorld.GetDictByKey(seasonStateDictName)
        if curSeasonState == seasonState and dictSeasonID == seasonID:
            #已经是这个状态了
            continue
        gameWorld.SetDict(zoneSeasonIDDictName, seasonID)
        gameWorld.SetDict(seasonStateDictName, seasonState)
        GameWorld.Log("跨服PK赛季状态变更: zoneID=%s,seasonID=%s,seasonState=%s" % (zoneID, seasonID, seasonState))
        serverGroupIDList = zoneIpyData.GetServerGroupIDList()
        # 赛季总结算,为确保子服榜单的实时性,这里需再同步子服最终榜单信息,需在同步赛季状态之前同步
        if seasonState == 2:
            SyncPKSyncBillboardToClientServer(zoneID, seasonID, serverGroupIDList)
        # 广播当前赛区的所有子服跨服PK赛季状态变更
        matchState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
        seasonInfo = {"ZoneID":zoneID, "SeasonID":seasonID, "SeasonState":seasonState, "MatchState":matchState}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKSeasonInfo, seasonInfo, serverGroupIDList)
    return
def OnCrossRealmPKDailyActionStateChange(isOpen):
    ## 跨服PK每日活动匹配状态变更
    if not GameWorld.IsCrossServer():
        return
    gameWorld = GameWorld.GetGameWorld()
    preState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
    gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK, isOpen)
    # 匹配状态从关闭到开启
    if not preState and isOpen:
        PyGameData.g_crossPKPlayerDict = {}
        PyGameData.g_crossPKZoneMatchPlayerDict = {}
        PyGameData.g_crossPKZoneMatchRobotPlayerDict = {}
        PyGameData.g_crossPKRoomDict = {}
        PyGameData.g_crossPKTodayPKRecordInfo = {}
        PyGameData.g_crossPKBesureMatchRobotInfo = {}
        GameWorld.Log("跨服PK匹配状态开启,重置相关匹配数据!")
    crossZoneName = GameWorld.GetCrossZoneName()
    crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return
    for zoneIpyData in crossZoneList:
        zoneID = zoneIpyData.GetZoneID()
        # 广播当前赛区的所有子服跨服PK赛季状态变更
        serverGroupIDList = zoneIpyData.GetServerGroupIDList()
        seasonID = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonID % zoneID)
        seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
        seasonInfo = {"ZoneID":zoneID, "SeasonID":seasonID, "SeasonState":seasonState, "MatchState":isOpen}
        GameWorld.Log("跨服PK匹配状态变更: zoneID=%s,seasonID=%s,seasonState=%s,matchState=%s" % (zoneID, seasonID, seasonState, isOpen))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKSeasonInfo, seasonInfo, serverGroupIDList)
    return
def Sync_CrossPKInitDataToClientServer(tick, serverGroupID=0):
    ''' 同步跨服PK活动数据到子服务器
    @param serverGroupID: 为0时同步所有子服
    '''
    GameWorld.Log("同步给子服对应的赛季信息: syncServerGroupID=%s" % (serverGroupID))
    if serverGroupID:
        ipyData = GetCrossPKServerGroupZone(serverGroupID)
        if not ipyData:
            return
        crossZoneList = [ipyData]
    else:
        crossZoneName = GameWorld.GetCrossZoneName()
        crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return
    gameWorld = GameWorld.GetGameWorld()
    for zoneIpyData in crossZoneList:
        zoneID = zoneIpyData.GetZoneID()
        seasonID = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonID % zoneID)
        seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
        matchState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
        seasonInfo = {"ZoneID":zoneID, "SeasonID":seasonID, "SeasonState":seasonState, "MatchState":matchState}
        serverGroupIDList = [serverGroupID] if serverGroupID else zoneIpyData.GetServerGroupIDList()
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKSeasonInfo, seasonInfo, serverGroupIDList)
    return
def GetCrossPKServerGroupZone(serverGroupID):
    ## 获取服务器组ID对应的跨服PK所属赛区,返回0代表该服务器没有分配赛区
    crossZoneName = GameWorld.GetCrossZoneName()
    crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return
    for zoneIpyData in crossZoneList:
        for groupInfo in zoneIpyData.GetServerGroupIDList():
            if (isinstance(groupInfo, int) and serverGroupID == groupInfo) \
                or (isinstance(groupInfo, tuple) and len(groupInfo) == 2 and groupInfo[0] <= serverGroupID <= groupInfo[1]):
                return zoneIpyData
    return
def OnPlayerLoginCrossServer(curPlayer):
    # 跨服登录处理
    mapID = curPlayer.GetMapID()
    mapIDList = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 4)
    if mapID not in mapIDList:
        return
    GameWorld.Log("玩家登录跨服PK地图, mapID=%s" % mapID, curPlayer.GetPlayerID())
    if not __CheckCanLoginCrossServerPKMap(curPlayer):
        CrossRealmPlayer.PlayerExitCrossServer(curPlayer)
        return
    return
def OnPlayerLogin(curPlayer):
    # 本服登录处理
    # 本服没有分配赛区不处理
    if not GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID):
        return
    __OnLoginNotifyPKOverInfo(curPlayer)
    crossZoneName = GameWorld.GetCrossZoneName()
    crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return
    # 同步所有赛区赛季信息
    zoneSeasonInfo = ChPyNetSendPack.tagGCCrossRealmPKSeasonInfo()
    zoneSeasonInfo.ZoneList = []
    for zoneIpyData in crossZoneList:
        zoneID = zoneIpyData.GetZoneID()
        zoneInfo = ChPyNetSendPack.tagGCCrossRealmPKZone()
        zoneInfo.ZoneID = zoneID
        zoneInfo.ZoneName = zoneIpyData.GetZoneName().decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding())
        zoneInfo.ZoneNameLen = len(zoneInfo.ZoneName)
        zoneInfo.SeasonList = []
        seasonList = IpyGameDataPY.GetIpyGameDataList("CrossRealmPKSeason", crossZoneName, zoneID)
        seasonList = [] if not seasonList else seasonList
        for seasonIpyData in seasonList:
            seasonInfo = ChPyNetSendPack.tagGCCrossRealmPKSeason()
            seasonInfo.SeasonID = seasonIpyData.GetSeasonID()
            seasonInfo.StartDate = seasonIpyData.GetStartDate()
            seasonInfo.EndDate = seasonIpyData.GetEndDate()
            seasonInfo.EndTime = seasonIpyData.GetEndTime()
            zoneInfo.SeasonList.append(seasonInfo)
        zoneInfo.SeasonCount = len(zoneInfo.SeasonList)
        zoneSeasonInfo.ZoneList.append(zoneInfo)
    zoneSeasonInfo.ZoneCount = len(zoneSeasonInfo.ZoneList)
    NetPackCommon.SendFakePack(curPlayer, zoneSeasonInfo)
    # 同步本服赛季状态
    gameWorld = GameWorld.GetGameWorld()
    seasonStatePack = ChPyNetSendPack.tagGCCrossRealmPKSeasonState()
    seasonStatePack.ZoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    seasonStatePack.SeasonID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
    seasonStatePack.SeasonState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    seasonStatePack.MatchState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
    seasonStatePack.CrossZoneName = GameWorld.GetCrossZoneName()
    seasonStatePack.CrossZoneNameLen = len(seasonStatePack.CrossZoneName)
    NetPackCommon.SendFakePack(curPlayer, seasonStatePack)
    return
def __CheckCanLoginCrossServerPKMap(curPlayer):
    # 检查玩家可否登录跨服PK房间
    playerID = curPlayer.GetPlayerID()
    vsRoomID = curPlayer.GetVsRoomId()
    if not vsRoomID:
        GameWorld.ErrLog("玩家没有对战房间ID,不可进入对战地图! 强制踢出跨服服务器!", playerID)
        return False
    if vsRoomID not in PyGameData.g_crossPKRoomDict:
        GameWorld.ErrLog("玩家对战房间ID已经不存在,不可进入对战地图! 强制踢出跨服服务器!vsRoomID=%s" % vsRoomID, playerID)
        return False
    vsRoom = PyGameData.g_crossPKRoomDict[vsRoomID]
    if playerID not in vsRoom.roomPlayerIDList:
        GameWorld.ErrLog("玩家对战房间ID不存在该玩家ID,不可进入对战地图! 强制踢出跨服服务器!vsRoomID=%s,roomPlayerIDList=%s"
                         % (vsRoomID, vsRoom.roomPlayerIDList), playerID)
        return False
    return True
## 玩家离线处理
def OnLeaveServer(curPlayer):    
@@ -81,17 +833,10 @@
    SendCancelCrossRealmPKMatch(curPlayer, "PlayerDisconnect")
    return
def IsCrossRealmPKOpen():
def IsCrossRealmPKMatchState():
    ## 跨服PK匹配赛是否开启
    return 1
    return GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_MergePKState) == ChConfig.Def_Action_Open
def ClientServerMsg_ServerInitOK(serverGroupID, tick):
    ## 子服启动成功
    GameWorld.Log("同步跨服PK赛季信息及状态到子服: serverGroupID=%s" % (serverGroupID))
    seasonInfo = {"SeasonID":1, "SeasonState":1, "MatchState":1}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKSeasonInfo, seasonInfo, [serverGroupID])
    return
    return GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState \
                                                 % ShareDefine.DailyActionID_CrossReamPK) == ChConfig.Def_Action_Open
def SendCancelCrossRealmPKMatch(curPlayer, reason):   
    ## 发送取消匹配
@@ -101,7 +846,7 @@
        return
    
    # 非活动中不处理
    if not IsCrossRealmPKOpen():
    if not IsCrossRealmPKMatchState():
        return
    
#    # 如果是要登陆到跨服服务器的,不发送取消
@@ -111,7 +856,7 @@
#        return
    vsRoomID = curPlayer.GetVsRoomId()
    if vsRoomID and PlayerControl.GetCrossRealmState(curPlayer) == 1:
    if vsRoomID and PlayerControl.GetCrossMapID(curPlayer) == ChConfig.Def_FBMapID_CrossRealmPK:
        GameWorld.DebugLog("玩家跨服PK状态,不能取消匹配!vsRoomID=%s" % vsRoomID, curPlayer.GetPlayerID())
        return
    
@@ -123,17 +868,18 @@
               }
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKCancel, dataMsg)
    PlayerControl.SetVsRoomId(curPlayer, 0)
    GameWorld.Log("发送取消跨服PK匹配到跨服服务器:dataMsg=%s" % str(dataMsg), curPlayer.GetPlayerID())
    SetIsCrossPKMatching(curPlayer, 0)
    GameWorld.DebugLog("发送取消跨服PK匹配到跨服服务器:dataMsg=%s" % str(dataMsg), curPlayer.GetPlayerID())
    return
def ClientServerMsg_PKMatch(serverGroupID, playerInfoDict, tick):
    ## 请求匹配
    
    if not GameWorld.IsMergeServer():
    if not GameWorld.IsCrossServer():
        GameWorld.ErrLog("非跨服服务器不处理跨服PK匹配请求!")
        return
    
    if not IsCrossRealmPKOpen():
    if not IsCrossRealmPKMatchState():
        GameWorld.Log("跨服匹配PK活动未开启,不允许请求匹配!")
        return
    
@@ -145,21 +891,28 @@
    job = playerInfoDict["playerJob"] # ְҵ
    playerLV = playerInfoDict["playerLV"] # ְҵ
    maxHP = playerInfoDict["maxHP"] # ְҵ
    fightPower = playerInfoDict["fightPower"] # 战斗力
    maxProDef = playerInfoDict["maxProDef"] # 护盾
    fightPower = min(playerInfoDict["fightPower"], ChConfig.Def_UpperLimit_DWord) # 战斗力,暂无用,限制不超过20E
    realmLV = playerInfoDict["realmLV"] # 境界
    pkScore = playerInfoDict["pkScore"] # 当前积分
    danLV = playerInfoDict["danLV"] # 当前段位
    cWinCount = playerInfoDict["cWinCount"] # 连胜次数
    cLoseCount = playerInfoDict["cLoseCount"] # 连败次数
    ondayScore = playerInfoDict["ondayScore"] # 过天时的积分
    
    if playerID in PyGameData.g_crossPKZoneMatchRobotPlayerDict:
        GameWorld.Log("玩家已匹配机器人,无法重复发起匹配! playerID=%s,accID=%s" % (playerID, accID))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 2], [serverGroupID])
        return
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
#    if playerID in zoneMatchPlayerList:
#        GameWorld.Log("玩家正在匹配中,无法重复发起匹配!playerID=%s,accID=%s" % (playerID, accID))
#        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 1], [serverGroupID])
#        return
#    if playerID in PyGameData.g_crossPKPlayerDict:
#        GameWorld.Log("玩家正在战斗中,无法重复发起匹配!playerID=%s,accID=%s" % (playerID, accID))
#        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, -2], [serverGroupID])
#        return
    if playerID in zoneMatchPlayerList:
        GameWorld.Log("玩家正在匹配中,无法重复发起匹配! playerID=%s,accID=%s" % (playerID, accID))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 1], [serverGroupID])
        return
    if playerID in PyGameData.g_crossPKPlayerDict:
        GameWorld.Log("玩家正在战斗中,无法重复发起匹配! playerID=%s,accID=%s" % (playerID, accID))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, -2], [serverGroupID])
        return
    
    pkPlayer = CrossPKPlayer()
    pkPlayer.accID = accID
@@ -168,36 +921,122 @@
    pkPlayer.playerJob = job
    pkPlayer.playerLV = playerLV
    pkPlayer.maxHP = maxHP
    pkPlayer.maxProDef = maxProDef
    pkPlayer.pkScore = pkScore
    pkPlayer.danLV = danLV
    pkPlayer.fightPower = fightPower
    pkPlayer.realmLV = realmLV
    pkPlayer.matchTick = tick
    pkPlayer.cWinCount = cWinCount
    pkPlayer.ondayScore = ondayScore
    pkPlayer.serverGroupID = serverGroupID
    pkPlayer.pkZoneID = pkZoneID
    pkPlayer.seasonID = seasonID
    # 判断是否匹配机器人
    matchRobotRate = 0
    danIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", danLV)
    if danIpyData:
        matchRobotRate = danIpyData.GetMatchRobotRate() + danIpyData.GetMatchRobotRateEx() * cLoseCount
    # 判断是否必定匹配机器人: 连输X局必定连续匹配机器人Y局
    cLoseCount = 0
    todayPKRecordList = PyGameData.g_crossPKTodayPKRecordInfo.get(playerID, [])
    for pkRecord in todayPKRecordList[::-1]:
        _, winnerID = pkRecord
        if playerID == winnerID:
            break
        cLoseCount += 1
    cLoseBesureMatchRobot, besureMatchRobotCount = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKRobot", 2)
    if cLoseCount >= cLoseBesureMatchRobot:
        PyGameData.g_crossPKBesureMatchRobotInfo[playerID] = besureMatchRobotCount
        GameWorld.DebugLog("连输X局必定连续匹配机器人Y局! playerID=%s,cLoseCount(%s) >= X(%s),Y(%s)"
                           % (playerID, cLoseCount, cLoseBesureMatchRobot, besureMatchRobotCount))
    matchRobotMaxRate = 100
    # 还有必定匹配机器人次数
    nowBesureMatchRobotCount = PyGameData.g_crossPKBesureMatchRobotInfo.get(playerID, 0)
    if nowBesureMatchRobotCount > 0:
        matchRobotRate = matchRobotMaxRate
        PyGameData.g_crossPKBesureMatchRobotInfo[playerID] = nowBesureMatchRobotCount - 1
        GameWorld.DebugLog("玩家必定匹配机器人! playerID=%s,nowBesureMatchRobotCount=%s" % (playerID, nowBesureMatchRobotCount))
    if matchRobotRate and GameWorld.CanHappen(matchRobotRate, matchRobotMaxRate):
        pkPlayer.notifyMatchRobotTick = tick + random.randint(3, 5) * 1000
        PyGameData.g_crossPKZoneMatchRobotPlayerDict[playerID] = pkPlayer
        GameWorld.DebugLog("玩家加入匹配: seasonID=%s,pkZoneID=%s,serverGroupID=%s,accID=%s,playerID=%s,pkScore=%s,fightPower=%s,cWinCount=%s"
                           % (seasonID, pkZoneID, serverGroupID, accID, playerID, pkScore, fightPower, cWinCount))
        GameWorld.DebugLog("    本次匹配到机器人: danLV=%s,cLoseCount=%s,概率=%s" % (danLV, cLoseCount, matchRobotRate), playerID)
        #这里优化下暂通知开始匹配,实际匹配到机器人做延迟通知
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 1], [serverGroupID])
        return
    PyGameData.g_crossPKPlayerDict[playerID] = pkPlayer
    
    # 加入赛区匹配列表
    zoneMatchPlayerList.append(playerID)
    PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID] = zoneMatchPlayerList
    
    GameWorld.Log("玩家加入匹配: seasonID=%s,pkZoneID=%s,serverGroupID=%s,accID=%s,playerID=%s,pkScore=%s,fightPower=%s,cWinCount=%s,len(zoneMatchPlayerList)=%s"
                  % (seasonID, pkZoneID, serverGroupID, accID, playerID, pkScore, fightPower, cWinCount, len(zoneMatchPlayerList)))
    GameWorld.DebugLog("玩家加入匹配: seasonID=%s,pkZoneID=%s,serverGroupID=%s,accID=%s,playerID=%s,pkScore=%s,fightPower=%s,cWinCount=%s,cLoseCount=%s,概率=%s,len(zoneMatchPlayerList)=%s"
                       % (seasonID, pkZoneID, serverGroupID, accID, playerID, pkScore, fightPower, cWinCount, cLoseCount, matchRobotRate, len(zoneMatchPlayerList)))
    
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 1], [serverGroupID])
    return
def __DelayNotifyMatchRobot(tick):
    ## 做体验,延迟通知匹配到机器人
    for playerID, pkPlayer in PyGameData.g_crossPKZoneMatchRobotPlayerDict.items():
        if pkPlayer.notifyMatchRobotTick == -1:
            #GameWorld.DebugLog("已通知过玩家匹配到机器人", playerID)
            continue
        if tick < pkPlayer.notifyMatchRobotTick:
            continue
        pkPlayer.notifyMatchRobotTick = -1
        serverGroupID = pkPlayer.serverGroupID
        GameWorld.DebugLog("延迟通知玩家匹配到机器人: serverGroupID=%s" % (serverGroupID), playerID)
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 2], [serverGroupID])
    return
def __DoTimeOutPlayerMatchRobot(matchTickSortList, matchPlayerIDList, tick):
    ## 超时很久很久的玩家系统匹配机器人
    if not matchPlayerIDList:
        return
    matchRobotTimeoutInfo = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKRobot", 1)
    if len(matchRobotTimeoutInfo) != 2:
        return
    matchRobotTick = random.randint(matchRobotTimeoutInfo[0], matchRobotTimeoutInfo[1]) * 1000 # 超时匹配机器人时间tick
    for matchPlayer in matchTickSortList:
        playerID = matchPlayer.playerID
        if playerID not in matchPlayerIDList:
            continue
        if tick - matchPlayer.matchTick < matchRobotTick:
            #GameWorld.DebugLog("    i=%s,玩家未满足超时匹配机器人时间条件!" % (i))
            break
        matchPlayerIDList.remove(playerID) # 这个列表为分区对应匹配中的玩家列表 PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID]
        PyGameData.g_crossPKZoneMatchRobotPlayerDict[playerID] = matchPlayer
        matchPlayer.notifyMatchRobotTick = -1
        serverGroupID = matchPlayer.serverGroupID
        GameWorld.DebugLog("直接通知超时玩家匹配到机器人: serverGroupID=%s" % (serverGroupID), playerID)
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 2], [serverGroupID])
    return
def ClientServerMsg_PKCancel(playerInfoDict, tick):
    ## 取消匹配
    
    if not GameWorld.IsMergeServer():
    if not GameWorld.IsCrossServer():
        GameWorld.ErrLog("非跨服服务器不处理取消跨服PK匹配!")
        return
    
    # 非活动中不处理
    if not IsCrossRealmPKOpen():
    if not IsCrossRealmPKMatchState():
        return
    
    accID = playerInfoDict["accID"] # 角色账号
@@ -206,22 +1045,22 @@
    vsRoomID = playerInfoDict["vsRoomID"] # 所属对战房间ID
    if vsRoomID in PyGameData.g_crossPKRoomDict:
        pkRoom = PyGameData.g_crossPKRoomDict[vsRoomID]
        if pkRoom.isMapOpen or pkRoom.readyTick:
        if pkRoom.mapFBOpenTick or pkRoom.readyTick:
            GameWorld.Log("跨服对战房间已经开启了线路,或者双方数据都已传输完毕,不可再取消匹配!vsRoomID=%s" % vsRoomID)
            return
        
    GameWorld.Log("玩家取消匹配: reason=%s,accID=%s,playerID=%s,vsRoomID=%s" % (reason, accID, playerID, vsRoomID))
    GameWorld.DebugLog("玩家取消匹配: reason=%s,accID=%s,playerID=%s,vsRoomID=%s" % (reason, accID, playerID, vsRoomID))
    
    pkZoneID = 0
    if playerID in PyGameData.g_crossPKPlayerDict:
        pkPlayer = PyGameData.g_crossPKPlayerDict.pop(playerID)
        pkZoneID = pkPlayer.pkZoneID
        GameWorld.Log("    移除PK玩家: pkZoneID=%s,accID=%s,playerID=%s" % (pkZoneID, accID, playerID))
        GameWorld.DebugLog("    移除PK玩家: pkZoneID=%s,accID=%s,playerID=%s" % (pkZoneID, accID, playerID))
        
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
    if playerID in zoneMatchPlayerList:
        zoneMatchPlayerList.remove(playerID)
        GameWorld.Log("    从匹配队列中删除,匹配队列剩余人数=%s" % (len(zoneMatchPlayerList)))
        GameWorld.DebugLog("    从匹配队列中删除,匹配队列剩余人数=%s" % (len(zoneMatchPlayerList)))
        
    #取消所有存在该玩家的房间,子服不一定知道玩家当前最新所属房间ID, 故只能通过遍历删除已经为玩家创建的房间
    for roomID, pkRoom in PyGameData.g_crossPKRoomDict.items():
@@ -230,25 +1069,33 @@
        
        for roomPlayerID in pkRoom.roomPlayerIDList:
            if roomPlayerID == playerID:
                GameWorld.Log("    自己不处理: roomID=%s,playerID=%s" % (roomID, playerID))
                GameWorld.DebugLog("    自己不处理: roomID=%s,playerID=%s" % (roomID, playerID))
                continue
            
            zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
            zoneMatchPlayerList.append(roomPlayerID)
            PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID] = zoneMatchPlayerList
            GameWorld.Log("    将之前匹配的对手重新加入匹配队列: roomID=%s,roomPlayerID=%s,当前匹配人数=%s"
                          % (roomID, roomPlayerID, len(zoneMatchPlayerList)))
            GameWorld.DebugLog("    将之前匹配的对手重新加入匹配队列: roomID=%s,roomPlayerID=%s,当前匹配人数=%s"
                               % (roomID, roomPlayerID, len(zoneMatchPlayerList)))
            
        PyGameData.g_crossPKRoomDict.pop(roomID)
        GameWorld.Log("    移除房间: popRoomID=%s" % (roomID))
        GameWorld.DebugLog("    移除房间: popRoomID=%s" % (roomID))
        # 记录流向
        dataDict = {"cancelPlayerID":playerID}
        dataDict.update(pkRoom.GetDRInfo())
        DR_CrossReamlPK("CancelRoom", dataDict)
        break
    
    if playerID in PyGameData.g_crossPKZoneMatchRobotPlayerDict:
        PyGameData.g_crossPKZoneMatchRobotPlayerDict.pop(playerID)
        GameWorld.DebugLog("    从匹配机器人队列中移除!")
    return
def ClientServerMsg_PKPrepareOK(playerInfoDict, tick):
    ## 玩家跨服对战数据准备OK
    
    if not GameWorld.IsMergeServer():
    if not GameWorld.IsCrossServer():
        GameWorld.ErrLog("非跨服服务器不处理取消跨服PK匹配!")
        return
    
@@ -258,8 +1105,9 @@
    if playerID not in PyGameData.g_crossPKPlayerDict:
        GameWorld.ErrLog("玩家跨服对战数据准备OK, 但找不到该对战玩家信息!vsRoomID=%s,playerID=%s" % (vsRoomID, playerID))
        return
    #pkPlayer = PyGameData.g_crossPKPlayerDict[playerID]
    pkPlayer = PyGameData.g_crossPKPlayerDict[playerID]
    pkPlayer.prepareOKTick = tick
    if vsRoomID not in PyGameData.g_crossPKRoomDict:
        GameWorld.ErrLog("玩家跨服对战数据准备OK, 但找不到该对战房间(%s)!可能对手已取消!" % vsRoomID)
        return
@@ -272,7 +1120,7 @@
    if playerID not in vsRoom.readyPlayerIDList:
        vsRoom.readyPlayerIDList.append(playerID)
        
    GameWorld.Log("玩家跨服PK准备完毕: accID=%s,playerID=%s,vsRoomID=%s" % (accID, playerID, vsRoomID))
    GameWorld.DebugLog("玩家跨服PK准备完毕: accID=%s,playerID=%s,vsRoomID=%s" % (accID, playerID, vsRoomID))
    return
def __ReadyOKRoomPlayerProcess(tick):
@@ -294,44 +1142,88 @@
        isAllReady = True
        roomGroupIDList = []
        readyMemberDict = {} # 已准备好的玩家信息
        for roomPlayerID in vsRoom.roomPlayerIDList:
        for num, roomPlayerID in enumerate(vsRoom.roomPlayerIDList, 1):
            if roomPlayerID not in vsRoom.readyPlayerIDList or roomPlayerID not in PyGameData.g_crossPKPlayerDict:
                isAllReady = False
                break
            roomPlayer = PyGameData.g_crossPKPlayerDict[roomPlayerID]
            pkZoneID = roomPlayer.pkZoneID
            roomGroupIDList.append(roomPlayer.serverGroupID)
            readyMemberDict[roomPlayerID] = {"ServerGroupID":roomPlayer.serverGroupID, "Name":roomPlayer.playerName,
                                             "Job":roomPlayer.playerJob, "LV":roomPlayer.playerLV, "MaxHP":roomPlayer.maxHP}
            readyMemberDict[roomPlayerID] = {"ServerGroupID":roomPlayer.serverGroupID, "Name":roomPlayer.playerName, "Number":num,
                                             "Job":roomPlayer.playerJob, "LV":roomPlayer.playerLV, "MaxHP":roomPlayer.maxHP, "MaxProDef":roomPlayer.maxProDef}
                    
        if not isAllReady:
            continue
        vsRoom.roomState = ShareDefine.Def_VsRoom_State_PrepareFight
        vsRoom.readyTick = tick
        GameWorld.Log("    准备好的房间: pkZoneID=%s,roomID=%s,mapID=%s,readyMemberDict=%s" % (pkZoneID, roomID, vsRoom.mapID, str(readyMemberDict)))
        GameWorld.DebugLog("    准备好的房间: pkZoneID=%s,roomID=%s,mapID=%s,readyMemberDict=%s" % (pkZoneID, roomID, vsRoom.mapID, str(readyMemberDict)))
        
        sendReadyOKRoomList.append([roomID, readyMemberDict])
        serverGroupIDList += roomGroupIDList
        
    # 将已准备好的房间广播到子服
    if sendReadyOKRoomList:
        GameWorld.Log("    已准备好的对战房间数: %s" % len(sendReadyOKRoomList))
        GameWorld.DebugLog("    已准备好的对战房间数: %s" % len(sendReadyOKRoomList))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKReadyOKRoomList, sendReadyOKRoomList, serverGroupIDList)
        
    return
#
#def TestAddMatch(addCount, maxDanLV=None, isClear=False):
#    tick = GameWorld.GetGameWorld().GetTick()
#    if isClear:
#        PyGameData.g_crossPKPlayerDict = {}
#        PyGameData.g_crossPKZoneMatchPlayerDict = {}
#        PyGameData.g_crossPKRoomDict = {}
#        PyGameData.g_crossPKRoomID = 0
#
#    ipyDataMgr = IpyGameDataPY.IPY_Data()
#    if maxDanLV == None:
#        maxDanLV = ipyDataMgr.GetCrossRealmPKDanCount() - 1
#    crossZoneName = GameWorld.GetCrossZoneName()
#    crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
#    if not crossZoneList:
#        return
#    for zoneIpyData in crossZoneList:
#        pkZoneID = zoneIpyData.GetZoneID()
#
#        addPlayerList = []
#        zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
#        maxPlayerID = pkZoneID * 1000 if not zoneMatchPlayerList else max(zoneMatchPlayerList)
#        for i in xrange(1, addCount + 1):
#            playerID = maxPlayerID + i
#
#            pkPlayer = CrossPKPlayer()
#            pkPlayer.playerID = playerID
#            danLV = random.randint(0, maxDanLV)
#            pkPlayer.danLV = danLV
#            pkPlayer.matchTick = tick
#            pkPlayer.pkScore = danLV * 1000 + random.randint(0, 999)
#            pkPlayer.pkZoneID = pkZoneID
#            pkPlayer.seasonID = 1
#
#            PyGameData.g_crossPKPlayerDict[playerID] = pkPlayer
#            addPlayerList.append([playerID, danLV])
#            # 加入赛区匹配列表
#            zoneMatchPlayerList.append(playerID)
#        PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID] = zoneMatchPlayerList
#        GameWorld.Log("Add zoneID=%s,addPlayerList=%s" % (pkZoneID, addPlayerList))
#
#    #GameWorld.Log("PyGameData.g_crossPKPlayerDict=%s" % PyGameData.g_crossPKPlayerDict)
#    return
def OnPKMatchProcess(tick):
    ## 玩家跨服PK匹配定时处理逻辑
    
    # 非跨服服务器不处理跨服PK匹配逻辑
    if not GameWorld.IsMergeServer():
    if not GameWorld.IsCrossServer():
        return
    
    if not IsCrossRealmPKOpen():
    if not IsCrossRealmPKMatchState():
        return
    
    # 同步子服排行榜
    #__SyncBillboardToClientServer(False, tick)
    # 定时更新排行榜
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    crossPKBillboardMgr.PKBillboardSortByTime(tick)
    
    processTick = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 1) * 1000
    processTickKey = "PKMatchLastTick"
@@ -340,6 +1232,8 @@
        return
    GameWorld.GetGameWorld().SetDict(processTickKey, tick)
    
    # 延迟通知匹配到机器人的
    __DelayNotifyMatchRobot(tick)
    # 处理超时的房间
    __DoCheckRoomTimeout(tick)
    # 通知已准备好的房间玩家可进入跨服
@@ -350,12 +1244,15 @@
    
    # 每个赛区单独匹配
    for pkZoneID, matchPlayerIDList in PyGameData.g_crossPKZoneMatchPlayerDict.items():
        matchPlayerCount = len(matchPlayerIDList)
        if matchPlayerCount < 2:
            #GameWorld.Log("匹配PK人数不足,不处理!pkZoneID=%s, 总人数:%s" % (pkZoneID, matchPlayerCount))
        if not matchPlayerIDList:
            #GameWorld.Log("没有玩家匹配PK,不处理!pkZoneID=%s" % (pkZoneID))
            continue
        matchPlayerCount = len(matchPlayerIDList)
#        if matchPlayerCount < 2:
#            #GameWorld.Log("匹配PK人数不足,不处理!pkZoneID=%s, 总人数:%s" % (pkZoneID, matchPlayerCount))
#            continue
        
        GameWorld.Log("★★★★★★★★★★开始跨服PK匹配(pkZoneID=%s, 总人数:%s)★★★★★★★★★★" % (pkZoneID, matchPlayerCount))
        GameWorld.DebugLog("★★★★★★★★★★开始跨服PK匹配(pkZoneID=%s, 总人数:%s)★★★★★★★★★★" % (pkZoneID, matchPlayerCount))
        
        matchPlayerList = []
        for matchPlayerID in matchPlayerIDList:
@@ -364,137 +1261,170 @@
            matchPlayerList.append(PyGameData.g_crossPKPlayerDict[matchPlayerID])
        # 按匹配时间、积分升序排序
        matchTickSortList = sorted(matchPlayerList, key=operator.attrgetter("matchTick"))
        scoreSortList = sorted(matchPlayerList, key=operator.attrgetter("pkScore"))
        #scoreSortList = sorted(matchPlayerList, key=operator.attrgetter("pkScore")) # 新匹配规则暂时用不到积分排序的
        
        matchPlayerVSList = [] # 成功匹配玩家对战列表
        
        # 优先匹配等待超时玩家
        __DoMatch_OutTimePlayer(matchTickSortList, scoreSortList, outTimeTick, matchPlayerVSList, tick)
        __DoMatchPlayer(matchTickSortList, matchPlayerVSList, maxGroupCnt, tick, outTimeTick)
        
        if len(matchPlayerVSList) < maxGroupCnt:
            # 再按积分段匹配玩家
            __DoMatch_DanScorePlayer(scoreSortList, maxGroupCnt, matchPlayerVSList)
            __DoMatchPlayer(matchTickSortList, matchPlayerVSList, maxGroupCnt, tick)
            
        # 给成功匹配的玩家非配对战房间
        matchPlayerVSList = matchPlayerVSList[:maxGroupCnt]
        __DoSetVSRoom(pkZoneID, matchPlayerVSList, tick)
        GameWorld.Log("==========匹配结束(总匹配队伍:%s)==========" % len(matchPlayerVSList))
        GameWorld.DebugLog("==========匹配结束(总匹配队伍:%s)==========" % len(matchPlayerVSList))
        #GameWorld.DebugLog("crossPlayerIDList=%s" % PyGameData.g_crossPKPlayerDict.keys())
        #GameWorld.DebugLog("matchPlayerIDList=%s" % PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID])
        __DoTimeOutPlayerMatchRobot(matchTickSortList, matchPlayerIDList, tick)
    return
def __DoMatch_OutTimePlayer(matchTickSortList, scoreSortList, outTimeTick, matchPlayerVSList, tick):
    '''匹配超时玩家
        匹配中的玩家按积分排序,最后一个默认匹配上一个,第一个默认匹配下一个,其他匹配前后积分差绝对值较小的一个
def __DoMatchPlayer(matchTickSortList, matchPlayerVSList, maxGroupCnt, tick, outTimeTick=0):
    ''' 匹配玩家
    @param matchTickSortList: 按匹配时间升序排序后的匹配玩家列表
    @param maxGroupCnt: 最大匹配组数
    @param matchPlayerVSList: 已经匹配的PK组列表
    @param outTimeTick: 超时tick设定,0为匹配非超时玩家
    '''
    GameWorld.Log(" ==优先匹配超时等待玩家==最大等待时间:%s, tick=%s" % (outTimeTick, tick))
    GameWorld.Log(" scoreSortListLen=%s" % len(scoreSortList))
    for i, matchPlayer in enumerate(matchTickSortList):
        # 只有一个玩家
        if len(scoreSortList) <= 1:
            GameWorld.Log("    当前玩家数%s<=1,不再匹配!" % len(scoreSortList))
    matchPlayerCount = 0
    danPlayerListDict = {}
    for matchPlayer in matchTickSortList:
        if outTimeTick and tick - matchPlayer.matchTick < outTimeTick:
            # 未超时,不再匹配,因为只有一个玩家
            break
        if tick - matchPlayer.matchTick < outTimeTick:
            GameWorld.Log("    i=%s,玩家未超时,不再匹配!" % (i))
            break
        GameWorld.Log("    i=%s,超时玩家, %s-%s=%s >= outTimeTick(%s)"
                      % (i, tick, matchPlayer.matchTick, tick - matchPlayer.matchTick, outTimeTick))
        # 已经被匹配走了
        if matchPlayer not in scoreSortList:
            GameWorld.Log("        已经被匹配走了!")
            continue
        outTimeIndex = scoreSortList.index(matchPlayer)
        # 最后一个默认匹配上一个
        if outTimeIndex == len(scoreSortList) - 1:
            vsIndex = outTimeIndex - 1
            GameWorld.Log("        超时玩家积分排序索引%s,最后一个,默认匹配上一个索引%s!" % (outTimeIndex, vsIndex))
        # 第一个默认匹配下一个
        elif outTimeIndex == 0:
            vsIndex = outTimeIndex + 1
            GameWorld.Log("        超时玩家积分排序索引%s,第一个,默认匹配下一个索引%s!" % (outTimeIndex, vsIndex))
        # 其他情况匹配积分较近的一个
        else:
            preIndex = outTimeIndex - 1
            nextIndex = outTimeIndex + 1
            prePlayer = scoreSortList[preIndex]
            nextPlayer = scoreSortList[nextIndex]
            preDiff = abs(prePlayer.pkScore - matchPlayer.pkScore)
            nextDiff = abs(matchPlayer.pkScore - nextPlayer.pkScore)
            vsIndex = preIndex if preDiff <= nextDiff else nextIndex
            GameWorld.Log("        超时玩家积分排序索引-积分(%s-%s),上一个(%s-%s),下一个(%s-%s),preDiff=%s,nextDiff=%s,vsIndex=%s"
                          % (outTimeIndex, matchPlayer.pkScore, preIndex, prePlayer.pkScore,
                             nextIndex, nextPlayer.pkScore, preDiff, nextDiff, vsIndex))
        if outTimeIndex > vsIndex:
            scoreSortList.pop(outTimeIndex)
            vsPlayer = scoreSortList.pop(vsIndex)
        elif outTimeIndex < vsIndex:
            vsPlayer = scoreSortList.pop(vsIndex)
            scoreSortList.pop(outTimeIndex)
        else:
            continue
        # 加入成功匹配列表
        matchPlayerVSList.append([matchPlayer, vsPlayer])
    return
def __DoMatch_DanScorePlayer(scoreSortList, maxGroupCnt, matchPlayerVSList):
    ''' 匹配积分分段玩家
            匹配中的玩家按段位积分归组,归组后,随机段位顺序,每个段位组中的玩家随机两两PK
    '''
    GameWorld.Log(" ==匹配积分分段玩家== maxGroupCnt=%s,scoreSortListLen=%s" % (maxGroupCnt, len(scoreSortList)))
    danPlayerListDict = {} # 按积分分段列表分散玩家
    for matchPlayer in scoreSortList:
        danLV = matchPlayer.danLV
        danPlayerList = danPlayerListDict.get(danLV, [])
        danPlayerList.append(matchPlayer)
        danPlayerListDict[danLV] = danPlayerList
        matchPlayerCount += 1
        
    # 按分段玩家随机匹配
    if outTimeTick:
        GameWorld.DebugLog(" ==优先匹配超时等待玩家==最大等待时间:%s, matchPlayerCount=%s" % (outTimeTick, matchPlayerCount))
    else:
        GameWorld.DebugLog(" ==常规段位玩家== maxGroupCnt=%s,matchPlayerCount=%s" % (maxGroupCnt, matchPlayerCount))
    if matchPlayerCount < 2:
        #GameWorld.DebugLog("    匹配玩家不足!不处理!matchPlayerCount=%s" % matchPlayerCount)
        return
    danList = danPlayerListDict.keys()
    random.shuffle(danList) # 打乱段位顺序
    random.shuffle(danList) # 打乱段位处理顺序
    
    GameWorld.Log("    积分分段个数: %s, %s" % (len(danList), danList))
    matchProtectRound = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch2", 1) # 几局内不能匹配到同一个玩家
    matchDanDiffList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKMatch2", 2) # 初始匹配段位差|超时匹配段位差
    matchDanDiff = matchDanDiffList[1] if outTimeTick else matchDanDiffList[0]
    
    # 日志输出分组明细
    #GameWorld.DebugLog(" 匹配玩家数=%s,同对手匹配保护局数=%s,匹配段位差=%s,danList=%s"
    #                   % (matchPlayerCount, matchProtectRound, matchDanDiff, danList))
    for danLV in danList:
        strList = []
        for player in danPlayerListDict[danLV]:
            strList.append((player.playerID, player.pkScore, player.fightPower))
        GameWorld.Log("        积分段组, danLV=%s, %s" % (danLV, str(strList)))
        danPlayerList = danPlayerListDict[danLV]
        lowDanPlayerList, highDanPlayerList = [], [] # 相对danLV的高低段位玩家需要分开处理,如果一起处理的话可能导致匹配到不应该匹配到的段位
        for vsDanLV in xrange(max(0, danLV - matchDanDiff), danLV + matchDanDiff + 1):
            if danLV == vsDanLV:
                continue
            if vsDanLV not in danPlayerListDict:
                continue
            if vsDanLV < danLV:
                lowDanPlayerList += danPlayerListDict[vsDanLV]
            else:
                highDanPlayerList += danPlayerListDict[vsDanLV]
        #GameWorld.DebugLog(" danLV=%s,danPlayerCount=%s,lowPlayerCount=%s,highPlayerCount=%s"
        #                   % (danLV, len(danPlayerList), len(lowDanPlayerList), len(highDanPlayerList)))
        
    doCount = 0
    while len(matchPlayerVSList) < maxGroupCnt and doCount < maxGroupCnt:
        doCount += 1
        # 非超时的,优先匹配同段位玩家
        if not outTimeTick:
            __MatchDanPlayerList(matchTickSortList, matchPlayerVSList, danPlayerList, danPlayerListDict, matchProtectRound, maxGroupCnt)
        # 分别匹配低段位、高段位
        __MatchDanPlayerList(matchTickSortList, matchPlayerVSList, danPlayerList + lowDanPlayerList, danPlayerListDict, matchProtectRound, maxGroupCnt)
        __MatchDanPlayerList(matchTickSortList, matchPlayerVSList, danPlayerList + highDanPlayerList, danPlayerListDict, matchProtectRound, maxGroupCnt)
    return
def __MatchDanPlayerList(matchTickSortList, matchPlayerVSList, matchPlayerList, danPlayerListDict, matchProtectRound, maxGroupCnt):
    ## 执行匹配段位玩家列表
    matchPlayerCount = len(matchPlayerList)
    if matchPlayerCount < 2:
        return
    random.shuffle(matchPlayerList) # 打乱匹配顺序,相当于随机两两分组,然后下面直接按顺序处理即可
    #GameWorld.DebugLog("    总未匹配人数=%s,已匹配组数=%s,待匹配人数=%s" % (len(matchTickSortList), len(matchPlayerVSList), len(matchPlayerList)))
    #for i, mPlayer in enumerate(matchPlayerList):
    #    GameWorld.DebugLog("    i=%s,playerID=%s,danLV=%s" % (i, mPlayer.playerID, mPlayer.danLV))
    #maxGroupCnt = 99999 # 测试批量匹配用
    nextIndex = 0
    doCount = matchPlayerCount
    # 至少需要两个  且 未达到最大成功匹配组数 且 限制最大可循环次数大于0
    while nextIndex < len(matchPlayerList) - 1 and len(matchPlayerVSList) < maxGroupCnt and doCount > 0:
        doCount -= 1
        i = nextIndex
        aPlayer = matchPlayerList[i]
        nextIndex += 1
        aPlayerID = aPlayer.playerID
        aDanLV = aPlayer.danLV
        #if maxGroupCnt == 99999 and random.randint(1, 10) > 5:
        #    GameWorld.DebugLog("        i=%s,aPlayerID=%s,aDanLV=%s 测试随机不匹配" % (i, aPlayerID, aDanLV))
        #    continue
        if aPlayer not in matchTickSortList:
            #GameWorld.DebugLog("        i=%s,aPlayerID=%s,aDanLV=%s 已经被匹配走了" % (i, aPlayerID, aDanLV))
            continue
        aTodayPKRecordList = PyGameData.g_crossPKTodayPKRecordInfo.get(aPlayerID, [])
        aMatchProtectPlayerIDList = [pkRecord[0] for pkRecord in aTodayPKRecordList[:-matchProtectRound - 1:-1]] # 最近X局匹配的玩家ID
        #GameWorld.DebugLog("        i=%s,aPlayerID=%s,aMatchProtectPlayerIDList=%s" % (i, aPlayerID, aMatchProtectPlayerIDList))
        isMatchOK = False
        for danLV in danList:
            danPlayerList = danPlayerListDict[danLV]
            danPlayerCount = len(danPlayerList)
            if danPlayerCount < 2:
                GameWorld.Log("    段位人数少于2个,此段位本轮轮空!doCount=%s,danLV=%s" % (doCount, danLV))
        for j, bPlayer in enumerate(matchPlayerList[nextIndex:], nextIndex):
            bPlayerID = bPlayer.playerID
            bDanLV = bPlayer.danLV
            #if maxGroupCnt == 99999 and random.randint(1, 10) > 5:
            #    GameWorld.DebugLog("            j=%s,bPlayerID=%s,bDanLV=%s 测试随机不匹配" % (j, bPlayerID, bDanLV))
            #    continue
            if bPlayer not in matchTickSortList:
                #GameWorld.DebugLog("            j=%s,bPlayerID=%s,bDanLV=%s 已经被匹配走了" % (j, bPlayerID, bDanLV))
                continue
            if bPlayerID in aMatchProtectPlayerIDList:
                GameWorld.DebugLog("            j=%s,bPlayerID=%s,bDanLV=%s %s局内不匹配同对手! bPlayerID=%s in aMatchProtectPlayerIDList=%s"
                                   % (j, bPlayerID, bDanLV, matchProtectRound, bPlayerID, aMatchProtectPlayerIDList))
                continue
            
            vsIndexList = random.sample(xrange(danPlayerCount), 2) # 随机取两个索引对战
            vsIndexList.sort()
            aPlayer = danPlayerList.pop(vsIndexList[1])
            bPlayer = danPlayerList.pop(vsIndexList[0])
            bTodayPKRecordList = PyGameData.g_crossPKTodayPKRecordInfo.get(bPlayerID, [])
            bMatchProtectPlayerIDList = [pkRecord[0] for pkRecord in bTodayPKRecordList[:-matchProtectRound - 1:-1]] # 最近X局匹配的玩家ID
            if aPlayerID in bMatchProtectPlayerIDList:
                GameWorld.DebugLog("            j=%s,bPlayerID=%s,bDanLV=%s %s局内不匹配同对手! aPlayerID=%s in bMatchProtectPlayerIDList=%s"
                                   % (j, bPlayerID, bDanLV, matchProtectRound, aPlayerID, bMatchProtectPlayerIDList))
                continue
            
            matchPlayerVSList.append([aPlayer, bPlayer])
            matchTickSortList.remove(aPlayer)
            matchTickSortList.remove(bPlayer)
            matchPlayerList.remove(aPlayer)
            matchPlayerList.remove(bPlayer)
            aDanPlayerList = danPlayerListDict.get(aDanLV, [])
            if aPlayer in aDanPlayerList:
                aDanPlayerList.remove(aPlayer)
            bDanPlayerList = danPlayerListDict.get(bDanLV, [])
            if bPlayer in bDanPlayerList:
                bDanPlayerList.remove(bPlayer)
            # 加入成功匹配列表
            isMatchOK = True
            GameWorld.Log("    成功匹配玩家: aPlayerID=%s,aScore=%s,aFP=%s VS bPlayerID=%s,bScore=%s,bFP=%s"
                          % (aPlayer.playerID, aPlayer.pkScore, aPlayer.fightPower, bPlayer.playerID, bPlayer.pkScore, bPlayer.fightPower))
            if len(matchPlayerVSList) >= maxGroupCnt:
                GameWorld.Log("    已经达到最大匹配数! 已匹配对战数=%s, 不再匹配!doCount=%s" % (len(matchPlayerVSList), doCount))
                break
        if not isMatchOK:
            GameWorld.Log("    已经没有满足匹配条件的玩家! 不再匹配!doCount=%s" % (doCount))
            matchPlayerVSList.append([aPlayer, bPlayer])
            GameWorld.DebugLog("            i=%s,j=%s,aPlayerID=%s(danLV:%s) VS bPlayerID=%s(danLV:%s) 匹配成功!"
                               % (i, j, aPlayerID, aDanLV, bPlayerID, bDanLV))
            break
        
        if isMatchOK:
            nextIndex -= 1
    return
def __DoSetVSRoom(pkZoneID, matchPlayerVSList, tick):
@@ -508,7 +1438,7 @@
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
    
    mapIDList = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 4)
    GameWorld.Log("===给配对的玩家开房间(pkZoneID=%s,配对数:%s)===" % (pkZoneID, len(matchPlayerVSList)))
    GameWorld.DebugLog("===给配对的玩家开房间(pkZoneID=%s,配对数:%s)===" % (pkZoneID, len(matchPlayerVSList)))
    for aPlayer, bPlayer in matchPlayerVSList:
        
        if not aPlayer or not bPlayer:
@@ -528,6 +1458,7 @@
        
        newRoom = CrossPKRoom()
        newRoom.pkZoneID = pkZoneID
        newRoom.seasonID = aPlayer.seasonID # 因为匹在一起的玩家一定是同一赛区同一赛季的,所以随便取一个玩家的赛季ID信息即可
        newRoom.roomID = roomID
        newRoom.mapID = mapID
        newRoom.openTick = tick
@@ -535,8 +1466,13 @@
        PyGameData.g_crossPKRoomDict[roomID] = newRoom
        
        aServerGroupID, bServerGroupID = aPlayer.serverGroupID, bPlayer.serverGroupID
        GameWorld.Log("    开房:pkZoneID=%s,mapID=%s,roomID=%s,aPlayerID=%s,bPlayerID=%s" % (pkZoneID, mapID, roomID, aPlayerID, bPlayerID))
        GameWorld.DebugLog("    开房:pkZoneID=%s,mapID=%s,roomID=%s,aPlayerID=%s,bPlayerID=%s" % (pkZoneID, mapID, roomID, aPlayerID, bPlayerID))
        vsRoomDict[roomID] = [mapID, [[aServerGroupID, aPlayerID], [bServerGroupID, bPlayerID]]]
        # 记录流向
        dataDict = {"aPlayer":aPlayer.GetDRInfo(), "bPlayer":bPlayer.GetDRInfo()}
        dataDict.update(newRoom.GetDRInfo())
        DR_CrossReamlPK("OpenRoom", dataDict)
        
        serverGroupIDList.append(aServerGroupID)
        serverGroupIDList.append(bServerGroupID)
@@ -570,10 +1506,9 @@
    
    timeoutRoomDict = {}
    serverGroupIDList = []
    #roomTimeout = IpyGameDataPY.GetFuncCfg("CheckRoomTimeout", 1) * 1000 # 这个时间尽量长点,目前暂时不确定玩家从准备好到进入到地图的时长
    roomTimeout = 180 * 1000 # 这个时间尽量长点,目前暂时不确定玩家从准备到进入到地图的时长
    roomTimeout = IpyGameDataPY.GetFuncCfg("CrossRealmPKCfg", 2) * 1000 # 这个时间尽量长点,目前暂时不确定玩家从准备到进入到地图的时长
    for roomID, pkRoom in PyGameData.g_crossPKRoomDict.items():
        if pkRoom.isMapOpen or not pkRoom.readyTick:
        if pkRoom.mapFBOpenTick or not pkRoom.readyTick:
            continue
        if tick - pkRoom.readyTick <= roomTimeout:
            continue
@@ -592,25 +1527,28 @@
        timeoutRoomDict[roomID] = roomPlayerInfo
        PyGameData.g_crossPKRoomDict.pop(roomID)
        
        # 记录流向
        DR_CrossReamlPK("TimeoutRoom", pkRoom.GetDRInfo())
    # 将超时房间广播到子服
    if timeoutRoomDict:
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKTimeoutRoomList, timeoutRoomDict, serverGroupIDList)
    return
def MapServer_CrossPKRoomOpen(msgList):
def MapServer_CrossPKRoomOpen(msgList, tick):
    roomID = msgList[0]
    if roomID not in PyGameData.g_crossPKRoomDict:
        GameWorld.ErrLog("MapServer_CrossPKRoomOpen => PK房间不存在!roomID=%s" % roomID)
        return
    pkRoom = PyGameData.g_crossPKRoomDict[roomID]
    pkRoom.isMapOpen = True
    pkRoom.mapFBOpenTick = tick
    GameWorld.Log("MapServer_CrossPKRoomOpen => roomID=%s" % roomID)
    return
def MapServer_MergePKOver(infoList):
def MapServer_CrossPKOver(infoList, tick):
    ## 收到MapServer副本跨服PK结果同步
    
    GameWorld.Log("收到MapServer_跨服PK战斗结果: %s" % str(infoList))
    GameWorld.DebugLog("收到MapServer_跨服PK战斗结果: %s" % str(infoList))
    
    roomID, winnerID, loserID, roundWinnerIDList, overType = infoList
    
@@ -647,15 +1585,16 @@
    loser = PyGameData.g_crossPKPlayerDict.pop(loserID)
    #winner = PyGameData.g_crossPKPlayerDict[winnerID]
    #loser = PyGameData.g_crossPKPlayerDict[loserID]
    seasonID = winner.seasonID
    zoneID = vsRoom.pkZoneID
    seasonID = vsRoom.seasonID
    
    cWinCount = winner.cWinCount
    winnerScore, loserScore = winner.pkScore, loser.pkScore
    winnerDanLV, loserDanLV = winner.danLV, loser.danLV
    winnerDayScore, loserDayScore = max(0, winnerScore - winner.ondayScore), max(0, loserScore - loser.ondayScore) # 今日已获得积分,正积分
    
    GameWorld.Log("winnerDayScore=%s,winnerScore=%s,winnerDanLV=%s,cWinCount=%s" % (winnerDayScore, winnerScore, winnerDanLV, cWinCount))
    GameWorld.Log("loserDayScore=%s,loserScore=%s,loserDanLV=%s" % (loserDayScore, loserScore, loserDanLV))
    GameWorld.DebugLog("winnerDayScore=%s,winnerScore=%s,winnerDanLV=%s,cWinCount=%s" % (winnerDayScore, winnerScore, winnerDanLV, cWinCount))
    GameWorld.DebugLog("loserDayScore=%s,loserScore=%s,loserDanLV=%s" % (loserDayScore, loserScore, loserDanLV))
    
    winIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", winnerDanLV)
    loseIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", loserDanLV)
@@ -690,8 +1629,23 @@
    if loseIpyData and loseIpyData.GetLVUpScore() and loser.pkScore >= loseIpyData.GetLVUpScore():
        loser.danLV += 1
        
    GameWorld.Log("wBaseScore=%s,wExScore=%s,winnerAddScore=%s,updScore=%s,updDanLV=%s,updCWinCount=%s" % (wBaseScore, wExScore, winnerAddScore, winner.pkScore, winner.danLV, winner.cWinCount))
    GameWorld.Log("lBaseScore=%s,lExScore=%s,loserAddScore=%s,updScore=%s,updDanLV=%s,updCWinCount=%s" % (lBaseScore, lExScore, loserAddScore, loser.pkScore, loser.danLV, loser.cWinCount))
    GameWorld.DebugLog("wBaseScore=%s,wExScore=%s,winnerAddScore=%s,updScore=%s,updDanLV=%s,updCWinCount=%s" % (wBaseScore, wExScore, winnerAddScore, winner.pkScore, winner.danLV, winner.cWinCount))
    GameWorld.DebugLog("lBaseScore=%s,lExScore=%s,loserAddScore=%s,updScore=%s,updDanLV=%s,updCWinCount=%s" % (lBaseScore, lExScore, loserAddScore, loser.pkScore, loser.danLV, loser.cWinCount))
    # 更新排行榜
    UpdateCrossPKBillboard(zoneID, seasonID, winner, loser)
    # 更新今日PK记录
    if winnerID not in PyGameData.g_crossPKTodayPKRecordInfo:
        PyGameData.g_crossPKTodayPKRecordInfo[winnerID] = []
    winnerPKRecordList = PyGameData.g_crossPKTodayPKRecordInfo[winnerID]
    winnerPKRecordList.append([loserID, winnerID])
    if loserID not in PyGameData.g_crossPKTodayPKRecordInfo:
        PyGameData.g_crossPKTodayPKRecordInfo[loserID] = []
    loserPKRecordList = PyGameData.g_crossPKTodayPKRecordInfo[loserID]
    loserPKRecordList.append([winnerID, winnerID])
    #GameWorld.DebugLog("PyGameData.g_crossPKTodayPKRecordInfo=%s" % PyGameData.g_crossPKTodayPKRecordInfo)
    
    timeStr = GameWorld.GetCurrentDataTimeStr()
    playerOverDict = {}
@@ -707,7 +1661,7 @@
        player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        notifyState = True if player else False
        
        playerOverDict[playerID] = [roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList] \
        playerOverDict[playerID] = [roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList] \
                                    + [serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
        if not player:
            continue
@@ -726,17 +1680,286 @@
        overPack.TagNameLen = len(overPack.TagName)
        NetPackCommon.SendFakePack(player, overPack)
        
        GameWorld.Log("同步玩家PK结果: serverGroupID=%s,roomID=%s,addScore=%s,pkScore=%s,danLV=%s,cWinCount=%s,tagPlayerID=%s"
                      % (serverGroupID, roomID, addScore, pkScore, danLV, cWinCount, tagPlayerID), playerID)
        GameWorld.DebugLog("同步玩家PK结果: serverGroupID=%s,roomID=%s,addScore=%s,pkScore=%s,danLV=%s,cWinCount=%s,tagPlayerID=%s"
                           % (serverGroupID, roomID, addScore, pkScore, danLV, cWinCount, tagPlayerID), playerID)
        
    serverGroupIDList = [winner.serverGroupID, loser.serverGroupID]
    GameWorld.Log("同步子服战斗结果: seasonID=%s,timeStr=%s,roomID=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s"
                  % (seasonID, timeStr, roomID, overType, winnerID, roundWinnerIDList))
    GameWorld.DebugLog("同步子服战斗结果: seasonID=%s,timeStr=%s,roomID=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s"
                       % (seasonID, timeStr, roomID, overType, winnerID, roundWinnerIDList))
    # 同步子服
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKOverInfo, playerOverDict, serverGroupIDList)
    # 记录流向
    dataDict = {"roundWinnerIDList":roundWinnerIDList, "overType":overType, "winner":winner.GetDRInfo(), "loser":loser.GetDRInfo()}
    dataDict.update(vsRoom.GetDRInfo())
    DR_CrossReamlPK("PKOverRoom", dataDict)
    return
def UpdatePKPlayerScore(roomID, pkPlayer, isWin):
    ## 更新PK玩家积分
    playerID = pkPlayer.playerID
    cWinCount = pkPlayer.cWinCount
    pkScore = pkPlayer.pkScore
    danLV = pkPlayer.danLV
    dayScore = max(0, pkScore - pkPlayer.ondayScore)# 今日已获得积分,正积分
    GameWorld.DebugLog("roomID=%s,playerID=%s,isWin=%s,dayScore=%s,pkScore=%s,danLV=%s,cWinCount=%s"
                       % (roomID, playerID, isWin, dayScore, pkScore, danLV, cWinCount))
    pkDanIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", danLV)
    if not pkDanIpyData:
        GameWorld.ErrLog("跨服PK房间段位数据异常! roomID=%s,playerID=%s,danLV=%s" % (roomID, playerID, danLV))
    baseScoreList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKScore", 2) # 胜负保底分
    wBaseScore = baseScoreList[0] if len(baseScoreList) > 0 else 0
    lBaseScore = baseScoreList[1] if len(baseScoreList) > 1 else 0
    wExScore = eval(IpyGameDataPY.GetFuncCompileCfg("CrossRealmPKScore", 3)) # 胜方附加分
    lExScore = 0
    if isWin:
        addScore = wBaseScore + wExScore
        pkPlayer.cWinCount += 1
    else:
        addScore = lBaseScore + lExScore
        pkPlayer.cWinCount = 0
    dayMaxScore = IpyGameDataPY.GetFuncCfg("CrossRealmPKScore", 1) # 每日获得积分上限,0为不限制
    if dayMaxScore and addScore:
        addScore = min(dayMaxScore - dayScore, addScore)
    pkPlayer.pkScore += addScore
    if pkDanIpyData and pkDanIpyData.GetLVUpScore() and pkPlayer.pkScore >= pkDanIpyData.GetLVUpScore():
        pkPlayer.danLV += 1
    return addScore
def ClientServerMsg_PKRobotOver(serverGroupID, playerInfoDict, tick):
    ## 收到子服同步的PK机器人结算
    if not GameWorld.IsCrossServer():
        GameWorld.ErrLog("非跨服服务器不处理跨服PK匹配请求!")
        return
    playerID = playerInfoDict["playerID"] # 角色ID
    isWinner = playerInfoDict["isWinner"] # 是否获胜
    if playerID not in PyGameData.g_crossPKZoneMatchRobotPlayerDict:
        GameWorld.DebugLog("玩家没有匹配到机器人,无法结算PK机器人奖励!", playerID)
        return
    pkPlayer = PyGameData.g_crossPKZoneMatchRobotPlayerDict.pop(playerID)
    if playerID in PyGameData.g_crossPKPlayerDict:
        PyGameData.g_crossPKPlayerDict.pop(playerID)
    zoneID = pkPlayer.pkZoneID
    seasonID = pkPlayer.seasonID
    GameWorld.Log("机器人跨服PK结算: isWinner=%s,zoneID=%s,seasonID=%s,pkScore=%s,danLV=%s,cWinCount=%s"
                  % (isWinner, zoneID, seasonID, pkPlayer.pkScore, pkPlayer.danLV, pkPlayer.cWinCount), playerID)
    roomID = 0
    addScore = UpdatePKPlayerScore(roomID, pkPlayer, isWinner)
    pkScore = pkPlayer.pkScore
    danLV = pkPlayer.danLV
    cWinCount = pkPlayer.cWinCount
    GameWorld.Log("    更新: addScore=%s,pkScore=%s,danLV=%s,cWinCount=%s" % (addScore, pkScore, danLV, cWinCount), playerID)
    if isWinner:
        winner, loser = pkPlayer, None
    else:
        winner, loser = None, pkPlayer
    # 更新排行榜
    UpdateCrossPKBillboard(zoneID, seasonID, winner, loser)
    # PK机器人默认结果数据
    overType = 0
    roundWinnerIDList = []
    notifyState = False
    winnerID = winner.playerID if winner else 0
    tagPlayerID = 0
    tagPlayerName = ""
    timeStr = GameWorld.GetCurrentDataTimeStr()
    playerOverDict = {}
    playerOverDict[playerID] = [roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList] \
                                + [serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
    serverGroupIDList = [pkPlayer.serverGroupID]
    GameWorld.DebugLog("同步子服战斗结果: seasonID=%s,timeStr=%s,roomID=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s"
                       % (seasonID, timeStr, roomID, overType, winnerID, roundWinnerIDList))
    # 同步子服
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKOverInfo, playerOverDict, serverGroupIDList)
    if playerID not in PyGameData.g_crossPKTodayPKRecordInfo:
        PyGameData.g_crossPKTodayPKRecordInfo[playerID] = []
    pkRecordList = PyGameData.g_crossPKTodayPKRecordInfo[playerID]
    pkRecordList.append([0, winnerID])
    #GameWorld.DebugLog("PyGameData.g_crossPKTodayPKRecordInfo=%s" % PyGameData.g_crossPKTodayPKRecordInfo)
    # 记录流向
    dataDict = {"zoneID":zoneID, "seasonID":seasonID, "pkPlayer":pkPlayer.GetDRInfo()}
    DR_CrossReamlPK("PKOverRobot", dataDict)
    return
def _GMSetCrossPK(serverGroupID, msgData):
    ## 收到子服GM同步的设置跨服PK数据
    if not GameWorld.IsCrossServer():
        GameWorld.ErrLog("GMSetCrossPK非跨服服务器不处理该跨服GM请求!")
        return
    zoneID = msgData["ZoneID"]
    seasonID = msgData["SeasonID"]
    playerInfoDict = msgData["PlayerInfo"]
    accID = playerInfoDict["accID"]
    playerID = playerInfoDict["playerID"]
    playerName = playerInfoDict["playerName"]
    playerJob = playerInfoDict["playerJob"]
    fightPower = playerInfoDict["fightPower"]
    realmLV = playerInfoDict["realmLV"]
    pkScore = playerInfoDict["pkScore"]
    danLV = playerInfoDict["danLV"]
    cWinCount = playerInfoDict["cWinCount"]
    ondayScore = playerInfoDict["ondayScore"]
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(zoneID, [])
    if playerID in zoneMatchPlayerList or playerID in PyGameData.g_crossPKZoneMatchRobotPlayerDict or playerID in PyGameData.g_crossPKZoneMatchRobotPlayerDict:
        GameWorld.ErrLog("GMSetCrossPK玩家正在匹配中,无法设置该GM请求数据! playerID=%s,accID=%s" % (playerID, accID))
        return
    pkPlayer = CrossPKPlayer()
    pkPlayer.accID = accID
    pkPlayer.playerID = playerID
    pkPlayer.playerName = playerName
    pkPlayer.playerJob = playerJob
    pkPlayer.pkScore = pkScore
    pkPlayer.danLV = danLV
    pkPlayer.fightPower = fightPower
    pkPlayer.realmLV = realmLV
    pkPlayer.cWinCount = cWinCount
    pkPlayer.ondayScore = ondayScore
    pkPlayer.serverGroupID = serverGroupID
    pkPlayer.pkZoneID = zoneID
    pkPlayer.seasonID = seasonID
    # 更新排行榜
    isOK = UpdateCrossPKBillboard(zoneID, seasonID, pkPlayer, None, True)
    GameWorld.Log("GMSetCrossPK设置跨服PK榜单玩家数据成功: isOK=%s,zoneID=%s,seasonID=%s,pkScore=%s,danLV=%s,cWinCount=%s,accID=%s"
                  % (isOK, zoneID, seasonID, pkScore, danLV, cWinCount, accID), playerID)
    # 记录流向
    dataDict = {"zoneID":zoneID, "seasonID":seasonID, "pkPlayer":pkPlayer.GetDRInfo()}
    DR_CrossReamlPK("GMSet", dataDict)
    return
##================================== 以下是子服逻辑 ==========================================
def OnGameServerInitOK():
    if GameWorld.IsCrossServer():
        return
    dbZoneID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    dbSeasonID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
    dbSeasonState = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    GameWorld.Log("OnGameServerInitOK dbZoneID=%s,dbSeasonID=%s,dbSeasonState=%s" % (dbZoneID, dbSeasonID, dbSeasonState))
    gameWorld = GameWorld.GetGameWorld()
    zoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    if not zoneID:
        gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID, dbZoneID)
        gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID, dbSeasonID)
        gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState, dbSeasonState)
        GameWorld.Log("    服务器启动取DB值设置本服赛区赛季信息: ")
    return
def OnMapServerInitOK():
    # 通知地图服务器状态
    if GameWorld.IsCrossServer():
        return
    gameWorld = GameWorld.GetGameWorld()
    zoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    seasonID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
    seasonState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    matchState = GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID, zoneID)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID, seasonID)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState, seasonState)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK, matchState)
    return
def CrossServerMsg_PKSeasonInfo(seasonInfo):
    ## 收到跨服服务器同步的赛季信息
    #seasonInfo = {"ZoneID":zoneID, "SeasonID":seasonID, "SeasonState":seasonState, "MatchState":matchState}
    GameWorld.Log("收到跨服服务器同步的赛季信息...")
    if not seasonInfo:
        return
    zoneID = seasonInfo.get("ZoneID", 0)
    seasonID = seasonInfo.get("SeasonID", 0)
    seasonState = seasonInfo.get("SeasonState", 0)
    matchState = seasonInfo.get("MatchState", 0)
    GameWorld.Log("    zoneID=%s,seasonID=%s,seasonState=%s,matchState=%s" % (zoneID, seasonID, seasonState, matchState))
    if not zoneID:
        return
    if not seasonID:
        dbSeasonID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
        if dbSeasonID:
            GameWorld.ErrLog("    已经有分配赛季ID的暂定不能被置为0! dbSeasonID=%s" % dbSeasonID)
            return
    gameWorld = GameWorld.GetGameWorld()
    curSeasonState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID, zoneID)
    gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID, seasonID)
    gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState, seasonState)
    gameWorld.SetDict(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK, matchState)
    # 强制覆盖存DB
    dbZoneID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    dbSeasonID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
    dbSeasonState = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    GameWorld.Log("    before dbZoneID=%s,dbSeasonID=%s,dbSeasonState=%s" % (dbZoneID, dbSeasonID, dbSeasonState))
    PlayerDBGSEvent.SetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID, zoneID)
    PlayerDBGSEvent.SetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID, seasonID)
    PlayerDBGSEvent.SetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState, seasonState)
    dbZoneID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    dbSeasonID = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID)
    dbSeasonState = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    GameWorld.Log("    update dbZoneID=%s,dbSeasonID=%s,dbSeasonState=%s" % (dbZoneID, dbSeasonID, dbSeasonState))
    if curSeasonState == 1 and seasonState == 2:
        pass
        #PlayerControl.WorldNotify(0, "NotifySeasonOver")
    # 通知地图服务器状态
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID, zoneID)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonID, seasonID)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState, seasonState)
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossDailyActionState % ShareDefine.DailyActionID_CrossReamPK, matchState)
    # 广播玩家赛季相关状态变更
    seasonStatePack = ChPyNetSendPack.tagGCCrossRealmPKSeasonState()
    seasonStatePack.ZoneID = zoneID
    seasonStatePack.SeasonID = seasonID
    seasonStatePack.SeasonState = seasonState
    seasonStatePack.MatchState = matchState
    seasonStatePack.CrossZoneName = GameWorld.GetCrossZoneName()
    seasonStatePack.CrossZoneNameLen = len(seasonStatePack.CrossZoneName)
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
            continue
        NetPackCommon.SendFakePack(curPlayer, seasonStatePack)
    return
def CrossServerMsg_PKMatchReqRet(retInfo):
    ## 跨服PK匹配请求结果
@@ -745,28 +1968,45 @@
    if not curPlayer:
        return
    
    if result == -2:
        PlayerControl.NotifyCode(curPlayer, "InCrossPKing")
    if not result:
        return
    
    if result == -2:
        PlayerControl.NotifyCode(curPlayer, "CrossMatching17")
        return
    startMatchPack = ChPyNetSendPack.tagGCCrossRealmPKStartMatch()
    if result == 1:
        NetPackCommon.SendFakePack(curPlayer, ChPyNetSendPack.tagGCCrossRealmPKStartMatch())
        SetIsCrossPKMatching(curPlayer, 1)
        startMatchPack.IsRobot = 0
    elif result == 2:
        SetIsCrossPKMatching(curPlayer, 0)
        startMatchPack.IsRobot = 1
        
    NetPackCommon.SendFakePack(curPlayer, startMatchPack)
    return
## 跨服匹配状态
def SetIsCrossPKMatching(curPlayer, isMatching):
    curPlayer.SetDict(ChConfig.Def_PlayerKey_IsCrossPKMatching, isMatching)
    sysMsg = str(isMatching)
    curPlayer.MapServer_QueryPlayerResult(0, 0, "CrossPKMatching", sysMsg, len(sysMsg))
    return
def GetIsCrossPKMatching(curPlayer): return curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_IsCrossPKMatching)
def CrossServerMsg_PKMatchResult(vsRoomDict):
    ## 跨服PK匹配结果
    curServerGroupID = GameWorld.GetServerGroupID()
    actionType = ShareDefine.Def_MergeAction_MergePK
    mapPosList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKMatch", 5)
    GameWorld.Log("=== 收到PK匹配结果处理  === curServerGroupID=%s" % curServerGroupID)
    GameWorld.DebugLog("=== 收到PK匹配结果处理  === curServerGroupID=%s" % curServerGroupID)
    if not mapPosList:
        GameWorld.ErrLog("没有配置对战地图进入坐标!")
        return
    
    for roomID, roomInfo in vsRoomDict.items():
        mapID, playerList = roomInfo
        GameWorld.Log("    roomID=%s,playerList=%s" % (roomID, playerList))
        GameWorld.DebugLog("    roomID=%s,playerList=%s" % (roomID, playerList))
        for i, playerInfo in enumerate(playerList):
            serverGroupID, playerID = playerInfo
            if serverGroupID != curServerGroupID:
@@ -782,7 +2022,7 @@
            PlayerControl.SetVsRoomId(player, roomID, True)
            # 通知地图玩家匹配成功, 上传数据, 准备进入跨服服务器
            posX, posY = mapPosList[i] if len(mapPosList) > i else mapPosList[0]
            CrossRealmPlayer.SendCrossRealmReg(player, actionType, mapID, mapID, 0, posX, posY)
            CrossRealmPlayer.SendCrossRealmReg(player, ChConfig.Def_FBMapID_CrossRealmPK, mapID, mapID, 0, posX, posY)
            
    return
@@ -790,13 +2030,18 @@
    ## 子服接收玩家已准备好的PK房间信息, 此房间里的玩家可传送进入跨服
    
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器通知已准备好的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    GameWorld.DebugLog("===收到跨服服务器通知已准备好的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    # serverGroupID, playerName, playerJob
    if not CrossRealmPlayer.IsCrossServerOpen():
        GameWorld.Log("跨服服务器维护中,不处理!")
        return
    
    for roomID, readyMemberDict in readyOKRoomList:
        for playerID, playerInfo in readyMemberDict.items():
            serverGroupID = playerInfo["ServerGroupID"]
            playerName = playerInfo["Name"]
            number = playerInfo["Number"]
            
            if serverGroupID != curServerGroupID:
                GameWorld.DebugLog("    不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID))
@@ -819,30 +2064,32 @@
                    matchPlayer.NameLen = len(matchPlayer.PlayerName)
                    matchPlayer.Job = readyPlayerInfo["Job"]
                    matchPlayer.LV = readyPlayerInfo["LV"]
                    matchPlayer.MaxHP = readyPlayerInfo["MaxHP"]
                    matchPlayer.MaxHP = readyPlayerInfo["MaxHP"] % ShareDefine.Def_PerPointValue
                    matchPlayer.MaxHPEx = readyPlayerInfo["MaxHP"] / ShareDefine.Def_PerPointValue
                    matchPlayer.MaxProDef = readyPlayerInfo["MaxProDef"]
                    break
                
            PlayerControl.SetCrossRealmState(player, 1)
            PlayerControl.SetCrossMapID(player, ChConfig.Def_FBMapID_CrossRealmPK)
            SetIsCrossPKMatching(player, 0)
            
            # 通知匹配成功,可进入跨服
            matchOKPack = ChPyNetSendPack.tagGCCrossRealmPKMatchOK()
            matchOKPack.RoomID = roomID
            matchOKPack.PlayerName = playerName
            matchOKPack.NameLen = len(matchOKPack.PlayerName)
            matchOKPack.Number = number
            matchOKPack.MatchPlayer = [matchPlayer]
            matchOKPack.MatchPlayerCount = len(matchOKPack.MatchPlayer)
            NetPackCommon.SendFakePack(player, matchOKPack)
            GameWorld.Log("    通知玩家进入跨服PK对战房间! roomID=%s,playerID=%s,matchPlayerID=%s" % (roomID, playerID, matchPlayer.PlayerID))
            GameWorld.DebugLog("    通知玩家进入跨服PK对战房间! roomID=%s,playerID=%s,matchPlayerID=%s" % (roomID, playerID, matchPlayer.PlayerID))
            
            # 到这里默认认为一定会有结果的,所以本服直接增加次数
            #player.MapServer_QueryPlayerResult(0, 0, 'MergePKAddCnt', "", 0)
    return
def CrossServerMsg_PKTimeoutRoomList(timeoutRoomDict):
    ## 子服接收已超时的PK房间信息, 此房间里的玩家重置跨服状态
    
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器通知已超时的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    GameWorld.DebugLog("===收到跨服服务器通知已超时的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    
    for roomID, roomPlayerInfo in timeoutRoomDict.items():
        if not roomPlayerInfo:
@@ -865,45 +2112,158 @@
            GameWorld.DebugLog("    房间ID不同, playerID=%s" % (playerID))
            continue
        player.SetDict(ChConfig.Def_PlayerKey_IsLoginToMergeServer, 0)
        PlayerControl.SetCrossRealmState(player, 0)
        PlayerControl.SetCrossMapID(player, 0)
        
    return
#跨服竞技场未通知玩家的比赛结果,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
class CrossPKUnNotifyOverInfoManager(object):
    def __init__(self):
        self.__unNotifyOverInfoDict = {} # {playerID:tagDBCrossPKUnNotifyOverInfo, ...}
        return
    def AddUnNotifyOverInfo(self, playerID, overInfoData):
        self.__unNotifyOverInfoDict[playerID] = overInfoData
        return
    def GetPlayerUnNotifyOverInfo(self, playerID): return self.__unNotifyOverInfoDict.pop(playerID, None)
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for overInfoData in self.__unNotifyOverInfoDict.values():
            cnt += 1
            savaData += overInfoData.getBuffer()
        GameWorld.Log("SaveDBCrossPKUnNotifyOverInfo cnt :%s len=%s" % (cnt, len(savaData)))
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("LoadDBCrossPKUnNotifyOverInfo cnt :%s" % cnt)
        for _ in xrange(cnt):
            overInfoData = PyGameDataStruct.tagDBCrossPKUnNotifyOverInfo()
            overInfoData.clear()
            pos += overInfoData.readData(datas, pos, dataslen)
            self.__unNotifyOverInfoDict[overInfoData.PlayerID] = overInfoData
        return pos
def CrossServerMsg_PKOverInfo(playerOverDict):
    ## 子服接收跨服PK结果信息
    
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器同步的跨服PK结果=== curServerGroupID=%s" % curServerGroupID)
    GameWorld.DebugLog("===收到跨服服务器同步的跨服PK结果=== curServerGroupID=%s" % curServerGroupID)
    
    for playerID, overInfo in playerOverDict.items():
        roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, \
        roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, \
            serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState = overInfo
        if serverGroupID != curServerGroupID:
            GameWorld.DebugLog("    不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID))
            continue
        
        sendMapOverInfo = [roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
        sendMapOverInfo = [roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
        player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not player:
            CrossRealmPlayer.DoOfflinePlayerExitCrossServer(playerID)
        if not player or PlayerControl.GetIsTJG(player):
            GameWorld.Log("    玩家不在线 或脱机中,先缓存,玩家上线后再同步,playerID=%s" % (playerID))
            PyGameData.g_crossPKUnNotifyOverInfo[playerID] = sendMapOverInfo
            GameWorld.DebugLog("    玩家不在线 或脱机中,先缓存,玩家上线后再同步,playerID=%s" % (playerID))
            overInfoData = PyGameDataStruct.tagDBCrossPKUnNotifyOverInfo()
            overInfoData.clear()
            overInfoData.ZoneID = zoneID
            overInfoData.SeasonID = seasonID
            overInfoData.RoomID = roomID
            overInfoData.TimeStr = timeStr
            overInfoData.OverType = overType
            overInfoData.PlayerID = playerID
            overInfoData.WinnerID = winnerID
            overInfoData.RoundWinnerInfo = str(roundWinnerIDList)
            overInfoData.RoundWinnerLen = len(overInfoData.RoundWinnerInfo)
            overInfoData.PKScore = pkScore
            overInfoData.DanLV = danLV
            overInfoData.CWinCount = cWinCount
            overInfoData.AddScore = addScore
            overInfoData.TagPlayerID = tagPlayerID
            overInfoData.TagPlayerName = tagPlayerName
            PyDataManager.GetCrossPKUnNotifyOverInfoManager().AddUnNotifyOverInfo(playerID, overInfoData)
            continue
        
        PlayerControl.SetVsRoomId(player, 0)
        sysMsg = str(sendMapOverInfo)
        player.MapServer_QueryPlayerResult(0, 0, "CrossPKOverInfo", sysMsg, len(sysMsg))
        GameWorld.Log("通知地图跨服PK结算: roomID=%s,seasonID=%s,timeStr=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s, pkScore=%s,danLV=%s,cWinCount=%s,addScore=%s,tagPlayerID=%s,notifyState=%s,mapID=%s"
                      % (roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, notifyState, player.GetMapID()), playerID)
        GameWorld.DebugLog("通知地图跨服PK结算: roomID=%s,zoneID=%s,seasonID=%s,timeStr=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s, pkScore=%s,danLV=%s,cWinCount=%s,addScore=%s,tagPlayerID=%s,notifyState=%s,mapID=%s"
                           % (roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, notifyState, player.GetMapID()), playerID)
    return
def __OnLoginNotifyPKOverInfo(curPlayer):
    playerID = curPlayer.GetPlayerID()
    if playerID not in PyGameData.g_crossPKUnNotifyOverInfo:
    overInfoData = PyDataManager.GetCrossPKUnNotifyOverInfoManager().GetPlayerUnNotifyOverInfo(playerID)
    if not overInfoData:
        return
    overInfo = PyGameData.g_crossPKUnNotifyOverInfo.pop(playerID)
    PlayerControl.SetCrossRealmState(curPlayer, 0)
    sysMsg = str(overInfo)
    PlayerControl.SetCrossMapID(curPlayer, 0)
    PlayerControl.SetVsRoomId(curPlayer, 0)
    zoneID = overInfoData.ZoneID
    seasonID = overInfoData.SeasonID
    roomID = overInfoData.RoomID
    timeStr = overInfoData.TimeStr
    overType = overInfoData.OverType
    #playerID = overInfoData.PlayerID
    winnerID = overInfoData.WinnerID
    roundWinnerIDList = []
    try:
        roundWinnerIDList = eval(overInfoData.RoundWinnerInfo)
    except:
        GameWorld.ErrLog("__OnLoginNotifyPKOverInfo roundWinnerIDList eval error! RoundWinnerInfo=%s" % overInfoData.RoundWinnerInfo, playerID)
    pkScore = overInfoData.PKScore
    danLV = overInfoData.DanLV
    cWinCount = overInfoData.CWinCount
    addScore = overInfoData.AddScore
    tagPlayerID = overInfoData.TagPlayerID
    tagPlayerName = overInfoData.TagPlayerName
    notifyState = 0 # 登录才通知的默认未通知
    sendMapOverInfo = [roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
    sysMsg = str(sendMapOverInfo)
    curPlayer.MapServer_QueryPlayerResult(0, 0, "CrossPKOverInfo", sysMsg, len(sysMsg))
    GameWorld.Log("玩家上线通知地图未结算的跨服PK结算: mapID=%s,overInfo=%s" % (curPlayer.GetMapID(), overInfo), playerID)
    GameWorld.DebugLog("玩家上线通知地图未结算的跨服PK结算: roomID=%s,zoneID=%s,seasonID=%s,timeStr=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s, pkScore=%s,danLV=%s,cWinCount=%s,addScore=%s,tagPlayerID=%s,notifyState=%s,mapID=%s"
                       % (roomID, zoneID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, notifyState, curPlayer.GetMapID()), playerID)
    return
def DR_CrossReamlPK(eventName, dataDict={}):
    drDataDict = {}
    drDataDict.update(dataDict)
    DataRecordPack.SendEventPack("CrossPK_%s" % eventName, drDataDict)
    return
def IsCrossRealmPKPlayer(playerID, checkPreSeason=False, checkAllSeason=False):
    ## 仅跨服服务器判断用
    # @param checkPreSeason: 检查上一赛季
    # @param checkAllSeason: 检查所有赛季
    # 默认取分区1的赛季作为当前赛季,所有分区赛季ID相同,且递增
    gameWorld = GameWorld.GetGameWorld()
    nowSeasonID = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonID % 1)
    preSeasonID = nowSeasonID - 1
    crossPKBillboardMgr = PyDataManager.GetCrossPKBillboardManager()
    crossZoneName = GameWorld.GetCrossZoneName()
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetCrossRealmPKSeasonCount()):
        seasonIpyData = ipyDataMgr.GetCrossRealmPKSeasonByIndex(index)
        if crossZoneName != seasonIpyData.GetCrossZoneName():
            continue
        zoneID = seasonIpyData.GetZoneID()
        seasonID = seasonIpyData.GetSeasonID()
        if checkAllSeason or seasonID == nowSeasonID or (checkPreSeason and seasonID == preSeasonID):
            pass
        else:
            continue
        _, orderDict = crossPKBillboardMgr.GetCrossPKBillboardInfo(zoneID, seasonID)
        if orderDict and playerID in orderDict:
            return True
    return False