5424 【后端】【1.4】跨服竞技场开发(流程调通版,可匹配、PK、结算,其他功能没有)
18个文件已修改
9个文件已添加
2447 ■■■■■ 已修改文件
ServerPython/CoreServerGroup/GameServer/Script/GM/GMShell.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmPK.py 909 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldMergeKing.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/ServerCommScript.ini 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py 498 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NetPackCommon.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossPKOverInfo.py 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossRealmReg.py 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_SetPlayerAttr.py 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/ServerScript.ini 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GM/GMShell.py
@@ -23,6 +23,7 @@
import PyGameData
import traceback
import GMCommon
import ShareDefine
import os
#---------------------------------------------------------------------
g_broadCastList = []
@@ -92,7 +93,7 @@
            if callFunc != None:
                extendParamList = callFunc(curPlayer)
                alist.extend(extendParamList)
                MergeChildMsg.SendMergerChildToCenterStringData(ChConfig.Def_SendGMCMD, alist)
                MergeChildMsg.SendMergerChildToCenterStringData(ShareDefine.ClientServerMsg_GMCMD, alist)
                return
            
        callFunc = GameWorld.GetExecFunc(Commands, "%s.%s"%(callFunName, "OnExec"))
@@ -245,7 +246,7 @@
    return cmdDict
## 收到子服务器发送的GM命令
def ClientServer_SendGMCMD(cmdMsgList, tick):
def ClientServerMsg_GMCMD(cmdMsgList, tick):
    if len(cmdMsgList) == 0:
        return 
    
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
New file
@@ -0,0 +1,143 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package CrossRealmMsg
#
# @todo:跨服信息管理
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服信息管理
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import ShareDefine
import PlayerControl
import IPY_GameServer
import CrossRealmPlayer
import CrossRealmPK
import GMShell
import traceback
def SendMsgToCrossServer(msgType, dataMsg):
    ## 发送信息到跨服服务器上
    if GameWorld.IsCrossServer():
        return
    if not dataMsg:
        return
    sendMsg = str({"MsgType":msgType, "Data":dataMsg, "ServerGroupID":GameWorld.GetServerGroupID()})
    GameWorld.Log("SendMsgToCrossServer => %s" % (sendMsg))
    GameWorld.GetGameWorld().SendMergerChildToCenterStringData(sendMsg, len(sendMsg))
    return
def OnCrossServerReceiveMsg(recvMsg, tick):
    ## 跨服服务器收到信息处理
    try:
        GameWorld.Log("OnCrossServerReceiveMsg: %s" % recvMsg)
        msgDict = eval(recvMsg)
        msgType = msgDict.get("MsgType", -1)
        msgData = msgDict.get("Data", "")
        serverGroupID = msgDict.get("ServerGroupID", 0)
        if msgType == ShareDefine.ClientServerMsg_PKMatch:
            CrossRealmPK.ClientServerMsg_PKMatch(serverGroupID, msgData, tick)
        elif msgType == ShareDefine.ClientServerMsg_PKCancel:
            CrossRealmPK.ClientServerMsg_PKCancel(msgData, tick)
        elif msgType == ShareDefine.ClientServerMsg_PKPrepareOK:
            CrossRealmPK.ClientServerMsg_PKPrepareOK(msgData, tick)
        elif msgType == ShareDefine.ClientServerMsg_GMCMD:
            GMShell.ClientServerMsg_GMCMD(msgData, tick)
        elif msgType == ShareDefine.ClientServerMsg_ServerInitOK:
            ClientServerMsg_ServerInitOK(serverGroupID, msgData, tick)
        else:
            GameWorld.ErrLog("没有该信息类型逻辑处理!")
    except:
        GameWorld.ErrLog("OnCrossServerReceiveMsg:%s; except:%s" % (recvMsg, traceback.format_exc()))
        if GameWorld.GetGameWorld().GetDebugLevel():
            raise BaseException(str(traceback.format_exc()))
    return
def ClientServerMsg_ServerInitOK(serverGroupID, msgData, tick):
    ''' 收到子服启动成功通知
         当子服启动成功后,可同步一次跨服服务器活动状态及活动数据给子服
    '''
    GameWorld.Log("收到跨服子服启动成功通知!")
    CrossRealmPK.ClientServerMsg_ServerInitOK(serverGroupID, tick)
    return
## ================================================================================================
def SendMsgToClientServer(msgType, dataMsg, serverGroupIDList=[]):
    ''' 广播信息到子服务器上
        @param serverGroupIDList: 发送指定的服务器组ID列表,内部已经针对列表中组ID去重,
        所以外部逻辑可直接添加,不用考虑组ID重复问题,没有指定服务器组ID时,默认广播所有子服
    '''
    if not GameWorld.IsCrossServer():
        return
    if not dataMsg:
        return
    sendMsg = str({"MsgType":msgType, "Data":dataMsg})
    GameWorld.Log("SendMsgToClientServer => serverGroupIDList=%s, sendMsg=%s" % (serverGroupIDList, sendMsg))
    if not serverGroupIDList:
        GameWorld.GetGameWorld().SendBroadcastMergeClient(sendMsg)
    else:
        serverGroupIDList = list(set(serverGroupIDList)) # 去重
        for serverGroupID in serverGroupIDList:
            GameWorld.GetGameWorld().SendMergeMsgToClientByGroupID(serverGroupID, sendMsg)
    return
def OnClientServerReceiveMsg(index, tick):
    ## 子服收到跨服服务器信息
    dataPack = IPY_GameServer.IPY_MGBroadcastMergeClient()
    dataMsg = dataPack.GetData()
    GameWorld.Log("OnClientServerReceiveMsg: %s" % dataMsg)
    try:
        msgDict = eval(dataMsg)
        msgType = msgDict.get("MsgType", -1)
        msgData = msgDict.get("Data", "")
        if msgType == ShareDefine.CrossServerMsg_ExitCrossServer:
            CrossRealmPlayer.CrossServerMsg_ExitCrossServer(msgData)
        elif msgType == ShareDefine.CrossServerMsg_Notify:
            PlayerControl.CrossServerMsg_Notify(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKMatchReqRet:
            CrossRealmPK.CrossServerMsg_PKMatchReqRet(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKMatchResult:
            CrossRealmPK.CrossServerMsg_PKMatchResult(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKReadyOKRoomList:
            CrossRealmPK.CrossServerMsg_PKReadyOKRoomList(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKTimeoutRoomList:
            CrossRealmPK.CrossServerMsg_PKTimeoutRoomList(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKOverInfo:
            CrossRealmPK.CrossServerMsg_PKOverInfo(msgData)
        else:
            GameWorld.ErrLog("没有该信息类型逻辑处理!")
    except:
        GameWorld.ErrLog("OnClientServerReceiveMsg:%s; except:%s" % (dataMsg, traceback.format_exc()))
        if GameWorld.GetGameWorld().GetDebugLevel():
            raise BaseException(str(traceback.format_exc()))
    return
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmPK.py
New file
@@ -0,0 +1,909 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package CrossRealmPK
#
# @todo:跨服PK竞技场
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服PK竞技场
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import PlayerControl
import CrossRealmMsg
import CrossRealmPlayer
import ChPyNetSendPack
import NetPackCommon
import IpyGameDataPY
import ShareDefine
import PyGameData
import ChConfig
import operator
import random
PKPlayerState_Matching = 0
PKPlayerState_Fighting = 1
class CrossPKPlayer():
    ## 跨服PK玩家类
    def __init__(self):
        self.accID = ""
        self.playerID = 0
        self.playerName = ""
        self.playerJob = 0
        self.playerLV = 0
        self.maxHP = 0
        self.fightPower = 0
        self.pkScore = 0
        self.danLV = 0
        self.matchTick = 0
        self.cWinCount = 0 # 连胜次数
        self.ondayScore = 0 # 过天时的积分
        self.serverGroupID = 0 # 所属服务器ID,一个服务器ID由多个服组成
        self.pkZoneID = 0 # 所属赛区ID,一个赛区由多个服务器ID组成
        self.seasonID = 0 # 赛季ID
        return
class CrossPKRoom():
    ## 跨服PK房间类
    def __init__(self):
        self.pkZoneID = 0
        self.roomID = 0
        self.mapID = 0
        self.openTick = 0 # 开房时间
        self.readyTick = 0 # 玩家都准备好的时间
        self.roomState = ShareDefine.Def_VsRoom_State_WaitPlayer # 默认状态
        self.roomPlayerIDList = [] # 对战玩家ID列表
        self.readyPlayerIDList = [] # 已经准备好的玩家ID列表
        self.isMapOpen = False # 地图是否已经开启该房间,未开启的房间超时后,本次匹配视为无效,有玩家进地图才会开启副本分线
        return
################################################################################
def OnPlayerLogin(curPlayer):
    if not GameWorld.IsCrossServer():
        __OnLoginNotifyPKOverInfo(curPlayer)
    return
## 玩家离线处理
def OnLeaveServer(curPlayer):
    # 发送取消匹配
    SendCancelCrossRealmPKMatch(curPlayer, "PlayerDisconnect")
    return
def IsCrossRealmPKOpen():
    ## 跨服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
def SendCancelCrossRealmPKMatch(curPlayer, reason):
    ## 发送取消匹配
    # 跨服服务器不处理
    if GameWorld.IsCrossServer():
        return
    # 非活动中不处理
    if not IsCrossRealmPKOpen():
        return
#    # 如果是要登陆到跨服服务器的,不发送取消
#    if curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_IsLoginToMergeServer):
#        GameWorld.DebugLog("本次离线为要登陆跨服服务器的自动离线行为,不发送取消匹配!", curPlayer.GetPlayerID())
#        curPlayer.SetDict(ChConfig.Def_PlayerKey_IsLoginToMergeServer, 0)
#        return
    vsRoomID = curPlayer.GetVsRoomId()
    if vsRoomID and PlayerControl.GetCrossRealmState(curPlayer) == 1:
        GameWorld.DebugLog("玩家跨服PK状态,不能取消匹配!vsRoomID=%s" % vsRoomID, curPlayer.GetPlayerID())
        return
    dataMsg = {"accID":curPlayer.GetAccID(), # 账号
               "playerID":curPlayer.GetPlayerID(), # 玩家ID
               "playerName":curPlayer.GetName(), # 跨服子服玩家名
               "reason":reason, # 取消原因
               "vsRoomID":vsRoomID, # 对战房间ID
               }
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKCancel, dataMsg)
    PlayerControl.SetVsRoomId(curPlayer, 0)
    GameWorld.Log("发送取消跨服PK匹配到跨服服务器:dataMsg=%s" % str(dataMsg), curPlayer.GetPlayerID())
    return
def ClientServerMsg_PKMatch(serverGroupID, playerInfoDict, tick):
    ## 请求匹配
    if not GameWorld.IsMergeServer():
        GameWorld.ErrLog("非跨服服务器不处理跨服PK匹配请求!")
        return
    if not IsCrossRealmPKOpen():
        GameWorld.Log("跨服匹配PK活动未开启,不允许请求匹配!")
        return
    seasonID = playerInfoDict["seasonID"] # 赛季ID
    pkZoneID = playerInfoDict["pkZoneID"] # 所属赛区
    accID = playerInfoDict["accID"] # 角色账号
    playerID = playerInfoDict["playerID"] # 角色ID
    playerName = playerInfoDict["playerName"] # 玩家名
    job = playerInfoDict["playerJob"] # ְҵ
    playerLV = playerInfoDict["playerLV"] # ְҵ
    maxHP = playerInfoDict["maxHP"] # ְҵ
    fightPower = playerInfoDict["fightPower"] # 战斗力
    pkScore = playerInfoDict["pkScore"] # 当前积分
    danLV = playerInfoDict["danLV"] # 当前段位
    cWinCount = playerInfoDict["cWinCount"] # 连胜次数
    ondayScore = playerInfoDict["ondayScore"] # 过天时的积分
    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
    pkPlayer = CrossPKPlayer()
    pkPlayer.accID = accID
    pkPlayer.playerID = playerID
    pkPlayer.playerName = playerName
    pkPlayer.playerJob = job
    pkPlayer.playerLV = playerLV
    pkPlayer.maxHP = maxHP
    pkPlayer.pkScore = pkScore
    pkPlayer.danLV = danLV
    pkPlayer.fightPower = fightPower
    pkPlayer.matchTick = tick
    pkPlayer.cWinCount = cWinCount
    pkPlayer.ondayScore = ondayScore
    pkPlayer.serverGroupID = serverGroupID
    pkPlayer.pkZoneID = pkZoneID
    pkPlayer.seasonID = seasonID
    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)))
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchReqRet, [playerID, 1], [serverGroupID])
    return
def ClientServerMsg_PKCancel(playerInfoDict, tick):
    ## 取消匹配
    if not GameWorld.IsMergeServer():
        GameWorld.ErrLog("非跨服服务器不处理取消跨服PK匹配!")
        return
    # 非活动中不处理
    if not IsCrossRealmPKOpen():
        return
    accID = playerInfoDict["accID"] # 角色账号
    playerID = playerInfoDict["playerID"] # 玩家ID
    reason = playerInfoDict["reason"] # 取消原因
    vsRoomID = playerInfoDict["vsRoomID"] # 所属对战房间ID
    if vsRoomID in PyGameData.g_crossPKRoomDict:
        pkRoom = PyGameData.g_crossPKRoomDict[vsRoomID]
        if pkRoom.isMapOpen or pkRoom.readyTick:
            GameWorld.Log("跨服对战房间已经开启了线路,或者双方数据都已传输完毕,不可再取消匹配!vsRoomID=%s" % vsRoomID)
            return
    GameWorld.Log("玩家取消匹配: 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))
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
    if playerID in zoneMatchPlayerList:
        zoneMatchPlayerList.remove(playerID)
        GameWorld.Log("    从匹配队列中删除,匹配队列剩余人数=%s" % (len(zoneMatchPlayerList)))
    #取消所有存在该玩家的房间,子服不一定知道玩家当前最新所属房间ID, 故只能通过遍历删除已经为玩家创建的房间
    for roomID, pkRoom in PyGameData.g_crossPKRoomDict.items():
        if playerID not in pkRoom.roomPlayerIDList:
            continue
        for roomPlayerID in pkRoom.roomPlayerIDList:
            if roomPlayerID == playerID:
                GameWorld.Log("    自己不处理: 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)))
        PyGameData.g_crossPKRoomDict.pop(roomID)
        GameWorld.Log("    移除房间: popRoomID=%s" % (roomID))
        break
    return
def ClientServerMsg_PKPrepareOK(playerInfoDict, tick):
    ## 玩家跨服对战数据准备OK
    if not GameWorld.IsMergeServer():
        GameWorld.ErrLog("非跨服服务器不处理取消跨服PK匹配!")
        return
    accID = playerInfoDict["accID"] # 玩家账号
    playerID = playerInfoDict["playerID"] # 玩家ID
    vsRoomID = playerInfoDict["vsRoomID"] # 所属对战房间ID
    if playerID not in PyGameData.g_crossPKPlayerDict:
        GameWorld.ErrLog("玩家跨服对战数据准备OK, 但找不到该对战玩家信息!vsRoomID=%s,playerID=%s" % (vsRoomID, playerID))
        return
    #pkPlayer = PyGameData.g_crossPKPlayerDict[playerID]
    if vsRoomID not in PyGameData.g_crossPKRoomDict:
        GameWorld.ErrLog("玩家跨服对战数据准备OK, 但找不到该对战房间(%s)!可能对手已取消!" % vsRoomID)
        return
    vsRoom = PyGameData.g_crossPKRoomDict[vsRoomID]
    if vsRoom.roomState != ShareDefine.Def_VsRoom_State_WaitPlayer:
        GameWorld.ErrLog("玩家跨服对战数据准备OK, 但房间状态非等待状态, state=%s!" % vsRoom.roomState)
        return
    if playerID not in vsRoom.readyPlayerIDList:
        vsRoom.readyPlayerIDList.append(playerID)
    GameWorld.Log("玩家跨服PK准备完毕: accID=%s,playerID=%s,vsRoomID=%s" % (accID, playerID, vsRoomID))
    return
def __ReadyOKRoomPlayerProcess(tick):
    ## 玩家跨服PK已准备好的房间处理
    #GameWorld.Log("===已准备好的对战房间处理===")
    serverGroupIDList = []
    sendReadyOKRoomList = []
    for roomID, vsRoom in PyGameData.g_crossPKRoomDict.items():
        # 非等待状态的房间不处理
        if vsRoom.roomState != ShareDefine.Def_VsRoom_State_WaitPlayer:
            continue
        if not vsRoom.roomPlayerIDList:
            continue
        pkZoneID = 0
        isAllReady = True
        roomGroupIDList = []
        readyMemberDict = {} # 已准备好的玩家信息
        for roomPlayerID in vsRoom.roomPlayerIDList:
            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}
        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)))
        sendReadyOKRoomList.append([roomID, readyMemberDict])
        serverGroupIDList += roomGroupIDList
    # 将已准备好的房间广播到子服
    if sendReadyOKRoomList:
        GameWorld.Log("    已准备好的对战房间数: %s" % len(sendReadyOKRoomList))
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKReadyOKRoomList, sendReadyOKRoomList, serverGroupIDList)
    return
def OnPKMatchProcess(tick):
    ## 玩家跨服PK匹配定时处理逻辑
    # 非跨服服务器不处理跨服PK匹配逻辑
    if not GameWorld.IsMergeServer():
        return
    if not IsCrossRealmPKOpen():
        return
    # 同步子服排行榜
    #__SyncBillboardToClientServer(False, tick)
    processTick = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 1)
    processTickKey = "PKMatchLastTick"
    lastProcessTick = GameWorld.GetGameWorld().GetDictByKey(processTickKey)
    if tick - lastProcessTick < processTick:
        return
    GameWorld.GetGameWorld().SetDict(processTickKey, tick)
    # 处理超时的房间
    __DoCheckRoomTimeout(tick)
    # 通知已准备好的房间玩家可进入跨服
    __ReadyOKRoomPlayerProcess(tick)
    maxGroupCnt = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 2)
    outTimeTick = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 3)
    # 每个赛区单独匹配
    for pkZoneID, matchPlayerIDList in PyGameData.g_crossPKZoneMatchPlayerDict.items():
        matchPlayerCount = len(matchPlayerIDList)
        if matchPlayerCount < 2:
            #GameWorld.Log("匹配PK人数不足,不处理!pkZoneID=%s, 总人数:%s" % (pkZoneID, matchPlayerCount))
            continue
        GameWorld.Log("★★★★★★★★★★开始跨服PK匹配(pkZoneID=%s, 总人数:%s)★★★★★★★★★★" % (pkZoneID, matchPlayerCount))
        matchPlayerList = []
        for matchPlayerID in matchPlayerIDList:
            if matchPlayerID not in PyGameData.g_crossPKPlayerDict:
                continue
            matchPlayerList.append(PyGameData.g_crossPKPlayerDict[matchPlayerID])
        # 按匹配时间、积分升序排序
        matchTickSortList = sorted(matchPlayerList, key=operator.attrgetter("matchTick"))
        scoreSortList = sorted(matchPlayerList, key=operator.attrgetter("pkScore"))
        matchPlayerVSList = [] # 成功匹配玩家对战列表
        # 优先匹配等待超时玩家
        __DoMatch_OutTimePlayer(matchTickSortList, scoreSortList, outTimeTick, matchPlayerVSList, tick)
        if len(matchPlayerVSList) < maxGroupCnt:
            # 再按积分段匹配玩家
            __DoMatch_DanScorePlayer(scoreSortList, maxGroupCnt, matchPlayerVSList)
        # 给成功匹配的玩家非配对战房间
        matchPlayerVSList = matchPlayerVSList[:maxGroupCnt]
        __DoSetVSRoom(pkZoneID, matchPlayerVSList, tick)
        GameWorld.Log("==========匹配结束(总匹配队伍:%s)==========" % len(matchPlayerVSList))
    return
def __DoMatch_OutTimePlayer(matchTickSortList, scoreSortList, outTimeTick, matchPlayerVSList, tick):
    '''匹配超时玩家
        匹配中的玩家按积分排序,最后一个默认匹配上一个,第一个默认匹配下一个,其他匹配前后积分差绝对值较小的一个
    '''
    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))
            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
    # 按分段玩家随机匹配
    danList = danPlayerListDict.keys()
    random.shuffle(danList) # 打乱段位顺序
    GameWorld.Log("    积分分段个数: %s, %s" % (len(danList), 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)))
    doCount = 0
    while len(matchPlayerVSList) < maxGroupCnt and doCount < maxGroupCnt:
        doCount += 1
        isMatchOK = False
        for danLV in danList:
            danPlayerList = danPlayerListDict[danLV]
            danPlayerCount = len(danPlayerList)
            if danPlayerCount < 2:
                GameWorld.Log("    段位人数少于2个,此段位本轮轮空!doCount=%s,danLV=%s" % (doCount, danLV))
                continue
            vsIndexList = random.sample(xrange(danPlayerCount), 2) # 随机取两个索引对战
            vsIndexList.sort()
            aPlayer = danPlayerList.pop(vsIndexList[1])
            bPlayer = danPlayerList.pop(vsIndexList[0])
            matchPlayerVSList.append([aPlayer, 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))
            break
    return
def __DoSetVSRoom(pkZoneID, matchPlayerVSList, tick):
    ## 设置对战房间
    if not matchPlayerVSList:
        return
    vsRoomDict = {}
    serverGroupIDList = []
    zoneMatchPlayerList = PyGameData.g_crossPKZoneMatchPlayerDict.get(pkZoneID, [])
    mapIDList = IpyGameDataPY.GetFuncCfg("CrossRealmPKMatch", 4)
    GameWorld.Log("===给配对的玩家开房间(pkZoneID=%s,配对数:%s)===" % (pkZoneID, len(matchPlayerVSList)))
    for aPlayer, bPlayer in matchPlayerVSList:
        if not aPlayer or not bPlayer:
            continue
        aPlayerID = aPlayer.playerID
        bPlayerID = bPlayer.playerID
        if aPlayerID not in PyGameData.g_crossPKPlayerDict or bPlayerID not in PyGameData.g_crossPKPlayerDict:
            GameWorld.ErrLog("玩家匹配数据异常!aPlayerID=%s,bPlayerID=%s" % (aPlayerID, bPlayerID))
            continue
        roomID = __GetNewRoomID()
        if not roomID:
            GameWorld.ErrLog("无法创建房间!该房间已经存在!PyGameData.g_crossPKRoomID=%s" % PyGameData.g_crossPKRoomID)
            continue
        mapID = random.choice(mapIDList)
        newRoom = CrossPKRoom()
        newRoom.pkZoneID = pkZoneID
        newRoom.roomID = roomID
        newRoom.mapID = mapID
        newRoom.openTick = tick
        newRoom.roomPlayerIDList = [aPlayerID, bPlayerID]
        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))
        vsRoomDict[roomID] = [mapID, [[aServerGroupID, aPlayerID], [bServerGroupID, bPlayerID]]]
        serverGroupIDList.append(aServerGroupID)
        serverGroupIDList.append(bServerGroupID)
        # 移除匹配队列
        if aPlayerID in zoneMatchPlayerList:
            zoneMatchPlayerList.remove(aPlayerID)
        if bPlayerID in zoneMatchPlayerList:
            zoneMatchPlayerList.remove(bPlayerID)
        PyGameData.g_crossPKZoneMatchPlayerDict[pkZoneID] = zoneMatchPlayerList
    # 将匹配结果广播到子服
    if vsRoomDict:
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKMatchResult, vsRoomDict, serverGroupIDList)
    return
def __GetNewRoomID():
    ## 获取新房间ID, 房间号直接自增,一定不会重复,除非自增一轮后房间ID还没有释放
    for _ in xrange(100):
        newRoomID = PyGameData.g_crossPKRoomID + 1
        if newRoomID > 65530:
            newRoomID = 1
        PyGameData.g_crossPKRoomID = newRoomID
        if newRoomID not in PyGameData.g_crossPKRoomDict:
            return newRoomID
    return 0
def __DoCheckRoomTimeout(tick):
    ## 处理超时的房间
    timeoutRoomDict = {}
    serverGroupIDList = []
    #roomTimeout = IpyGameDataPY.GetFuncCfg("CheckRoomTimeout", 1) * 1000 # 这个时间尽量长点,目前暂时不确定玩家从准备好到进入到地图的时长
    roomTimeout = 180 * 1000 # 这个时间尽量长点,目前暂时不确定玩家从准备到进入到地图的时长
    for roomID, pkRoom in PyGameData.g_crossPKRoomDict.items():
        if pkRoom.isMapOpen or not pkRoom.readyTick:
            continue
        if tick - pkRoom.readyTick <= roomTimeout:
            continue
        pkZoneID = pkRoom.pkZoneID
        GameWorld.Log("PK房间等待玩家进来超时,没有玩家进来,关闭该房间!pkZoneID=%s,roomID=%s,openTick=%s,readyTick=%s,tick=%s"
                      % (pkZoneID, roomID, pkRoom.openTick, pkRoom.readyTick, tick))
        roomPlayerInfo = []
        for roomPlayerID in pkRoom.roomPlayerIDList:
            pkPlayer = PyGameData.g_crossPKPlayerDict.pop(roomPlayerID, None)
            if not pkPlayer:
                continue
            serverGroupID = pkPlayer.serverGroupID
            GameWorld.Log("    移除玩家,玩家需重新手动匹配,serverGroupID=%s,roomPlayerID=%s" % (serverGroupID, roomPlayerID))
            serverGroupIDList.append(serverGroupID)
            roomPlayerInfo.append([serverGroupID, roomPlayerID])
        timeoutRoomDict[roomID] = roomPlayerInfo
        PyGameData.g_crossPKRoomDict.pop(roomID)
    # 将超时房间广播到子服
    if timeoutRoomDict:
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKTimeoutRoomList, timeoutRoomDict, serverGroupIDList)
    return
def MapServer_CrossPKRoomOpen(msgList):
    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
    GameWorld.Log("MapServer_CrossPKRoomOpen => roomID=%s" % roomID)
    return
def MapServer_MergePKOver(infoList):
    ## 收到MapServer副本跨服PK结果同步
    GameWorld.Log("收到MapServer_跨服PK战斗结果: %s" % str(infoList))
    roomID, winnerID, loserID, roundWinnerIDList, overType = infoList
    if roomID not in PyGameData.g_crossPKRoomDict:
        GameWorld.ErrLog("跨服PK房间数据不存在!roomID=%s" % roomID)
        return
    vsRoom = PyGameData.g_crossPKRoomDict.pop(roomID)
    #vsRoom = PyGameData.g_crossPKRoomDict[roomID]
    roomPlayerIDList = vsRoom.roomPlayerIDList
    if not winnerID and not loserID:
        GameWorld.ErrLog("地图没有结算跨服PK胜负玩家,随机玩家获胜!")
        if not roomPlayerIDList or len(roomPlayerIDList) != 2:
            return
        winnerID, loserID = roomPlayerIDList
    elif not loserID:
        for roomPlayerID in roomPlayerIDList:
            if roomPlayerID != winnerID:
                loserID = roomPlayerID
                break
    if winnerID not in roomPlayerIDList or loserID not in roomPlayerIDList:
        GameWorld.ErrLog("跨服PK房间及玩家不匹配,不结算!roomID=%s,winnerID=%s,loserID=%s,roomPlayerIDList=%s"
                         % (roomID, winnerID, loserID, vsRoom.roomPlayerIDList))
        return
    if winnerID not in PyGameData.g_crossPKPlayerDict:
        GameWorld.ErrLog("跨服PK房间获取不到玩家PK数据, roomID=%s,winnerID=%s" % (roomID, winnerID))
        return
    if loserID not in PyGameData.g_crossPKPlayerDict:
        GameWorld.ErrLog("跨服PK房间获取不到玩家PK数据, roomID=%s,loserID=%s" % (roomID, loserID))
        return
    winner = PyGameData.g_crossPKPlayerDict.pop(winnerID)
    loser = PyGameData.g_crossPKPlayerDict.pop(loserID)
    #winner = PyGameData.g_crossPKPlayerDict[winnerID]
    #loser = PyGameData.g_crossPKPlayerDict[loserID]
    seasonID = winner.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))
    winIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", winnerDanLV)
    loseIpyData = IpyGameDataPY.GetIpyGameData("CrossRealmPKDan", loserDanLV)
    if not winIpyData or not loseIpyData:
        GameWorld.ErrLog("跨服PK房间段位数据异常! roomID=%s,winnerDanLV=%s,loserDanLV=%s" % (roomID, winnerDanLV, loserDanLV))
    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
    winnerAddScore = wBaseScore + wExScore
    loserAddScore = lBaseScore + lExScore
    dayMaxScore = IpyGameDataPY.GetFuncCfg("CrossRealmPKScore", 1) # 每日获得积分上限,0为不限制
    if dayMaxScore:
        if winnerAddScore > 0:
            winnerAddScore = min(dayMaxScore - winnerDayScore, winnerAddScore)
        if loserAddScore > 0:
            loserAddScore = min(dayMaxScore - loserDayScore, loserAddScore)
    winner.pkScore += winnerAddScore
    loser.pkScore += loserAddScore
    winner.cWinCount += 1
    loser.cWinCount = 0
    if winIpyData and winIpyData.GetLVUpScore() and winner.pkScore >= winIpyData.GetLVUpScore():
        winner.danLV += 1
    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))
    timeStr = GameWorld.GetCurrentDataTimeStr()
    playerOverDict = {}
    # 通知客户端战斗结果
    for playerID in [winnerID, loserID]:
        if playerID == winnerID:
            serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName = \
                winner.serverGroupID, winner.pkScore, winner.danLV, winner.cWinCount, winnerAddScore, loser.playerID, loser.playerName
        else:
            serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName = \
                loser.serverGroupID, loser.pkScore, loser.danLV, loser.cWinCount, loserAddScore, winner.playerID, winner.playerName
        player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        notifyState = True if player else False
        playerOverDict[playerID] = [roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList] \
                                    + [serverGroupID, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState]
        if not player:
            continue
        overPack = ChPyNetSendPack.tagGCCrossRealmPKOverInfo()
        overPack.TimeStr = timeStr
        overPack.OverType = overType
        overPack.WinnerID = winnerID
        overPack.RoundWinnerID = roundWinnerIDList
        overPack.RoundCount = len(overPack.RoundWinnerID)
        overPack.AddScore = addScore
        overPack.Score = pkScore
        overPack.DanLV = danLV
        overPack.CWinCnt = cWinCount
        overPack.TagName = tagPlayerName
        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)
    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))
    # 同步子服
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PKOverInfo, playerOverDict, serverGroupIDList)
    return
##================================== 以下是子服逻辑 ==========================================
def CrossServerMsg_PKMatchReqRet(retInfo):
    ## 跨服PK匹配请求结果
    playerID, result = retInfo
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    if result == -2:
        PlayerControl.NotifyCode(curPlayer, "InCrossPKing")
        return
    if result == 1:
        NetPackCommon.SendFakePack(curPlayer, ChPyNetSendPack.tagGCCrossRealmPKStartMatch())
    return
def CrossServerMsg_PKMatchResult(vsRoomDict):
    ## 跨服PK匹配结果
    curServerGroupID = GameWorld.GetServerGroupID()
    actionType = ShareDefine.Def_MergeAction_MergePK
    mapPosList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKMatch", 5)
    GameWorld.Log("=== 收到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))
        for i, playerInfo in enumerate(playerList):
            serverGroupID, playerID = playerInfo
            if serverGroupID != curServerGroupID:
                GameWorld.DebugLog("        不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID))
                continue
            player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not player:
                GameWorld.DebugLog("        玩家不在线, playerID=%s" % (playerID))
                continue
            if PlayerControl.GetIsTJG(player):
                GameWorld.DebugLog("        玩家脱机中, playerID=%s" % (playerID))
                continue
            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)
    return
def CrossServerMsg_PKReadyOKRoomList(readyOKRoomList):
    ## 子服接收玩家已准备好的PK房间信息, 此房间里的玩家可传送进入跨服
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器通知已准备好的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    # serverGroupID, playerName, playerJob
    for roomID, readyMemberDict in readyOKRoomList:
        for playerID, playerInfo in readyMemberDict.items():
            serverGroupID = playerInfo["ServerGroupID"]
            playerName = playerInfo["Name"]
            if serverGroupID != curServerGroupID:
                GameWorld.DebugLog("    不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID))
                continue
            player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not player:
                GameWorld.DebugLog("    玩家不在线 , playerID=%s" % (playerID))
                continue
            if PlayerControl.GetIsTJG(player):
                GameWorld.DebugLog("    玩家脱机中, playerID=%s" % (playerID))
                continue
            player.SetDict(ChConfig.Def_PlayerKey_IsLoginToMergeServer, 1)
            matchPlayer = ChPyNetSendPack.tagGCCrossRealmPKMatchPlayer()
            for readyPlayerID, readyPlayerInfo in readyMemberDict.items():
                if readyPlayerID != playerID:
                    matchPlayer.PlayerID = readyPlayerID
                    matchPlayer.PlayerName = readyPlayerInfo["Name"]
                    matchPlayer.NameLen = len(matchPlayer.PlayerName)
                    matchPlayer.Job = readyPlayerInfo["Job"]
                    matchPlayer.LV = readyPlayerInfo["LV"]
                    matchPlayer.MaxHP = readyPlayerInfo["MaxHP"]
                    break
            PlayerControl.SetCrossRealmState(player, 1)
            # 通知匹配成功,可进入跨服
            matchOKPack = ChPyNetSendPack.tagGCCrossRealmPKMatchOK()
            matchOKPack.RoomID = roomID
            matchOKPack.PlayerName = playerName
            matchOKPack.NameLen = len(matchOKPack.PlayerName)
            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))
            # 到这里默认认为一定会有结果的,所以本服直接增加次数
            #player.MapServer_QueryPlayerResult(0, 0, 'MergePKAddCnt', "", 0)
    return
def CrossServerMsg_PKTimeoutRoomList(timeoutRoomDict):
    ## 子服接收已超时的PK房间信息, 此房间里的玩家重置跨服状态
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器通知已超时的对战PK房间信息处理=== curServerGroupID=%s" % curServerGroupID)
    for roomID, roomPlayerInfo in timeoutRoomDict.items():
        if not roomPlayerInfo:
            continue
        serverGroupID, playerID = roomPlayerInfo
        if serverGroupID != curServerGroupID:
            GameWorld.DebugLog("    不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID))
            continue
        player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not player:
            GameWorld.DebugLog("    玩家不在线 , playerID=%s" % (playerID))
            continue
        if PlayerControl.GetIsTJG(player):
            GameWorld.DebugLog("    玩家脱机中, playerID=%s" % (playerID))
            continue
        playerVSRoomID = player.GetVsRoomId()
        if playerVSRoomID and playerVSRoomID != roomID:
            GameWorld.DebugLog("    房间ID不同, playerID=%s" % (playerID))
            continue
        player.SetDict(ChConfig.Def_PlayerKey_IsLoginToMergeServer, 0)
        PlayerControl.SetCrossRealmState(player, 0)
    return
def CrossServerMsg_PKOverInfo(playerOverDict):
    ## 子服接收跨服PK结果信息
    curServerGroupID = GameWorld.GetServerGroupID()
    GameWorld.Log("===收到跨服服务器同步的跨服PK结果=== curServerGroupID=%s" % curServerGroupID)
    for playerID, overInfo in playerOverDict.items():
        roomID, 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]
        player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not player or PlayerControl.GetIsTJG(player):
            GameWorld.Log("    玩家不在线 或脱机中,先缓存,玩家上线后再同步,playerID=%s" % (playerID))
            PyGameData.g_crossPKUnNotifyOverInfo[playerID] = sendMapOverInfo
            continue
        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)
    return
def __OnLoginNotifyPKOverInfo(curPlayer):
    playerID = curPlayer.GetPlayerID()
    if playerID not in PyGameData.g_crossPKUnNotifyOverInfo:
        return
    overInfo = PyGameData.g_crossPKUnNotifyOverInfo.pop(playerID)
    PlayerControl.SetCrossRealmState(curPlayer, 0)
    sysMsg = str(overInfo)
    curPlayer.MapServer_QueryPlayerResult(0, 0, "CrossPKOverInfo", sysMsg, len(sysMsg))
    GameWorld.Log("玩家上线通知地图未结算的跨服PK结算: mapID=%s,overInfo=%s" % (curPlayer.GetMapID(), overInfo), playerID)
    return
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldMergeKing.py
@@ -14,7 +14,7 @@
# 详细描述: 王者争霸(跨服PK赛周积分排名前32可获得争霸资格)
#
#---------------------------------------------------------------------
"""Version = 2015-11-19 14:00"""
#"""Version = 2015-11-19 14:00"""
#---------------------------------------------------------------------
import ReadChConfig
import PlayerControl
@@ -23,6 +23,7 @@
import PlayerCompensation
import PlayerUniversalGameRec
import PlayerMergeRegister
import CrossRealmPlayer
import GameWorldMergePK
import PlayerDBGSEvent
import MergeBroadcast
@@ -644,7 +645,7 @@
        lastNotifyTime = gameWorld.GetDictByKey(notifyTimeKey)
        if tick - lastNotifyTime >= notifyCD * 1000:
            mark = loginNotifyDict[playerRecRank]
            mergeName = PlayerControl.GetMergePlayerName(accID, curPlayer.GetName())
            mergeName = CrossRealmPlayer.GetCrossPlayerName(curPlayer)
            PlayerControl.MergeWorldNotify(0, mark, [mergeName])
            gameWorld.SetDict(notifyTimeKey, tick)
        else:
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
@@ -86,7 +86,6 @@
#import MergeBroadcast
#import GameWorldMixServerCampaign
#import GameWorldMergeKing
#import GameWorldMergePK
#import PlayerManorWar
import GameWorldBoss
#import GameWorldActionTeHui
@@ -96,7 +95,8 @@
import ReadChConfig
import EventReport
#import ReloadModule
import MergeChildMsg
import CrossRealmMsg
import CrossRealmPK
#import MergePlayer
import PlayerFBHelpBattle
import PlayerFamilyRedPacket
@@ -298,7 +298,7 @@
    #跨服广播
    #MergeBroadcast.OnBroadcastProccee(tick)
    #跨服PK匹配
    #GameWorldMergePK.OnPKMatchProcess(tick)
    CrossRealmPK.OnPKMatchProcess(tick)
    #跨服王者争霸
    #GameWorldMergeKing.OnMergeKingProcess(tick)
    
@@ -1251,15 +1251,13 @@
    #GameWorldBoss.CheckResetBossKilledCntOnServerInit()
    #GameWorldActionTeHui.OnGameServerInitOK() # 特惠活动初始化
    #子服启动成功告知跨服主服
    #===========================================================================
    # serverGroupID = GameWorld.GetServerGroupID()
    # if GameWorld.IsMergeOpen() and not GameWorld.IsMergeServer():
    #    GameWorld.Log("通知跨服主服务器启动成功, 可接收最新跨服活动状态及数据...")
    #    dataMsg = {"Platform":GameWorld.GetPlatform(), "ServerID":GameWorld.GetServerSID(), "ServerGroupID":serverGroupID}
    #    MergeChildMsg.SendMergerChildToCenterStringData(ChConfig.Def_ClientServerInitOK, dataMsg)
    #
    # GameWorld.Log("服务器启动成功: ServerGroupID=%s" % serverGroupID)
    #===========================================================================
    serverGroupID = GameWorld.GetServerGroupID()
    if GameWorld.IsCrossRealmOpen() and not GameWorld.IsCrossServer():
        GameWorld.Log("通知跨服主服务器启动成功, 可接收最新跨服活动状态及数据...")
        dataMsg = {"ServerGroupID":serverGroupID}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_ServerInitOK, dataMsg)
    GameWorld.Log("服务器启动成功: ServerGroupID=%s" % serverGroupID)
    return
def DoCheckNewServerOpen(tick):
ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py
@@ -29,7 +29,7 @@
import ChPyNetSendPack
import traceback
import ChMapToGamePyPack
import MergeChildMsg
import CrossRealmMsg
#-------------------------------------------------------------------------------
#---全局变量---
@@ -95,9 +95,13 @@
#        Log("ReadPyPackTable: moudle: %s"%dir(moudle))
        
        for index in range(regCnt):
            if not config.get(section, "PacketCMD_%s"%(index + 1)):
                continue
            cmd = config.get(section, "PacketCMD_%s"%(index + 1))
            subCmd = config.get(section, "PacketSubCMD_%s"%(index + 1))
            callFunc = config.get(section, "PacketCallFunc_%s"%(index + 1))
            if not cmd or not subCmd or not callFunc:
                continue
            
            cmd = int(cmd, 16)
            subCmd = int(subCmd, 16)
@@ -302,7 +306,7 @@
        #以下添加后续处理函数
        #...
        #...
        MergeChildMsg.Recv_MergerChildToCenterProcess(packData, tick)
        CrossRealmMsg.OnCrossServerReceiveMsg(packData, tick)
    except Exception:
        Log("跨服子服自定义封包消息处理失败")
    return
ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -41,7 +41,6 @@
import PlayerBourse
import GameWorldActionTeHui
import PlayerXMZZ
import GameWorldMergePK
import GameWorldShopItem
import MergeChildMsg
import PlayerTruck
@@ -70,6 +69,7 @@
import PyGameData
import GMShell
import IPY_PlayerDefine
import CrossRealmPK
#---------------------------------------------------------------------
#---------------------------------------------------------------------
@@ -172,6 +172,8 @@
        PyGameData.g_todayPlayerLVDict[curPlayer.GetID()] = curPlayer.GetLV()
        #副本助战
        PlayerFBHelpBattle.OnHelpPlayerLogin(curPlayer)
        #跨服PK
        CrossRealmPK.OnPlayerLogin(curPlayer)
        
        GMShell.OnPlayerLogin(curPlayer)
        GMT_CTG.OnPlayerLogin(curPlayer)
@@ -515,7 +517,7 @@
def __Func_PlayerDisconnect(curPlayer, tick):
    
    #跨服匹配PK
    #GameWorldMergePK.OnLeaveServer(curPlayer)
    CrossRealmPK.OnLeaveServer(curPlayer)
    
    #组队玩家离线
    PlayerTeam.DoPlayerLogOffTeamLogic(curPlayer, tick)
ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
New file
@@ -0,0 +1,115 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package CrossRealmPlayer
#
# @todo:跨服玩家
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服玩家
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import ShareDefine
import CrossRealmMsg
import ReadChConfig
import ChConfig
import PlayerControl
# 获取玩家跨服服务器上的名字
def GetCrossPlayerName(curPlayer):
    # 通过游戏账号中的平台标志获取名称,目前为spid
    playerName = curPlayer.GetPlayerName()
    nameFormat = ReadChConfig.GetPyMongoConfig("Merge", "NameFormat", True)
    if not nameFormat:
        return playerName
    opName = ReadChConfig.GetPyMongoConfig("Merge", "OpName_%s" % GameWorld.GetPlayerPlatform(curPlayer))
    return (nameFormat%{"opname":opName, "sid":GameWorld.GetPlayerServerID(curPlayer)}).decode('gbk').encode(GameWorld.GetCharacterEncoding()) + playerName
def CrossServerMsg_ExitCrossServer(msgData):
    ## 收到跨服服务器同步的玩家退出跨服服务器
    playerID = msgData
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    PlayerControl.SetCrossRealmState(curPlayer, 0)
    return
def SendCrossRealmReg(curPlayer, actionType, mapID=0, dataMapID=0, copyMapID=0, posX=0, posY=0):
    # 发送跨服账号注册上传数据
    # 设置上传数据的活动类型
    curPlayer.SetDict(ChConfig.Def_PlayerKey_MergeRegisterType, actionType)
    sysMsg = str([actionType, mapID, dataMapID, copyMapID, posX, posY])
    curPlayer.MapServer_QueryPlayerResult(0, 0, "CrossRealmReg", sysMsg, len(sysMsg))
    GameWorld.Log("SendCrossRealmReg actionType=%s,mapID=%s,dataMapID=%s,copyMapID=%s,posX=%s,posY=%s"
                  % (actionType, mapID, dataMapID, copyMapID, posX, posY), curPlayer.GetPlayerID())
    return
def OnCrossRealmRegOK(playerID, msgList, tick):
    ## 跨服报名结果
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    #newAccount, newName = msgList
    actionType = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MergeRegisterType)
    GameWorld.Log("跨服报名成功 , actionType=%s" % (actionType), playerID)
    # 跨服PK上传数据完毕,通知跨服服务器,准备完毕
    if actionType == ShareDefine.Def_MergeAction_MergePK:
        regVSRoomID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MergeRegisterRoomID)
        vsRoomID = curPlayer.GetVsRoomId()
        if regVSRoomID != vsRoomID:
            GameWorld.Log("上传跨服服务器的 regVSRoomID=%s 与玩家当前的 roomID=%s 不同!不发送准备完毕!"
                          % (regVSRoomID, vsRoomID), playerID)
            return
        dataMsg = {
                   "accID":curPlayer.GetAccID(), # 角色账号ID
                   "playerID":playerID, # 角色ID
                   "vsRoomID":vsRoomID, # 所属对战房间ID
                   }
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKPrepareOK, dataMsg)
        GameWorld.Log("通知跨服服务器, 玩家匹配PK准备完毕!%s" % str(dataMsg), playerID)
    # 其他的,在上传数据完毕后,使用通用的通知可进入跨服
    else:
        NotifyCanEnterMergeServer(curPlayer, actionType)
    # hxp 2015.09.10 跨服boss,后面的暂时不需要
    return
def NotifyCanEnterMergeServer(curPlayer, actionType):
    # 通用包,通知客户端可进入跨服服务器
#    mapID, lineID = 0, 0
#    mapPosInfo = GetMergeActionMapPos(actionType)
#    if mapPosInfo:
#        mapID = mapPosInfo[0]
#        lineID = mapPosInfo[2]
#
#    canEnterMServer = ChPyNetSendPack.tagCanEnterMergeServer()
#    canEnterMServer.Clear()
#    canEnterMServer.ActionType = actionType
#    canEnterMServer.MapID = mapID
#    canEnterMServer.LineID = lineID
#    canEnterMServer.NewAccID = newAccID
#    canEnterMServer.NewAccIDLen = len(canEnterMServer.NewAccID)
#    canEnterMServer.NewPsw = newPwd
#    canEnterMServer.NewPswLen = len(canEnterMServer.NewPsw)
#    NetPackCommon.SendFakePack(curPlayer, canEnterMServer)
    return
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py
@@ -23,7 +23,6 @@
# @change: "2015-07-14 21:00" xdh 聊天信息原附加值改为Extras
# @change: "2015-10-28 00:00" hxp 增加设置对战房间ID
# @change: "2015-11-05 12:00" hxp 增加跨服全服广播
# @change: "2015-11-06 16:30" hxp 增加GetMergePlayerName
# @change: "2017-06-22 15:00" hxp 跨服广播增加条件过滤子服是否提醒;跨服服务器全服广播同步子服
#---------------------------------------------------------------------
#"""Version = 2017-06-22 15:00"""
@@ -32,6 +31,7 @@
import IPY_GameServer
import MergeBroadcast
import IpyGameDataPY
import CrossRealmMsg
import ShareDefine
import ChConfig
import types
@@ -51,6 +51,21 @@
        return
      
    curPlayer.NotifyCode(msgMark, __GetNotifyCodeList(msgParamList))
    return
def NotifyCodeToClientServer(serverGroupID, playerID, msgMark, msgParamList=[]):
    dataMsg = {"Type":"Player", "ID":playerID, "Mark":msgMark, "Param":msgParamList}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_Notify, dataMsg, [serverGroupID])
    return
def CrossServerMsg_Notify(notifyInfoDict):
    notifyType = notifyInfoDict["Type"]
    notifyID = notifyInfoDict["ID"]
    if notifyType == "Player":
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(notifyID)
        if not curPlayer:
            return
        NotifyCode(curPlayer, notifyInfoDict["Mark"], notifyInfoDict["Param"])
    return
    
## 跨服世界广播
@@ -288,38 +303,25 @@
    curPlayer.SetVsRoomId(roomID)
    if isSetMergeRegRoomID:
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MergeRegisterRoomID, roomID) 
    battleIDStr = str(roomID)
    GameWorld.Log("SetVSRoomID playerID=%s, roomID=%s" % (curPlayer.GetPlayerID(), roomID))
    curPlayer.MapServer_QueryPlayerResult(0, 0, 'CreatePlayerRoomID', battleIDStr, len(battleIDStr))
    SetMapServerPlayerAttrValue(curPlayer, "SetVsRoomId", roomID)
    return
## 根据子服账号及名称获取跨服角色名
def GetMergePlayerName(playerAccID, playerName):
    orgPlayerName = playerName.strip()
## 跨服状态: 0-非跨服状态,1-跨服状态
def GetCrossRealmState(curPlayer): return curPlayer.GetExAttr5()
def SetCrossRealmState(curPlayer, value):
    ''' 设置玩家跨服状态
    @param isExitCrossRealm: 非跨服状态时是否通知前端退出跨服服务器
    '''
    curPlayer.SetExAttr5(value)
    SetMapServerPlayerAttrValue(curPlayer, "SetExAttr5", value)
    return
    
    #取帐号后缀作为区服标识
    parserList = playerAccID.split('@')
    serverSign = ""
    if len(parserList) >= 2:
        serverSign = parserList[-1]
    parserList2 = orgPlayerName.split('-')
    orgNameServerSign = ""
    nameNotServerSign = parserList2[0]
    if len(parserList2) >= 2:
        orgNameServerSign = parserList2[-1]
    if orgNameServerSign:
        if orgNameServerSign == serverSign:
            return orgPlayerName
        else:
            return "%s-%s" % (nameNotServerSign, serverSign)
    if serverSign:
        return "%s-%s" % (nameNotServerSign, serverSign)
    return orgPlayerName
def SetMapServerPlayerAttrValue(curPlayer, attrName, value, exData=[]):
    ## 设置地图服务器玩家对应属性值
    setAttrInfo = str([attrName, value] + exData)
    curPlayer.MapServer_QueryPlayerResult(0, 0, "SetPlayerAttr", setAttrInfo, len(setAttrInfo))
    return
## 地图服务器扣物品
#  @param curPlayer
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -74,6 +74,9 @@
import PyGameData
import PlayerTalk
import PlayerStore
import CrossRealmPlayer
import CrossRealmMsg
import CrossRealmPK
import time
import datetime
@@ -426,12 +429,14 @@
        PlayerCompensation.SendPersonalItemMailBatch(eval(resultName))
        return
    
    if callName == 'SendMergerChildMsg':
        operType, dataMsg = eval(resultName)
        MergeChildMsg.SendMergerChildToCenterStringData(operType, dataMsg)
        # 如果是恢复跨服PK连胜的,先记录恢复的玩家ID, 等待成功后同步最新结果给玩家
        if operType == ChConfig.Def_RecoverMergePKWin:
            GameWorldMergePK.Add_RecoverMergePKWinPlayer(srcPlayerID)
    if callName == "SendMsgToCrossServer":
        msgType, dataMsg = eval(resultName)
        CrossRealmMsg.SendMsgToCrossServer(msgType, dataMsg)
        return
    if callName == "SendMsgToClientServer":
        msgType, dataMsg, serverGroupIDList = eval(resultName)
        CrossRealmMsg.SendMsgToClientServer(msgType, dataMsg, serverGroupIDList)
        return
    
    if callName == 'MergeWorldNotify':
@@ -534,26 +539,36 @@
        return
    #跨服赛报名获得新账号
    if callName == 'MergeRegister':
        PlayerMergeRegister.MergeWarRegisterNewAcc(srcPlayerID, eval(resultName), tick)
        return
    #if callName == 'MergeRegister':
    #    PlayerMergeRegister.MergeWarRegisterNewAcc(srcPlayerID, eval(resultName), tick)
    #    return
    
    #跨服王者争霸
    if callName == 'MergeKingFB':
        GameWorldMergeKing.MapServer_MergeKingFB(eval(resultName))
        return
    
    #跨服匹配PK
    if callName == 'MergePKOver':
        GameWorldMergePK.MapServer_MergePKOver(eval(resultName))
    #跨服匹配PK战斗结算
    if callName == "CrossPKOver":
        CrossRealmPK.MapServer_MergePKOver(eval(resultName))
        return
    #跨服匹配房间开启
    if callName == "CrossPKRoomOpen":
        CrossRealmPK.MapServer_CrossPKRoomOpen(eval(resultName))
        return
    
    #跨服匹配PK取消匹配
    if callName == 'MergePKCancel':
    if callName == "CrossRealmPKCancel":
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
        if not curPlayer:
            return
        GameWorldMergePK.SendCancelMergePKMatch(curPlayer, resultName)
        CrossRealmPK.SendCancelCrossRealmPKMatch(curPlayer, resultName)
        return
    #跨服注册结果
    if callName == "CrossRealmReg":
        CrossRealmPlayer.OnCrossRealmRegOK(srcPlayerID, eval(resultName), tick)
        return
    
    #py喇叭聊天
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -85,3 +85,11 @@
g_autoViceleaderDict = {}#自动安排副盟主的玩家记录{familyID:[]}
g_forbidAutoViceleaderFamily = [] #禁止自动安排副盟主的仙盟[familyID,..]
g_crossPKPlayerDict = {} # 跨服PK玩家字典  {playerID:PKPlayer, ...}
g_crossPKZoneMatchPlayerDict = {} # 跨服PK匹配中的玩家字典 {zoneID:[playerID, ...], ...}
g_crossPKRoomDict = {} # 跨服PK房间字典 {roomID:PKRoom, ...}
g_crossPKRoomID = 0 # 跨服PK当前已经创建到的房间ID,自增创建
g_crossPKUnNotifyOverInfo = {} # 跨服PK未同步的结算信息 {player:[overInfo], ...}
ServerPython/CoreServerGroup/GameServer/ServerCommScript.ini
@@ -114,15 +114,15 @@
PacketSubCMD_1=0x4
PacketCallFunc_1=GetDGDBOperResultInfo
[MergeBroadCast]
ScriptName = Player\MergeBroadCastRecive.py
Writer = wdb
Releaser = wdb
[CrossRealmMsg]
ScriptName = GameWorldLogic\CrossRealmMsg.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 1
PacketCMD_1=0x4
PacketSubCMD_1=0x5
PacketCallFunc_1=OnMergeBroadCastPack
PacketCallFunc_1=OnClientServerReceiveMsg
;家族
[PlayerFamily]
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -368,9 +368,9 @@
PacketSubCMD_8=0x03
PacketCallFunc_8=PySetAdult
PacketCMD_9=0xC1
PacketSubCMD_9=0x03
PacketCallFunc_9=ClientPlayerGetReward
PacketCMD_9=
PacketSubCMD_9=
PacketCallFunc_9=
PacketCMD_10=0xB4
PacketSubCMD_10=0x04
@@ -551,21 +551,37 @@
PacketCallFunc_2 = OnGetManorWarDailyAward
;跨服匹配PK
[PlayerMergePK]
ScriptName = Player\PlayerMergePK.py
;跨服PK竞技场
[PlayerCrossRealmPK]
ScriptName = Player\PlayerCrossRealmPK.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 2
RegisterPackCount = 3
PacketCMD_1=0xC1
PacketSubCMD_1=0x09
PacketCallFunc_1=OnRequestMergePK
PacketSubCMD_1=0x01
PacketCallFunc_1=OnCrossRealmPKMatch
PacketCMD_2=0xC1
PacketSubCMD_2=0x10
PacketCallFunc_2=OnRequestRecoverMergePKWin
PacketCMD_1=0xC1
PacketSubCMD_1=0x02
PacketCallFunc_1=OnCrossRealmPKBuy
PacketCMD_1=0xC1
PacketSubCMD_1=0x03
PacketCallFunc_1=OnCrossRealmPKGetAward
;跨服玩家
[CrossRealmPlayer]
ScriptName = Player\CrossRealmPlayer.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 1
PacketCMD_1=0xC1
PacketSubCMD_1=0x04
PacketCallFunc_1=OnExitCrossRealm
;王者争霸
[PlayerMergeKing]
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1781,6 +1781,8 @@
Def_FBMapID_Dogz = 21110
#聚魂副本
Def_FBMapID_GatherSoul = 31340
#跨服竞技场
Def_FBMapID_CrossRealmPK = 32010
#副本关闭时未拾取的物品邮件发放给玩家
#这里只有需要的副本才配置,不做默认逻辑,防止某些副本实际不能给导致刷物品,如麒麟之府
Def_SendUnPickItemMailMapIDList = [Def_FBMapID_IceLode, Def_FBMapID_PersonalBoss, Def_FBMapID_MunekadoTrial, 
@@ -1868,7 +1870,7 @@
                'SealDemon':[Def_FBMapID_SealDemon, Def_FBMapID_SealDemonEx], #封魔坛
                'XMZZ':[Def_FBMapID_XMZZ], #仙魔之争
                'Dogz':[Def_FBMapID_Dogz], #神兽副本
                'GatherSoul':[Def_FBMapID_GatherSoul],#聚魂副本
                'CrossRealmPK':[Def_FBMapID_CrossRealmPK], #跨服竞技场
                }
#特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
@@ -3832,6 +3834,16 @@
Def_PDict_MergeKing_SupportAward = "PD_MergePK_SupportAward" # 跨服王者争霸玩家竞猜积分奖励领取记录
Def_PDict_MergeKing_Worship = "PD_MergePK_Worship" # 跨服王者争霸玩家每日膜拜记录
# 跨服竞技场
Def_PDict_CrossPK_TotalScore = "CrossPK_TotalScore" # 当前总积分
Def_PDict_CrossPK_OnDayScore = "CrossPK_OnDayScore" # 今天过天时的积分
Def_PDict_CrossPK_DanLV = "CrossPK_DanLV" # 当前段位
Def_PDict_CrossPK_PKCount = "CrossPK_PKCount" # 当前总PK次数
Def_PDict_CrossPK_WinCount = "CrossPK_WinCount" # 当前胜利次数
Def_PDict_CrossPK_CWinCount = "CrossPK_CWinCount" # 跨当前连胜次数
Def_PDict_CrossPK_TodayPKCount = "CrossPK_TodayPKCount" # 今日已PK次数
Def_PDict_CrossPK_TodayBuyCount = "CrossPK_TodayBuyCount" # 今日已购买PK次数
#自动战斗设置记录
Def_PDict_AutoFightSetting = "AFSetting_%s_%s"
@@ -5110,8 +5122,10 @@
tttFBAddTime, # 可挑战下一关倒计时 6
tttDeadTime, # 死亡倒计时(古神禁地) 7
tttPickupItem, # 拾取物品倒计时 8
tttWaitPlayer, # 等待对手倒计时 9
tttPlayerLeave, # 对手掉线倒计时 10
tttMax,
) = range(10)
) = range(12)
Def_FBPickupItemTime = 30000
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -1229,7 +1229,7 @@
def PlayerLoginInFBCheck(curPlayer, tick):
    gameMap = GameWorld.GetMap()
    #如果此地图是自动释放的, 需要检查这个玩家
    if gameMap.GetMapFBType() == 0:
    if gameMap.GetMapFBType() in [IPY_GameWorld.fbtNull, IPY_GameWorld.fbtCrossVSRoom]:
        return False
    
    #玩家 在副本中,并且副本不踢出玩家下线
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -630,7 +630,7 @@
#---------------------------------------------------------------------
def SyncDynamicBarrierState(barrierPointList, state, curPlayer=None):
    '''同步动态障碍物是否有效性
    @param barrierPointList: 障碍物点列表 [[aPosX,aPosY,bPosX,bPosY], [aPosX,aPosY,bPosX,bPosY], ...]
    @param barrierPointList: 障碍物点列表 [[aPosX,aPosY,bPosX,bPosY,angle可选], [aPosX,aPosY,bPosX,bPosY,angle可选], ...]
    @param state: 是否有效
    @param curPlayer: 指定通知目标玩家,为None时广播本地图所有玩家
    '''
@@ -638,12 +638,15 @@
    barrierStatePack.Clear()
    barrierStatePack.State = state
    barrierStatePack.BarrierList = []
    for aPosX, aPosY, bPosX, bPosY in barrierPointList:
    for posInfo in barrierPointList:
        aPosX, aPosY, bPosX, bPosY = posInfo[:4]
        angle = posInfo[4] if len(posInfo) > 4 else 0
        barrier = ChPyNetSendPack.tagMCDynamicBarrier()
        barrier.APosX = aPosX
        barrier.APosY = aPosY
        barrier.BPosX = bPosX
        barrier.BPosY = bPosY
        barrier.Angle = angle
        barrierStatePack.BarrierList.append(barrier)
    barrierStatePack.Count = len(barrierStatePack.BarrierList)
    if curPlayer:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossRealmPK.py
New file
@@ -0,0 +1,498 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GameWorldLogic.FBProcess.GameLogic_CrossRealmPK
#
# @todo:跨服匹配PK
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服匹配PK
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import ChPlayer
import GameWorld
import IPY_GameWorld
import PlayerControl
import GameWorldProcess
import IpyGameDataPY
import SkillCommon
import FBCommon
import ChConfig
(
Def_Time_MaxWait, # 最长等待时间, 秒
Def_Time_MapPrepare, # 准备时间, 秒
Def_Time_Fight, # 战斗时间, 秒
Def_Time_Protect, # 保护时间,秒,玩家战斗中掉线保护时长
Def_Time_Leave, # 结束退出时间, 秒
) = range(5)
# 当前副本地图的状态
(
FB_State_Open,
FB_State_Waiting, # 等待对手阶段
FB_State_MapPrepare, # 地图准备
FB_State_Fight, # 战斗阶段
FB_State_Reborn, # 复活阶段
FB_State_Leave, # 离开阶段
FB_State_Close, # 关闭阶段
) = range(7)
# 对战结束类型定义
(
Def_OverType_LackPlayer, # 缺少对手
Def_OverType_PlayerExit, # 对手退出(视为认输)
Def_OverType_Kill, # 击杀对手
Def_OverType_TimeOut, # PK时间超时
) = range(4)
# 副本相关字典key
GameFBDict_FBPlayerID = "FBD_FBPlayerID_%s" # 玩家ID, 参数, 进入顺序
GameFBDict_PlayerWinCnt = "FBD_PlayerWinCnt_%s" # 玩家已获胜次数, 参数[playerID]
GameFBDict_PlayerLeaveTick = "FBD_PlayerLeaveTick_%s" # 玩家已获胜次数, 参数[playerID]
FBPDict_PVPDamage = "FBPD_PVPDamage" # 玩家伤害输出
FBPDict_PVPDamUpdTick = "FBPD_PVPDamUpdTick" # 更新伤害tick
FBPDict_ResetPosX = "FBPD_ResetPosX" # 玩家重置坐标X
FBPDict_ResetPosY = "FBPD_ResetPosY" # 玩家重置坐标Y
FBPDict_RoundNum = "FBPD_RoundNum" # 玩家当前所属回合数
FB_RoundNum = "FB_RoundNum" # 副本当前回合数
FB_RoundWinPlayerID = "FB_RoundWinPlayerID_%s" # 回合获胜玩家ID, 参数[回合数]
## 是否能够通过活动查询进入
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    return True
#def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
#    posDict = {117401:(40,37), 117403:(10, 7)}
#    return posDict.get(curPlayer.GetPlayerID())
## 玩家进入副本
def DoEnterFB(curPlayer, tick):
    playerID = curPlayer.GetPlayerID()
    playerVSRoomID = curPlayer.GetVsRoomId()
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    GameWorld.Log("DoEnterFB fbRoomID=%s,playerVSRoomID=%s,fbStep=%s" % (roomID, playerVSRoomID, fbStep), playerID)
    gameFB.SetGameFBDict(GameFBDict_PlayerLeaveTick % playerID, 0)
    fbRoundNum = gameFB.GetGameFBDictByKey(FB_RoundNum)
    playerRoundNum = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_RoundNum)
    if playerRoundNum and fbRoundNum and playerRoundNum != fbRoundNum:
        # 一般是掉线时上个回合已经结束了
        GameWorld.DebugLog("玩家进入副本时与当前副本回合数不一致时,重置状态!fbRoundNum=%s,playerRoundNum=%s"
                           % (fbRoundNum, playerRoundNum), playerID)
        gameFB.SetPlayerGameFBDict(playerID, FBPDict_RoundNum, fbRoundNum)
        __ResetPlayerState(gameFB, curPlayer, playerID)
    if fbStep >= FB_State_Leave or not roomID or not playerVSRoomID or roomID != playerVSRoomID:
        PlayerControl.PlayerLeaveFB(curPlayer)
        return
    # 非战斗阶段,通知动态障碍点
    if fbStep < FB_State_Fight:
        FBCommon.SyncDynamicBarrierState(IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 2), 1, curPlayer) # 准备期间有动态障碍点
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    if fbStep == FB_State_Open:
        gameFB.SetGameFBDict(GameFBDict_FBPlayerID % 1, playerID)
        gameFB.SetPlayerGameFBDict(playerID, FBPDict_ResetPosX, curPlayer.GetPosX())
        gameFB.SetPlayerGameFBDict(playerID, FBPDict_ResetPosY, curPlayer.GetPosY())
        gameFB.SetPlayerGameFBDict(playerID, FBPDict_RoundNum, 1)
        FBCommon.SetFBStep(FB_State_Waiting, tick)
        GameWorld.Log("    第一个进入,设置副本进入等待对手阶段!roomID=%s" % (roomID), playerID)
        __ResetPlayerState(gameFB, curPlayer, playerID, False)
        curPlayer.Sync_TimeTick(ChConfig.tttWaitPlayer, 0, fbTimeList[Def_Time_MaxWait] * 1000, True)
        sendMsg = str([roomID])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CrossPKRoomOpen", sendMsg, len(sendMsg))
    elif fbStep == FB_State_Waiting:
        playerIDA = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 1)
        playerIDB = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 2)
        if not playerIDB and playerIDA != playerID:
            gameFB.SetGameFBDict(GameFBDict_FBPlayerID % 2, playerID)
            gameFB.SetPlayerGameFBDict(playerID, FBPDict_ResetPosX, curPlayer.GetPosX())
            gameFB.SetPlayerGameFBDict(playerID, FBPDict_ResetPosY, curPlayer.GetPosY())
            gameFB.SetPlayerGameFBDict(playerID, FBPDict_RoundNum, 1)
            GameWorld.Log("    第二个进入的玩家!roomID=%s" % (roomID), playerID)
            __ResetPlayerState(gameFB, curPlayer, playerID, False)
        if GameWorld.GetMapCopyPlayerManager().GetPlayerCount() == 2:
            GameWorld.Log("    两个人都在,设置副本进入战斗倒计时阶段!roomID=%s" % (roomID), playerID)
            FBCommon.SetFBStep(FB_State_MapPrepare, tick)
            FBCommon.Sync_Player_TimeTick(ChConfig.tttWaitStart, fbTimeList[Def_Time_MapPrepare] * 1000)
            gameFB.SetGameFBDict(FB_RoundNum, 1)
        else:
            GameWorld.Log("    对手不在,继续等待!roomID=%s" % (roomID), playerID)
    elif fbStep == FB_State_MapPrepare:
        notify_tick = fbTimeList[Def_Time_MapPrepare] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(ChConfig.tttWaitStart, 0, max(notify_tick, 0), True)
    elif fbStep == FB_State_Fight:
        notify_tick = fbTimeList[Def_Time_Fight] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(ChConfig.tttTowerTake, 0, max(notify_tick, 0), True)
    FBCommon.Notify_FBHelp(curPlayer, __GetFBHelpInfo())
    return
def __GetFBHelpInfo():
    gameFB = GameWorld.GetGameFB()
    roundWinerIDList = []
    roundNum = gameFB.GetGameFBDictByKey(FB_RoundNum)
    for roundNum in xrange(1, roundNum + 1):
        winnerID = gameFB.GetGameFBDictByKey(FB_RoundWinPlayerID % roundNum)
        if not winnerID:
            break
        roundWinerIDList.append(winnerID)
    return {"roundNum":roundNum, "roundWinerIDList":roundWinerIDList}
## 玩家退出副本
def DoExitFB(curPlayer, tick):
    # 结算时间
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    if fbStep >= FB_State_Leave:
        return
    playerID = curPlayer.GetPlayerID()
    gameFB.SetGameFBDict(GameFBDict_PlayerLeaveTick % playerID, tick)
    GameWorld.Log("玩家战斗阶段下线!playerID=%s,waitPlayerID=%s" % (playerID, playerID))
    return
## 获得副本帮助信息
def DoFBHelp(curPlayer, tick):
    return
## 副本总逻辑计时器
def OnProcess(tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    if fbStep == FB_State_Waiting:
        __DoLogic_Waiting(tick)
    elif fbStep == FB_State_MapPrepare:
        if not __CheckLeaveProtectTimeout(tick):
            __DoLogic_MapPrepare(tick)
    elif fbStep == FB_State_Fight:
        if not __CheckLeaveProtectTimeout(tick):
            __DoLogic_MapFight(tick)
    elif fbStep == FB_State_Reborn:
        __DoLogic_Reborn(tick)
    elif fbStep == FB_State_Leave:
        __DoLogic_LeaveTime(tick)
    return
##等待玩家进入阶段处理
def __DoLogic_Waiting(tick):
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    if tick - GameWorld.GetGameFB().GetFBStepTick() < fbTimeList[Def_Time_MaxWait] * 1000:
        return
    winner, winnerID, loser, loserID = __GetTimeoutWinerInfo(tick)
    # 对手没来,直接获胜
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("战前等待对手阶段超时,直接结束!roomID=%s,winnerID=%s,loserID=%s" % (roomID, winnerID, loserID))
    __DoFBPKAllOver(winner, winnerID, loser, loserID, Def_OverType_LackPlayer, tick)
    return
def __CheckLeaveProtectTimeout(tick):
    gameFB = GameWorld.GetGameFB()
    playerIDA = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 1)
    playerIDB = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 2)
    playerLeaveTickA = gameFB.GetGameFBDictByKey(GameFBDict_PlayerLeaveTick % playerIDA)
    playerLeaveTickB = gameFB.GetGameFBDictByKey(GameFBDict_PlayerLeaveTick % playerIDB)
    if not playerLeaveTickA and not playerLeaveTickB:
        return
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    if playerLeaveTickA > playerLeaveTickB:
        if tick - playerLeaveTickA < fbTimeList[Def_Time_Protect] * 1000:
            return
    else:
        if tick - playerLeaveTickB < fbTimeList[Def_Time_Protect] * 1000:
            return
    winner, winnerID, loser, loserID = __GetTimeoutWinerInfo(tick)
    # 对手没来,直接获胜
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("战斗中对手离线超时,直接结束!roomID=%s,winnerID=%s,loserID=%s" % (roomID, winnerID, loserID))
    __DoFBPKAllOver(winner, winnerID, loser, loserID, Def_OverType_PlayerExit, tick)
    return True
def __GetTimeoutWinerInfo(tick):
    ''' 时间超时,获取获胜的玩家
                    战斗阶段有两个玩家时优先比较输出
                    其他情况则在线玩家获胜,如果没有玩家在线,则最迟离线的获胜
    '''
    winner, winnerID, loser, loserID = None, 0, None, 0
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    copyMapPlayerManager = GameWorld.GetMapCopyPlayerManager()
    playerIDA = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 1)
    playerIDB = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 2)
    # 战斗阶段有两个玩家时优先比较输出
    if fbStep == FB_State_Fight and copyMapPlayerManager.GetPlayerCount() == 2:
        # 时间到还没分出胜负, 根据以下规则决定胜负,这里用玩家ID处理,防止结算时都掉线了导致没有结果
        # 伤害输出 > 优先到达时间 > 剩余HP > 最大HP > playerID
        playerInfoList = []
        for playerID in [playerIDA, playerIDB]:
            player = copyMapPlayerManager.FindPlayerByID(playerID)
            pvpDamage = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_PVPDamage)
            pvpDamTick = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_PVPDamUpdTick)
            sortTick = tick - pvpDamTick
            curHP = 0 if not player else player.GetHP()
            curMaxHP = 0 if not player else player.GetMaxHP()
            playerInfoList.append([pvpDamage, sortTick, curHP, curMaxHP, playerID, player])
            GameWorld.Log("PK超时: pvpDamge=%s,pvpDamTick=%s,tick=%s,sortTick=%s,HP=%s/%s"
                          % (pvpDamage, pvpDamTick, tick, sortTick, curHP, curMaxHP), playerID)
        playerInfoList.sort(reverse=True)
        GameWorld.Log("PK超时, 进入结算!playerInfoList=%s" % str(playerInfoList))
        winner = playerInfoList[0][-1] if len(playerInfoList) > 0 else None
        loser = playerInfoList[1][-1] if len(playerInfoList) > 1 else None
        winnerID = 0 if not winner else winner.GetPlayerID()
        loserID = 0 if not loser else loser.GetPlayerID()
        return winner, winnerID, loser, loserID
    # 其他情况则在线玩家获胜,如果没有玩家在线,则最迟离线的获胜
    for i in xrange(copyMapPlayerManager.GetPlayerCount()):
        player = copyMapPlayerManager.GetPlayerByIndex(i)
        if player == None or player.IsEmpty():
            continue
        winner = player
        winnerID = player.GetPlayerID()
        break
    if not winner:
        playerLeaveTickA = gameFB.GetGameFBDictByKey(GameFBDict_PlayerLeaveTick % playerIDA)
        playerLeaveTickB = gameFB.GetGameFBDictByKey(GameFBDict_PlayerLeaveTick % playerIDB)
        # 离线tick较大的就是比较晚离线的
        if playerLeaveTickA > playerLeaveTickB:
            winnerID = playerIDA
            loserID = playerIDB
        else:
            winnerID = playerIDB
            loserID = playerIDA
    else:
        loserID = playerIDB if playerIDA == winnerID else playerIDA
    return winner, winnerID, loser, loserID
##副本准备时间
def __DoLogic_MapPrepare(tick):
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    GameWorld.DebugLog("__DoLogic_MapPrepare ... %s" % fbTimeList[Def_Time_MapPrepare])
    if tick - GameWorld.GetGameFB().GetFBStepTick() < fbTimeList[Def_Time_MapPrepare] * 1000:
        return
    FBCommon.SetFBStep(FB_State_Fight, tick)
    FBCommon.Sync_Player_TimeTick(ChConfig.tttTowerTake, fbTimeList[Def_Time_Fight] * 1000)
    FBCommon.SyncDynamicBarrierState(IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 2), 0)
    # 通知回合开始
    helpDict = __GetFBHelpInfo()
    helpDict["isStart"] = 1
    copyMapPlayerManager = GameWorld.GetMapCopyPlayerManager()
    for i in xrange(copyMapPlayerManager.GetPlayerCount()):
        player = copyMapPlayerManager.GetPlayerByIndex(i)
        if player == None or player.IsEmpty():
            continue
        FBCommon.Notify_FBHelp(player, helpDict)
    return
##战斗阶段
def __DoLogic_MapFight(tick):
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    if tick - GameWorld.GetGameFB().GetFBStepTick() < fbTimeList[Def_Time_Fight] * 1000:
        return
    winner, winnerID, loser, loserID = __GetTimeoutWinerInfo(tick)
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("PK超时, 进入结算! roomID=%s,winnerID=%s,loserID=%s" % (roomID, winnerID, loserID))
    __DoLogicAddPlayerWinCnt(winner, winnerID, loser, loserID, Def_OverType_TimeOut, tick)
    return
##复活阶段阶段
def __DoLogic_Reborn(tick):
    gameFB = GameWorld.GetGameFB()
    helpDict = __GetFBHelpInfo()
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    prepareTime = fbTimeList[Def_Time_MapPrepare] * 1000
    helpDict["prepareTime"] = prepareTime
    nextRoundNum = gameFB.GetGameFBDictByKey(FB_RoundNum) + 1
    gameFB.SetGameFBDict(FB_RoundNum, nextRoundNum)
    isWinnerResetState = False
    copyMapPlayerManager = GameWorld.GetMapCopyPlayerManager()
    for i in xrange(copyMapPlayerManager.GetPlayerCount()):
        player = copyMapPlayerManager.GetPlayerByIndex(i)
        if player == None or player.IsEmpty():
            continue
        playerID = player.GetPlayerID()
        gameFB.SetPlayerGameFBDict(playerID, FBPDict_RoundNum, nextRoundNum)
        if player.GetPlayerAction() == IPY_GameWorld.paDie or player.GetHP() <= 0:
            GameWorld.DebugLog("复活玩家...", player.GetPlayerID())
            ChPlayer.PlayerRebornByType(player, ChConfig.rebornType_System, tick)
            __ResetPlayerState(gameFB, player, playerID)
        else:
            if isWinnerResetState:
                __ResetPlayerState(gameFB, player, playerID)
        FBCommon.Notify_FBHelp(player, helpDict)
    GameWorld.Log("开始下一回合: nextRoundNum=%s" % (nextRoundNum))
    # 重置伤害输出
    playerIDA = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 1)
    playerIDB = gameFB.GetGameFBDictByKey(GameFBDict_FBPlayerID % 2)
    gameFB.SetPlayerGameFBDict(playerIDA, FBPDict_PVPDamage, 0)
    gameFB.SetPlayerGameFBDict(playerIDA, FBPDict_PVPDamUpdTick, tick)
    gameFB.SetPlayerGameFBDict(playerIDB, FBPDict_PVPDamage, 0)
    gameFB.SetPlayerGameFBDict(playerIDB, FBPDict_PVPDamUpdTick, tick)
    # 进入准备倒计时,开始下一局准备
    FBCommon.SetFBStep(FB_State_MapPrepare, tick)
    #FBCommon.Sync_Player_TimeTick(ChConfig.tttWaitStart, prepareTime)
    return
##比赛结束的空闲时间
def __DoLogic_LeaveTime(tick):
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    if tick - GameWorld.GetGameFB().GetFBStepTick() < fbTimeList[Def_Time_Leave] * 1000:
        return
    FBCommon.SetFBStep(FB_State_Close, tick)
    GameWorldProcess.CloseFB(tick)
    return
## PVP伤害相关
def OnPVPDamage(curPlayer, damageValue, tagPlayer, tick):
    playerID = curPlayer.GetPlayerID()
    tagPlayerID = tagPlayer.GetPlayerID()
    gameFB = GameWorld.GetGameFB()
    curPlayerDamage = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_PVPDamage)
    updDamage = min(ChConfig.Def_UpperLimit_DWord, curPlayerDamage + damageValue)
    gameFB.SetPlayerGameFBDict(playerID, FBPDict_PVPDamage, updDamage)
    gameFB.SetPlayerGameFBDict(playerID, FBPDict_PVPDamUpdTick, tick)
    GameWorld.DebugLog("OnPVPDamage playerID=%s,tagPlayerID=%s,damageValue=%s,updDamage=%s,tick=%s"
                       % (playerID, tagPlayerID, damageValue, updDamage, tick))
    #helpDict = {"PVPDamage":[playerID, updDamage, tick]}
    #FBCommon.Notify_FBHelp(curPlayer, helpDict)
    #FBCommon.Notify_FBHelp(tagPlayer, helpDict)
    return
##处理副本中杀死玩家逻辑
def DoFBOnKill_Player(curPlayer, defender, tick):
    winnerID = curPlayer.GetPlayerID()
    loserID = defender.GetPlayerID()
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("DoFBOnKill_Player roomID=%s,winnerID=%s,loserID=%s" % (roomID, winnerID, loserID), winnerID)
    if GameWorld.GetGameFB().GetFBStep() != FB_State_Fight:
        return
    __DoLogicAddPlayerWinCnt(curPlayer, winnerID, defender, loserID, Def_OverType_Kill, tick)
    return True
def __DoLogicAddPlayerWinCnt(winner, winnerID, loser, loserID, overType, tick):
    gameFB = GameWorld.GetGameFB()
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    winnerWinCnt = gameFB.GetGameFBDictByKey(GameFBDict_PlayerWinCnt % winnerID)
    updWinCnt = winnerWinCnt + 1
    gameFB.SetGameFBDict(GameFBDict_PlayerWinCnt % winnerID, updWinCnt)
    roundNum = gameFB.GetGameFBDictByKey(FB_RoundNum)
    gameFB.SetGameFBDict(FB_RoundWinPlayerID % roundNum, winnerID)
    GameWorld.Log("回合结束: roomID=%s,roundNum=%s,winnerID=%s,loserID=%s,updWinCnt=%s" % (roomID, roundNum, winnerID, loserID, updWinCnt))
    isOver = (updWinCnt >= IpyGameDataPY.GetFuncCfg("CrossRealmPKFB", 3))
    if not isOver:
        FBCommon.SetFBStep(FB_State_Reborn, tick)
        return
    GameWorld.Log("    已达到最大胜场,获得最终胜利!winnerID=%s" % winnerID)
    __DoFBPKAllOver(winner, winnerID, loser, loserID, overType, tick)
    return
def __ResetPlayerState(gameFB, player, playerID, resetPos=True):
    if resetPos:
        posX = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_ResetPosX)
        posY = gameFB.GetPlayerGameFBDictByKey(playerID, FBPDict_ResetPosY)
        player.ResetPos(posX, posY)
    if player.GetHP() != player.GetMaxHP():
        player.SetHP(player.GetMaxHP())
    if PlayerControl.GetProDef(player) != PlayerControl.GetMaxProDef(player):
        PlayerControl.SetProDef(player, PlayerControl.GetMaxProDef(player))
    SkillCommon.ResetAllSkillCD(player)
    return
## 跨服PK结束处理
def __DoFBPKAllOver(winner, winnerID, loser, loserID, overType, tick):
    gameFB = GameWorld.GetGameFB()
    roundWinerIDList = []
    roundNum = gameFB.GetGameFBDictByKey(FB_RoundNum)
    for roundNum in xrange(1, roundNum + 1):
        roundWinerIDList.append(gameFB.GetGameFBDictByKey(FB_RoundWinPlayerID % roundNum))
    #副本状态进入关闭倒计时
    fbTimeList = IpyGameDataPY.GetFuncEvalCfg("CrossRealmPKFB", 1)
    FBCommon.Sync_Player_TimeTick(ChConfig.tttLeaveMap, fbTimeList[Def_Time_Leave] * 1000)
    FBCommon.SetFBStep(FB_State_Leave, tick)
    #发送一条消息到GameServer通知PK对战结束,因为地图可能对手没来导致没有失败玩家ID,所以结算统一在GameServer处理
    overType = 1 if overType in [Def_OverType_LackPlayer, Def_OverType_PlayerExit] else 0
    roomID = GameWorld.GetGameWorld().GetPropertyID()
    sendMsg = str([roomID, winnerID, loserID, roundWinerIDList, overType])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CrossPKOver", sendMsg, len(sendMsg))
    GameWorld.Log("PK结算SendToGameServer: roomID=%s,winnerID=%s,loserID=%s,roundWinerIDList=%s,overType=%s"
                  % (roomID, winnerID, loserID, roundWinerIDList, overType))
    return
#关系有3层,无-友好-敌人
def CheckPlayersRelation_IsFriend(curPlayer, curTagPlayer):
    return not CanAttackPlayer(curPlayer, curTagPlayer)
##副本中,攻击队友逻辑
def DoCanAttackTeamer(curPlayer, curTagPlayer):
    return CanAttackPlayer(curPlayer, curTagPlayer)
##副本中,是否可攻击
def CanAttackPlayer(curPlayer, curTagPlayer):
    fbStep = GameWorld.GetGameFB().GetFBStep()
    if fbStep != FB_State_Fight:
        GameWorld.DebugLog("非战斗阶段,不可攻击!")
        return False
    return True
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NetPackCommon.py
@@ -95,9 +95,13 @@
#        Log("ReadPyPackTable: moudle: %s"%dir(moudle))
        
        for index in range(regCnt):
            if not config.has_option(section, "PacketCMD_%s"%(index + 1)):
                continue
            cmd = config.get(section, "PacketCMD_%s"%(index + 1))
            subCmd = config.get(section, "PacketSubCMD_%s"%(index + 1))
            callFunc = config.get(section, "PacketCallFunc_%s"%(index + 1))
            if not cmd or not subCmd or not callFunc:
                continue
            
            cmd = int(cmd, 16)
            subCmd = int(subCmd, 16)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -108,6 +108,7 @@
import PlayerActTotalRecharge
import PlayerSpringSale
import PlayerFairyCeremony
import CrossRealmPlayer
import ChNetSendPack
import FamilyRobBoss
import FBHelpBattle
@@ -249,7 +250,7 @@
    
    # 屏蔽跨服下关闭和子服重复的数据的发送 pushsend接口, notifyall正常发送
    # !!!必要发送的数据要注意位置
    if GameWorld.IsMergeServer():
    if GameWorld.IsCrossServer():
        curPlayer.SetForbiddenSyncClientState(True)
    
    SyncGuideState(curPlayer)
@@ -308,6 +309,9 @@
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FuncChangeLineID, 0)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_HighChangeLineID, 0)
        #GameWorld.DebugLog("离线超过10秒重置切线临时保存的相关记录值!leaveServerSecond=%s" % leaveServerSecond, curPlayer.GetPlayerID())
        # 离线过久恢复为非跨服状态
        if PlayerControl.GetCrossRealmState(curPlayer):
            PlayerControl.SetCrossRealmState(curPlayer, 0)
        
    # 合服首登处理
    __DoMixServerFirstLogin(curPlayer)
@@ -578,8 +582,9 @@
        
    # 屏蔽跨服下关闭和子服重复的数据的发送 pushsend接口, notifyall正常发送
    # !!!必要发送的数据要注意位置
    if GameWorld.IsMergeServer():
    if GameWorld.IsCrossServer():
        curPlayer.SetForbiddenSyncClientState(False)
        PlayerControl.SetCrossRealmState(curPlayer, 1) # 因为主服上传数据之前该值为1,所以登录跨服后在跨服服务器要设置为1
        
    return
@@ -835,7 +840,7 @@
def UpdatePlayerServerGroupID(curPlayer):
    # 更新自己的服务器组ID, 跨服服务器不处理
    if GameWorld.IsMergeServer():
    if GameWorld.IsCrossServer():
        return
    serverGroupID = GameWorld.GetServerGroupID()
    if not serverGroupID:
@@ -3828,7 +3833,7 @@
    #复活冷却时间(秒)
    rebornTime = GetRebronTime(curPlayer, playerRebornType)    
    #冷却时间到了
    if not CanRebornByTimeOver(curPlayer, rebornTime):
    if playerRebornType != ChConfig.rebornType_System and not CanRebornByTimeOver(curPlayer, rebornTime):
        PlayerControl.NotifyCode(curPlayer, 'RebornCD')
        return False
    
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py
New file
@@ -0,0 +1,80 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.CrossRealmPlayer
#
# @todo:跨服玩家
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服玩家
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import ReadChConfig
import PlayerControl
import IPY_GameWorld
import ShareDefine
import ChConfig
import FBLogic
# 获取玩家跨服服务器上的名字
def GetCrossPlayerName(curPlayer):
    # 通过游戏账号中的平台标志获取名称,目前为spid
    playerName = curPlayer.GetPlayerName()
    nameFormat = ReadChConfig.GetPyMongoConfig("Merge", "NameFormat", True)
    if not nameFormat:
        return playerName
    opName = ReadChConfig.GetPyMongoConfig("Merge", "OpName_%s" % GameWorld.GetPlayerPlatform(curPlayer))
    return (nameFormat%{"opname":opName, "sid":GameWorld.GetPlayerServerID(curPlayer)}).decode('gbk').encode(GameWorld.GetCharacterEncoding()) + playerName
#// C1 04 主动退出跨服 #tagCMExitCrossRealm
#
#struct    tagCMExitCrossRealm
#{
#    tagHead        Head;
#};
def OnExitCrossRealm(index, curPackData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    if not GameWorld.IsCrossServer():
        return
    FBLogic.DoPlayerLeaveFB(curPlayer, tick)
    PlayerExitCrossServer(curPlayer)
    return
def PlayerExitCrossServer(curPlayer):
    ## 玩家退出跨服服务器
    # 通知子服玩家退出跨服服务器
    playerID = curPlayer.GetPlayerID()
    serverGroupID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ServerGroupID)
    GameWorld.SendMsgToClientServer(ShareDefine.CrossServerMsg_ExitCrossServer, playerID, [serverGroupID])
    # 设置非跨服状态,踢下线
    PlayerControl.SetCrossRealmState(curPlayer, 0)
    curPlayer.Kick(IPY_GameWorld.disMapServerClose)
    GameWorld.Log("PlayerExitCrossServer...", curPlayer.GetPlayerID())
    return
def DoEnterCrossRealm(curPlayer):
    ## 玩家进入跨服处理,本服的逻辑处理
    curPlayer.SetVisible(False)
    return
def DoExitCrossRealm(curPlayer):
    ## 玩家退出跨服处理,本服的逻辑处理
    GameWorld.Log("DoExitCrossRealm...", curPlayer.GetPlayerID())
    curPlayer.SetVisible(True)
    if PlayerControl.GetCrossRealmState(curPlayer):
        PlayerControl.SetCrossRealmState(curPlayer, 0)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -84,6 +84,7 @@
import PlayerCostRebate
import PlayerFairyCeremony
import FunctionNPCCommon
import CrossRealmPlayer
import ChNetSendPack
import PlayerState
import QuestCommon
@@ -1498,6 +1499,11 @@
    if not curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RouteServerInitOK):
        #RouteServer未初始化不允许切换地图, 缓存处理
        GameServerRefresh.Set_PlayerRouteServerInitOK_OnLeaveFB(curPlayer, 1)
        return
    GameWorld.Log("PlayerLeaveFB...", curPlayer.GetPlayerID())
    if GameWorld.IsCrossServer():
        CrossRealmPlayer.PlayerExitCrossServer(curPlayer)
        return
    #中立地图回到上一次非中立常规地图    
@@ -5708,6 +5714,10 @@
def SetFBFuncLineID(curPlayer, funcLineID): return curPlayer.SetExAttr3(funcLineID, False, False)
def GetFBFuncLineID(curPlayer): return curPlayer.GetExAttr3()
## 跨服状态: 0-非跨服状态,1-跨服状态
def GetCrossRealmState(curPlayer): return curPlayer.GetExAttr5()
def SetCrossRealmState(curPlayer, value): curPlayer.SetExAttr5(value, False, True)
## 铜钱点, 支持铜钱超20亿
def GetSilver(curPlayer): return curPlayer.GetExAttr6() * ChConfig.Def_PerPointValue + curPlayer.GetSilver()
def SetSilver(curPlayer, totalSilver):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py
New file
@@ -0,0 +1,229 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.PlayerCrossRealmPK
#
# @todo:跨服PK竞技场
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服PK竞技场
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import ShareDefine
import PlayerControl
import CrossRealmPlayer
import ChPyNetSendPack
import NetPackCommon
import GameWorld
import ChConfig
def DoPlayerOnDay(curPlayer):
    totalScore = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_TotalScore)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_OnDayScore, totalScore)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_TodayPKCount, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_TodayBuyCount, 0)
    return
def IsCrossRealmPKOpen():
    ## 跨服PK匹配赛是否开启
    return 1
    return GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_MergePKState) == ChConfig.Def_Action_Open
def GetCrossPKZoneID():
    ## 获取本服跨服PK所属赛区
    return 1
def GetSeasonID():
    ## 获取当前赛季ID
    return 1
def IsCrossRealmPKSeasonOpen():
    return True
#// C1 01 跨服PK匹配 #tagCMCrossRealmPKMatch
#
#struct    tagCMCrossRealmPKMatch
#{
#    tagHead        Head;
#    BYTE        Type;    // 0-取消匹配; 1-进行匹配
#};
def OnCrossRealmPKMatch(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    accID = curPlayer.GetAccID()
    playerID = curPlayer.GetPlayerID()
    requestType = clientData.Type
    if GameWorld.IsCrossServer():
        return
    if not IsCrossRealmPKOpen():
        GameWorld.Log("OnRequestMergePK 跨服活动未开启,不可进行匹配!", playerID)
        PlayerControl.NotifyCode(curPlayer, "GeRen_hgg_21675")
        return
    GameWorld.Log("收到跨服PK匹配: type=%s,accID=%s" % (requestType, accID), playerID)
    # 进行匹配
    if requestType == 1:
#        pkCnt = __GetMergePKPDictValue(curPlayer, ChConfig.Def_PDict_MergePK_Cnt)
#        buyCnt = __GetMergePKPDictValue(curPlayer, ChConfig.Def_PDict_MergePK_BuyCnt)
#        unUsedBuyCnt = __GetMergePKPDictValue(curPlayer, ChConfig.Def_PDict_MergePK_UnUsedBuyCnt)
#        freeCnt, maxBuyCnt, moneyType, buyCostFormat = ReadChConfig.GetEvalChConfig("MergePK_BuyCost")
#
#        # 超出免费次数 且 无可用的已购买次数  则需购买次数
#        if pkCnt >= freeCnt and unUsedBuyCnt <= 0:
#
#            if buyCnt >= maxBuyCnt:
#                GameWorld.Log("    已达到最大可购买PK次数,不可再买!:buyCnt=%s,maxBuyCnt=%s,pkCnt=%s,unUsedBuyCnt=%s"
#                              % (buyCnt, maxBuyCnt, pkCnt, unUsedBuyCnt), playerID)
#                return
#
#            buyCost = eval(buyCostFormat)
#            infoDict = {"pkCnt":pkCnt, "freeCnt":freeCnt, "buyCnt":buyCnt, "buyCost":buyCost}
#            if not PlayerControl.PayMoney(curPlayer, moneyType, buyCost, ChConfig.Def_Cost_BuyMergePKCnt, infoDict):
#                return
#
#            # 增加购买次数 及 未使用的购买次数
#            __SetMergePKPDictValue(curPlayer, ChConfig.Def_PDict_MergePK_BuyCnt, buyCnt + 1)
#            __SetMergePKPDictValue(curPlayer, ChConfig.Def_PDict_MergePK_UnUsedBuyCnt, unUsedBuyCnt + 1)
#            Sync_MergePKCnt(curPlayer)
#
#            GameWorld.Log("    购买PK次数消耗: pkCnt=%s,freeCnt=%s,buyCnt=%s,moneyType=%s,buyCost=%s"
#                              % (pkCnt, freeCnt, buyCnt, moneyType, buyCost), playerID)
        dataMsg = {
                   "seasonID":GetSeasonID(), # 赛季ID
                   "pkZoneID":GetCrossPKZoneID(), # PK赛区
                   "accID":accID,
                   "playerID":playerID,
                   "playerName":CrossRealmPlayer.GetCrossPlayerName(curPlayer),
                   "playerJob":curPlayer.GetJob(),
                   "playerLV":curPlayer.GetLV(),
                   "maxHP":curPlayer.GetMaxHP(),
                   "fightPower":curPlayer.GetFightPower(),
                   "pkScore":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_TotalScore), # 当前积分
                   "danLV":1, #curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_DanLV), # 当前段位
                   "cWinCount":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_CWinCount), # 连胜次数
                   "ondayScore":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_OnDayScore), # 过天时的积分
                   }
        GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PKMatch, dataMsg)
        GameWorld.Log("    发送请求匹配到跨服服务器 dataMsg=%s" % str(dataMsg), playerID)
    # 取消匹配
    else:
        sendMsg = "Client Cancel!"
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "CrossRealmPKCancel", sendMsg, len(sendMsg))
        GameWorld.Log("    发送取消匹配到GameServer sendMsg=%s" % str(sendMsg), playerID)
    return
def CrossServerMsg_PKOverInfo(curPlayer, overInfo):
    ## 收到跨服服务器的PK结算信息
    playerID = curPlayer.GetPlayerID()
    roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, tagPlayerName, notifyState = overInfo
    isWinner = winnerID == playerID
    GameWorld.Log("地图收到跨服PK结算: isWinner=%s,roomID=%s,seasonID=%s,timeStr=%s,overType=%s,winnerID=%s,roundWinnerIDList=%s,pkScore=%s,danLV=%s,cWinCount=%s,addScore=%s,tagPlayerID=%s,notifyState=%s"
                  % (isWinner, roomID, seasonID, timeStr, overType, winnerID, roundWinnerIDList, pkScore, danLV, cWinCount, addScore, tagPlayerID, notifyState), playerID)
    curSeasonID = GetSeasonID()
    if curSeasonID != seasonID:
        GameWorld.Log("    非本赛季的结算信息,不处理!curSeasonID=%s,seasonID=%s" % (curSeasonID, seasonID), playerID)
        return
    # 赛季是否已结算
    if not IsCrossRealmPKSeasonOpen():
        GameWorld.Log("    赛季已经结算过了,不处理!seasonID=%s" % (seasonID), playerID)
        return
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_TotalScore, pkScore)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_DanLV, danLV)
    pkCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_PKCount) + 1
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_PKCount, pkCount)
    if isWinner:
        winCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_WinCount) + 1
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_WinCount, winCount)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_CWinCount, cWinCount)
    else:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_CWinCount, 0)
    # 同一天的话增加当日PK次数
    if GameWorld.CheckTimeIsSameServerDayEx(GameWorld.ChangeTimeStrToNum(timeStr)):
        todayPKCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_CrossPK_TodayPKCount) + 1
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_CrossPK_TodayPKCount, todayPKCount)
    ## 跨服已经通知过了,证明还在跨服服务器,不做以下的处理
    if notifyState:
        return
    overPack = ChPyNetSendPack.tagGCCrossRealmPKOverInfo()
    overPack.TimeStr = timeStr
    overPack.OverType = overType
    overPack.WinnerID = winnerID
    overPack.RoundWinnerID = roundWinnerIDList
    overPack.RoundCount = len(overPack.RoundWinnerID)
    overPack.AddScore = addScore
    overPack.Score = pkScore
    overPack.DanLV = danLV
    overPack.CWinCnt = cWinCount
    overPack.TagName = tagPlayerName
    overPack.TagNameLen = len(overPack.TagName)
    NetPackCommon.SendFakePack(curPlayer, overPack)
    return
#// C1 02 跨服PK购买次数 #tagCMCrossRealmPKBuy
#
#struct    tagCMCrossRealmPKBuy
#{
#    tagHead        Head;
#};
def OnCrossRealmPKBuy(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    return
#// C1 03 跨服PK领取奖励 #tagCMCrossRealmPKGetAward
#
#struct    tagCMCrossRealmPKGetAward
#{
#    tagHead        Head;
#    BYTE        AwardType;    // 奖励类型;1-每日匹配奖励,2-每日胜利奖励,3-段位达标奖励,4-赛季结算奖励
#    BYTE        AwardData;    // 奖励类型对应领取值;每日匹配奖励时为匹配次数,每日胜利奖励时为胜利次数,段位达标奖励时为领取的段位
#};
def OnCrossRealmPKGetAward(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    return
def SyncCrossRealmPKPlayerInfo(curPlayer):
#    DWORD        Score;    // 当前积分
#    BYTE        DanLV;    // 当前段位
#    WORD        PKCount;    // PK次数
#    WORD        WinCount;    // 胜利次数
#    WORD        CWinCount;    // 连胜次数
#    BYTE        DayPKCount;    // 当日已PK次数
#    BYTE        DayWinCount;    // 当日已胜利次数
#    BYTE        DayBuyCount; // 当日已购买次数
    pkPlayerInfo = ChPyNetSendPack.tagMCCrossRealmPKPlayerInfo()
    pkPlayerInfo.Score = 0
    pkPlayerInfo.DanLV = 0
    return
def SyncCrossRealmPKAwardState(curPlayer):
#    DWORD        DayPKCountAwardState;    // 每日匹配次数奖励记录,二进制位存储是否已领取,按匹配次数升序排序索引代表奖励位
#    DWORD        DayWinCountAwardState;    // 每日胜利次数奖励记录,二进制位存储是否已领取,按胜利次数升序排序索引代表奖励位
#    DWORD        DanLVAwardState;        // 段位达标奖励记录,二进制位存储是否已领取,按段位代表奖励位
#    BYTE        SeasonAwardState;    // 赛季结算奖励是否已领取
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -73,6 +73,7 @@
import PlayerNewGuyCard
import PlayerMergeKing
import PlayerMergePK
import PlayerCrossRealmPK
import PlayerPet
import ReloadModule
import BossHurtMng
@@ -548,6 +549,8 @@
        NPCCommon.CollNPCTimeOnDay(curPlayer)
        #副本助战
        FBHelpBattle.DoPlayerOnDay(curPlayer)
        #跨服竞技场
        PlayerCrossRealmPK.DoPlayerOnDay(curPlayer)
        
    PlayerTJG.TJGOnDay(curPlayer, onEventType)
    # 以下为支持两种重置模式切换配置的
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossPKOverInfo.py
New file
@@ -0,0 +1,50 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.RemoteQuery.GY_Query_CrossPKOverInfo
#
# @todo:跨服PK结果同步
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服PK结果同步
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import PlayerCrossRealmPK
#------------------------------------------------------------------------------
## 跨服赛报名调用接口
#  @param query_Type 请求类型
#  @param query_ID 请求的玩家ID
#  @param packCMDList 发包命令
#  @param tick 当前时间
#  @return "True" or "False" or ""
#  @remarks 函数详细说明.
def DoLogic(query_Type, query_ID, packCMDList, tick):
    return
#------------------------------------------------------------------------------
## 执行结果
#  @param curPlayer 发出请求的玩家
#  @param callFunName 功能名称
#  @param funResult 查询的结果
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def DoResult(curPlayer, callFunName, funResult, tick):
    overInfo = eval(funResult)
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("GY_Query_CrossPKOverInfo overInfo=%s" % (overInfo), playerID)
    PlayerCrossRealmPK.CrossServerMsg_PKOverInfo(curPlayer, overInfo)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossRealmReg.py
New file
@@ -0,0 +1,94 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.RemoteQuery.GY_Query_CrossRealmReg
#
# @todo:跨服活动玩家注册
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 跨服活动玩家注册
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import CrossRealmPlayer
import IPY_GameWorld
import ChPlayer
#------------------------------------------------------------------------------
## 跨服赛报名调用接口
#  @param query_Type 请求类型
#  @param query_ID 请求的玩家ID
#  @param packCMDList 发包命令
#  @param tick 当前时间
#  @return "True" or "False" or ""
#  @remarks 函数详细说明.
def DoLogic(query_Type, query_ID, packCMDList, tick):
    return
#------------------------------------------------------------------------------
## 执行结果
#  @param curPlayer 发出请求的玩家
#  @param callFunName 功能名称
#  @param funResult 查询的结果
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def DoResult(curPlayer, callFunName, funResult, tick):
    resultInfo = eval(funResult)
    actionType = resultInfo[0]
    mapPosInfo = resultInfo[1:]
    if not curPlayer:
        return
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("GY_Query_CrossRealmReg DoResult %s" % funResult, playerID)
    if GameWorld.IsMergeServer():
        GameWorld.Log("    跨服服务器不允许上传报名数据!", playerID)
        return
    if not mapPosInfo:
        return
    #跨服前更新自己所属服务器组ID
    ChPlayer.UpdatePlayerServerGroupID(curPlayer)
    mapID, dataMapID, copyMapID, posX, posY = mapPosInfo
    #curPlayer.SendMergeRegisterPlayer(mapID, dataMapID, copyMapID, posX, posY)
    curPlayer.SendMergeRegisterPlayerAfterChange(CrossRealmPlayer.GetCrossPlayerName(curPlayer), mapID, dataMapID, copyMapID, posX, posY)
    GameWorld.Log("    发送跨服玩家数据注册: actionType=%s,mapID=%s,dataMapID=%s,copyMapID=%s,posX=%s,posY=%s,GetVsRoomId=%s"
                  % (actionType, mapID, dataMapID, copyMapID, posX, posY, curPlayer.GetVsRoomId()), playerID)
    return
## 跨服赛报名结果(上传数据)
#  @param index 玩家索引
#  @param tick 当前时间
#  @return None
def GameServer_MergeRegisterResult(index, tick):
    registerResult = IPY_GameWorld.IPY_GMMergeRegisterPlayerResult()
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    result = registerResult.GetResult()
    if not result:
        errorMsg = registerResult.GetErrorMsg()
        GameWorld.Log("CrossRealmReg result Error:%s" % errorMsg, playerID)
        return
    #newAccount = registerResult.GetAccount()
    #newName = registerResult.GetPwd()
    msgList = str([])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, "CrossRealmReg", msgList, len(msgList))
    GameWorld.Log("GameServer_MergeRegisterResult msgList=%s" % msgList, playerID)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_SetPlayerAttr.py
New file
@@ -0,0 +1,59 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.RemoteQuery.GY_Query_SetPlayerAttr
#
# @todo:设置玩家属性
# @author hxp
# @date 2018-12-21
# @version 1.0
#
# 详细描述: 设置玩家属性
#
#-------------------------------------------------------------------------------
#"""Version = 2018-12-21 18:00"""
#-------------------------------------------------------------------------------
import GameWorld
import PlayerControl
import CrossRealmPlayer
## 执行结果
#  @param curPlayer 发出请求的玩家
#  @param callFunName 功能名称
#  @param funResult 查询的结果
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def DoResult(curPlayer, callFunName, funResult, tick):
    if not funResult:
        return
    setInfo = eval(funResult)
    if not isinstance(setInfo, list) or len(setInfo) < 2:
        return
    attrName, value = setInfo[:2]
    if hasattr(curPlayer, attrName):
        # 要在更新值之前处理
        if attrName == "SetExAttr5":
            if value:
                CrossRealmPlayer.DoEnterCrossRealm(curPlayer)
            elif PlayerControl.GetCrossRealmState(curPlayer):
                CrossRealmPlayer.DoExitCrossRealm(curPlayer)
        callObj = getattr(curPlayer, attrName)
        callObj(value)
        GameWorld.Log("SetPlayerAttr curPlayer.%s, value=%s" % (attrName, value), curPlayer.GetPlayerID())
    elif hasattr(PlayerControl, attrName):
        callObj = getattr(PlayerControl, attrName)
        callObj(curPlayer, value)
        GameWorld.Log("SetPlayerAttr PlayerControl.%s, value=%s" % (attrName, value), curPlayer.GetPlayerID())
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/ServerScript.ini
@@ -174,9 +174,9 @@
PacketCallFunc_1=GameSever_PlayerCountByCountry
[PlayerMergeWar]
ScriptName = Player\RemoteQuery\GY_Query_MergeWarRegister.py
Writer = wdb
Releaser = wdb
ScriptName = Player\RemoteQuery\GY_Query_CrossRealmReg.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 1
PacketCMD_1=0x9