9415 【BT5】【后端】古神战场(初版:包含战场副本外的所有功能;副本中暂仅支持击杀玩家玩法)

# Conflicts:
# ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
# ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
# ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerControl.py
# ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
29个文件已修改
6个文件已添加
4329 ■■■■■ 已修改文件
ServerPython/CoreServerGroup/GameServer/PyNetPack.ini 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CrossFB.py 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/test_OnDay.py 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/test_OnWeek.py 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBattlefield.py 1120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerCompensation.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerEventCounter.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py 356 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/CrossBattle.py 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossBattlefield.py 890 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActivity.py 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossBattlefield.py 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossBattlefield.py 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
@@ -364,6 +364,21 @@
PacketSubCMD_1=0x05
PacketCallFunc_1=OnQueryNotifyEquipDetailInfo
[CrossBattlefield]
ScriptName = GameWorldLogic\CrossBattlefield.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 2
PacketCMD_1=0xC0
PacketSubCMD_1=0x07
PacketCallFunc_1=OnCrossBattlefieldJoinByCall
PacketCMD_2=0xC0
PacketSubCMD_2=0x08
PacketCallFunc_2=OnCrossBattlefieldCallKick
[CrossRealmPK]
ScriptName = GameWorldLogic\CrossRealmPK.py
Writer = hxp
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -702,6 +702,8 @@
Def_FBMapID_CrossGrasslandLing = 32040
#跨服仙草园
Def_FBMapID_CrossGrasslandXian = 32050
#跨服战场
Def_FBMapID_CrossBattlefield = 32060
#情缘副本
Def_FBMapID_Love = 31300
@@ -717,6 +719,7 @@
                         Def_FBMapID_CrossDemonKing:"CrossZonePK",
                         Def_FBMapID_CrossGrasslandLing:"CrossZonePK",
                         Def_FBMapID_CrossGrasslandXian:"CrossZonePK",
                         Def_FBMapID_CrossBattlefield:"CrossZonePK",
                         }
#跨服分区对应地图配置表名 - 仅适用于固定地图及虚拟分线的跨服玩法
Def_CrossZoneMapTableName = {Def_FBMapID_CrossPenglai:"CrossPenglaiZoneMap",
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
@@ -3987,6 +3987,126 @@
#------------------------------------------------------
# C0 08 跨服战场召集场次踢人 #tagCGCrossBattlefieldCallKick
class  tagCGCrossBattlefieldCallKick(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("TagPlayerID", c_int),    #目标玩家ID,即要被踢出去的玩家ID
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC0
        self.SubCmd = 0x08
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC0
        self.SubCmd = 0x08
        self.Hour = 0
        self.Minute = 0
        self.TagPlayerID = 0
        return
    def GetLength(self):
        return sizeof(tagCGCrossBattlefieldCallKick)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C0 08 跨服战场召集场次踢人 //tagCGCrossBattlefieldCallKick:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                TagPlayerID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.TagPlayerID
                                )
        return DumpString
m_NAtagCGCrossBattlefieldCallKick=tagCGCrossBattlefieldCallKick()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGCrossBattlefieldCallKick.Cmd,m_NAtagCGCrossBattlefieldCallKick.SubCmd))] = m_NAtagCGCrossBattlefieldCallKick
#------------------------------------------------------
# C0 07 跨服战场加入召集场次 #tagCGCrossBattlefieldJoinByCall
class  tagCGCrossBattlefieldJoinByCall(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("BuyPlayerID", c_int),    #加入目标玩家的召集队伍,即购买召集场的玩家ID
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC0
        self.SubCmd = 0x07
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC0
        self.SubCmd = 0x07
        self.Hour = 0
        self.Minute = 0
        self.BuyPlayerID = 0
        return
    def GetLength(self):
        return sizeof(tagCGCrossBattlefieldJoinByCall)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C0 07 跨服战场加入召集场次 //tagCGCrossBattlefieldJoinByCall:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                BuyPlayerID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.BuyPlayerID
                                )
        return DumpString
m_NAtagCGCrossBattlefieldJoinByCall=tagCGCrossBattlefieldJoinByCall()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGCrossBattlefieldJoinByCall.Cmd,m_NAtagCGCrossBattlefieldJoinByCall.SubCmd))] = m_NAtagCGCrossBattlefieldJoinByCall
#------------------------------------------------------
# C0 03 强制退出跨服状态 #tagCGForceQuitCrossState
class  tagCGForceQuitCrossState(Structure):
@@ -18941,6 +19061,66 @@
#------------------------------------------------------
# C1 09 跨服战场购买开启场次 #tagCMCrossBattlefieldBuyOpen
class  tagCMCrossBattlefieldBuyOpen(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("Faction", c_ubyte),    #阵营 1-红;2-蓝
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC1
        self.SubCmd = 0x09
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC1
        self.SubCmd = 0x09
        self.Hour = 0
        self.Minute = 0
        self.Faction = 0
        return
    def GetLength(self):
        return sizeof(tagCMCrossBattlefieldBuyOpen)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C1 09 跨服战场购买开启场次 //tagCMCrossBattlefieldBuyOpen:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                Faction:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.Faction
                                )
        return DumpString
m_NAtagCMCrossBattlefieldBuyOpen=tagCMCrossBattlefieldBuyOpen()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMCrossBattlefieldBuyOpen.Cmd,m_NAtagCMCrossBattlefieldBuyOpen.SubCmd))] = m_NAtagCMCrossBattlefieldBuyOpen
#------------------------------------------------------
# C1 06 跨服NPC对话 #tagCMCrossNPCTalk
class  tagCMCrossNPCTalk(Structure):
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
@@ -12288,6 +12288,283 @@
#------------------------------------------------------
# C0 09 跨服战场玩家购买战场信息 #tagGCCrossBattlefieldBuyInfo
class  tagGCCrossBattlefieldPlayer(Structure):
    PlayerID = 0    #(DWORD PlayerID)
    PlayerName = ""    #(char PlayerName[33])
    Job = 0    #(BYTE Job)
    LV = 0    #(WORD LV)//等级
    RealmLV = 0    #(WORD RealmLV)//境界
    FightPower = 0    #(DWORD FightPower)//战力求余亿部分
    FightPowerEx = 0    #(DWORD FightPowerEx)//战力整除亿部分
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.PlayerName,_pos = CommFunc.ReadString(_lpData, _pos,33)
        self.Job,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.LV,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.RealmLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        return _pos
    def Clear(self):
        self.PlayerID = 0
        self.PlayerName = ""
        self.Job = 0
        self.LV = 0
        self.RealmLV = 0
        self.FightPower = 0
        self.FightPowerEx = 0
        return
    def GetLength(self):
        length = 0
        length += 4
        length += 33
        length += 1
        length += 2
        length += 2
        length += 4
        length += 4
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteDWORD(data, self.PlayerID)
        data = CommFunc.WriteString(data, 33, self.PlayerName)
        data = CommFunc.WriteBYTE(data, self.Job)
        data = CommFunc.WriteWORD(data, self.LV)
        data = CommFunc.WriteWORD(data, self.RealmLV)
        data = CommFunc.WriteDWORD(data, self.FightPower)
        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
        return data
    def OutputString(self):
        DumpString = '''
                                PlayerID:%d,
                                PlayerName:%s,
                                Job:%d,
                                LV:%d,
                                RealmLV:%d,
                                FightPower:%d,
                                FightPowerEx:%d
                                '''\
                                %(
                                self.PlayerID,
                                self.PlayerName,
                                self.Job,
                                self.LV,
                                self.RealmLV,
                                self.FightPower,
                                self.FightPowerEx
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyPlayer(Structure):
    BuyPlayerID = 0    #(DWORD BuyPlayerID)//购买的玩家ID,即召集人
    Faction = 0    #(BYTE Faction)//阵营 1-红;2-蓝
    FactionPlayerCount = 0    #(BYTE FactionPlayerCount)
    FactionPlayerList = list()    #(vector<tagGCCrossBattlefieldPlayer> FactionPlayerList)//阵营所有玩家列表,包含召集人
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.BuyPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.Faction,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.FactionPlayerCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.FactionPlayerCount):
            temFactionPlayerList = tagGCCrossBattlefieldPlayer()
            _pos = temFactionPlayerList.ReadData(_lpData, _pos)
            self.FactionPlayerList.append(temFactionPlayerList)
        return _pos
    def Clear(self):
        self.BuyPlayerID = 0
        self.Faction = 0
        self.FactionPlayerCount = 0
        self.FactionPlayerList = list()
        return
    def GetLength(self):
        length = 0
        length += 4
        length += 1
        length += 1
        for i in range(self.FactionPlayerCount):
            length += self.FactionPlayerList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteDWORD(data, self.BuyPlayerID)
        data = CommFunc.WriteBYTE(data, self.Faction)
        data = CommFunc.WriteBYTE(data, self.FactionPlayerCount)
        for i in range(self.FactionPlayerCount):
            data = CommFunc.WriteString(data, self.FactionPlayerList[i].GetLength(), self.FactionPlayerList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                BuyPlayerID:%d,
                                Faction:%d,
                                FactionPlayerCount:%d,
                                FactionPlayerList:%s
                                '''\
                                %(
                                self.BuyPlayerID,
                                self.Faction,
                                self.FactionPlayerCount,
                                "..."
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyHM(Structure):
    Hour = 0    #(BYTE Hour)//战场开启时
    Minute = 0    #(BYTE Minute)//战场开启分
    BuyPlayerCount = 0    #(BYTE BuyPlayerCount)
    BuyPlayerList = list()    #(vector<tagGCCrossBattlefieldBuyPlayer> BuyPlayerList)//购买本场次的玩家信息列表
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.Hour,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.Minute,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.BuyPlayerCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.BuyPlayerCount):
            temBuyPlayerList = tagGCCrossBattlefieldBuyPlayer()
            _pos = temBuyPlayerList.ReadData(_lpData, _pos)
            self.BuyPlayerList.append(temBuyPlayerList)
        return _pos
    def Clear(self):
        self.Hour = 0
        self.Minute = 0
        self.BuyPlayerCount = 0
        self.BuyPlayerList = list()
        return
    def GetLength(self):
        length = 0
        length += 1
        length += 1
        length += 1
        for i in range(self.BuyPlayerCount):
            length += self.BuyPlayerList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteBYTE(data, self.Hour)
        data = CommFunc.WriteBYTE(data, self.Minute)
        data = CommFunc.WriteBYTE(data, self.BuyPlayerCount)
        for i in range(self.BuyPlayerCount):
            data = CommFunc.WriteString(data, self.BuyPlayerList[i].GetLength(), self.BuyPlayerList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                Hour:%d,
                                Minute:%d,
                                BuyPlayerCount:%d,
                                BuyPlayerList:%s
                                '''\
                                %(
                                self.Hour,
                                self.Minute,
                                self.BuyPlayerCount,
                                "..."
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyInfo(Structure):
    Head = tagHead()
    HMCount = 0    #(BYTE HMCount)// 为0时清空重置,其他为增量更新
    HMBuyList = list()    #(vector<tagGCCrossBattlefieldBuyHM> HMBuyList)//购买场次列表
    data = None
    def __init__(self):
        self.Clear()
        self.Head.Cmd = 0xC0
        self.Head.SubCmd = 0x09
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        _pos = self.Head.ReadData(_lpData, _pos)
        self.HMCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.HMCount):
            temHMBuyList = tagGCCrossBattlefieldBuyHM()
            _pos = temHMBuyList.ReadData(_lpData, _pos)
            self.HMBuyList.append(temHMBuyList)
        return _pos
    def Clear(self):
        self.Head = tagHead()
        self.Head.Clear()
        self.Head.Cmd = 0xC0
        self.Head.SubCmd = 0x09
        self.HMCount = 0
        self.HMBuyList = list()
        return
    def GetLength(self):
        length = 0
        length += self.Head.GetLength()
        length += 1
        for i in range(self.HMCount):
            length += self.HMBuyList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
        data = CommFunc.WriteBYTE(data, self.HMCount)
        for i in range(self.HMCount):
            data = CommFunc.WriteString(data, self.HMBuyList[i].GetLength(), self.HMBuyList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                Head:%s,
                                HMCount:%d,
                                HMBuyList:%s
                                '''\
                                %(
                                self.Head.OutputString(),
                                self.HMCount,
                                "..."
                                )
        return DumpString
m_NAtagGCCrossBattlefieldBuyInfo=tagGCCrossBattlefieldBuyInfo()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCCrossBattlefieldBuyInfo.Head.Cmd,m_NAtagGCCrossBattlefieldBuyInfo.Head.SubCmd))] = m_NAtagGCCrossBattlefieldBuyInfo
#------------------------------------------------------
# C0 07 跨服排行榜信息 #tagGCCrossBillboardInfo
class  tagGCCrossBillboardData(Structure):
@@ -40971,6 +41248,62 @@
#------------------------------------------------------
# C1 07 跨服战场玩家信息 #tagMCCrossBattlefieldPlayerInfo
class  tagMCCrossBattlefieldPlayerInfo(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("BuyOpenCountToday", c_ubyte),    # 今日已购买开启战场次数
                  ("HighScoreToday", c_int),    # 今日最高积分
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC1
        self.SubCmd = 0x07
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC1
        self.SubCmd = 0x07
        self.BuyOpenCountToday = 0
        self.HighScoreToday = 0
        return
    def GetLength(self):
        return sizeof(tagMCCrossBattlefieldPlayerInfo)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C1 07 跨服战场玩家信息 //tagMCCrossBattlefieldPlayerInfo:
                                Cmd:%s,
                                SubCmd:%s,
                                BuyOpenCountToday:%d,
                                HighScoreToday:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.BuyOpenCountToday,
                                self.HighScoreToday
                                )
        return DumpString
m_NAtagMCCrossBattlefieldPlayerInfo=tagMCCrossBattlefieldPlayerInfo()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCCrossBattlefieldPlayerInfo.Cmd,m_NAtagMCCrossBattlefieldPlayerInfo.SubCmd))] = m_NAtagMCCrossBattlefieldPlayerInfo
#------------------------------------------------------
# C1 02 跨服PK玩家奖励记录 #tagMCCrossRealmPKAwardState
class  tagMCCrossRealmPKAwardState(Structure):
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CrossFB.py
New file
@@ -0,0 +1,212 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GM.Commands.CrossFB
#
# @todo:跨服副本
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服副本
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import GameWorld
import PyGameData
import IpyGameDataPY
import PlayerFB
#---------------------------------------------------------------------
#逻辑实现
## 执行逻辑
#  @param curPlayer 当前玩家
#  @param gmList [cmdIndex gmAccID msg]
#  @return None
#  @remarks 函数详细说明.
def OnExec(curPlayer, gmList):
    ## 本服处理
    GameWorld.DebugAnswer(curPlayer, "----------------------------")
    if not gmList:
        GameWorld.DebugAnswer(curPlayer, "输出人数: CrossFB mapID")
        GameWorld.DebugAnswer(curPlayer, "虚拟进入: CrossFB mapID 功能线路 人数 等级")
        GameWorld.DebugAnswer(curPlayer, "添加人数: CrossFB 1 场景ID 虚拟线路 在线 离线")
        GameWorld.DebugAnswer(curPlayer, "扣除人数: CrossFB 2 场景ID 虚拟线路 在线 离线")
    return
def OnMergeServerExec(gmList, tick):
    ## 跨服处理
    serverGroupID = gmList[-2]
    playerID = gmList[-1]
    dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1, {})
    gmList = gmList[:-2]
    if not gmList:
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "---以下为可分流场景虚拟线路条数")
        for mapID, mapIDList in dynamicLineMapDict.items():
            gameMap = GameWorld.GetMap(mapID)
            if not gameMap:
                continue
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "动态分流: %s【mapID:%s】" % (gameMap.GetMapName(), mapID))
            for realMapID in mapIDList:
                GameWorld.DebugAnswerCross(playerID, serverGroupID, "    分流场景虚拟线路数:%s\t%s"
                                           % (realMapID, PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0)))
        return
    value0 = gmList[0]
    mapID = value0
    if value0 in [1, 2]:
        if len(gmList) != 5:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "参数不足,请重新输入")
            return
        realMapID, copyMapID, fbPlayerCount, offlineCount = gmList[1:]
        key = (realMapID, copyMapID)
        if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "不存在该场景线路或未开启!realMapID=%s,copyMapID=%s" % (realMapID, copyMapID))
            return
        copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] # CrossCopyMapInfo
        mapID = __GetMapIDByRealMapID(dynamicLineMapDict, realMapID)
        if mapID not in dynamicLineMapDict:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "分流场景地图错误")
            return
        # 添加
        if value0 == 1:
            robotID = __GetMapRobotID(dynamicLineMapDict[mapID])
            for _ in xrange(fbPlayerCount):
                robotID += 1
                copyMapObj.fbPlayerDict[robotID] = serverGroupID
            for _ in xrange(offlineCount):
                robotID += 1
                copyMapObj.offlinePlayerDict[robotID] = serverGroupID
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "场景%s线%s:添加在线(%s),离线(%s)" % (realMapID, copyMapID, fbPlayerCount, offlineCount))
        # 扣除
        else:
            delFBPlayerCount, delOfflinePlayerCount = 0, 0
            fbPlayerIDList = copyMapObj.fbPlayerDict.keys()
            for pID in fbPlayerIDList:
                if pID < 10000:
                    copyMapObj.fbPlayerDict.pop(pID, 0)
                    delFBPlayerCount += 1
                    if delFBPlayerCount >= fbPlayerCount:
                        break
            offlinePlayerIDList = copyMapObj.offlinePlayerDict.keys()
            for pID in offlinePlayerIDList:
                if pID < 10000:
                    copyMapObj.offlinePlayerDict.pop(pID, 0)
                    delOfflinePlayerCount += 1
                    if delOfflinePlayerCount >= offlineCount:
                        break
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "场景%s线%s:删除在线(%s),离线(%s)" % (realMapID, copyMapID, delFBPlayerCount, delOfflinePlayerCount))
        waitPlayerCount, fbPlayerCount, offlinePlayerCount = len(copyMapObj.waitPlayerDict), len(copyMapObj.fbPlayerDict), len(copyMapObj.offlinePlayerDict)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "    %s,功能区-线-场:%s-%s-%s,等待%s,副本%s,离线%s"
                                   % (copyMapID, copyMapObj.zoneID, copyMapObj.funcLineID, copyMapObj.newFuncLineNum,
                                      waitPlayerCount, fbPlayerCount, offlinePlayerCount))
    # 虚拟玩家进入: CrossFB mapID funcLineID 等级 人数
    elif len(gmList) >= 4:
        mapID, funcLineID, enterCount, lv = gmList
        if mapID not in dynamicLineMapDict:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "mapID错误")
            return
        robotID = __GetMapRobotID(dynamicLineMapDict[mapID])
        GameWorld.DebugLog("mapID=%s,robotID=%s" % (mapID, robotID))
        okCount = 0
        copyMapObj = None
        for _ in xrange(enterCount):
            robotID += 1
            msgData = {"PlayerID":robotID, "MapID":mapID, "FuncLineID":funcLineID, "LV":lv}
            copyMapObj = PlayerFB.ClientServerMsg_EnterFB(serverGroupID, msgData, tick)
            if not copyMapObj:
                GameWorld.DebugAnswerCross(playerID, serverGroupID, "进入失败,查看跨服GameServer日志")
                break
            okCount += 1
            copyMapObj.waitPlayerDict.pop(robotID, None)
            copyMapObj.offlinePlayerDict.pop(robotID, None)
            copyMapObj.fbPlayerDict[robotID] = serverGroupID
        if copyMapObj:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "虚拟进入:%s - %s, LV:(%s), 个数(%s), 成功(%s)" % (mapID, funcLineID, lv, enterCount, okCount))
            waitPlayerCount, fbPlayerCount, offlinePlayerCount = len(copyMapObj.waitPlayerDict), len(copyMapObj.fbPlayerDict), len(copyMapObj.offlinePlayerDict)
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "    %s,功能区-线-场:%s-%s-%s,等待%s,副本%s,离线%s"
                                       % (copyMapObj.copyMapID, copyMapObj.zoneID, copyMapObj.funcLineID, copyMapObj.newFuncLineNum,
                                          waitPlayerCount, fbPlayerCount, offlinePlayerCount))
    elif len(gmList) == 1:
        pass
    else:
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "参数个数错误!")
        return
    gameMap = GameWorld.GetMap(mapID)
    if mapID not in dynamicLineMapDict or not gameMap:
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "不存在该分流副本地图! %s" % mapID)
        return
    GameWorld.DebugAnswerCross(playerID, serverGroupID, "---分流场景明细: %s【mapID:%s】" % (gameMap.GetMapName(), mapID))
    dynamicMapIDList = dynamicLineMapDict[mapID]
    for realMapID in dynamicMapIDList:
        copyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0)
        GameWorld.DebugAnswerCross(playerID, serverGroupID, "分流场景【%s】虚拟线路数%s条" % (realMapID, copyMapCount))
        playerCountTotal = 0
        for copyMapID in xrange(copyMapCount):
            key = (realMapID, copyMapID)
            if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
                continue
            copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] # CrossCopyMapInfo
            waitPlayerCount, fbPlayerCount, offlinePlayerCount = len(copyMapObj.waitPlayerDict), len(copyMapObj.fbPlayerDict), len(copyMapObj.offlinePlayerDict)
            playerCount = waitPlayerCount + fbPlayerCount + offlinePlayerCount
            #if not playerCount:
            #    continue
            playerCountTotal += playerCount
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "    %s,功能区-线-场:%s-%s-%s,等待%s,副本%s,离线%s"
                                       % (copyMapID, copyMapObj.zoneID, copyMapObj.funcLineID, copyMapObj.newFuncLineNum,
                                          waitPlayerCount, fbPlayerCount, offlinePlayerCount))
        if not playerCountTotal:
            GameWorld.DebugAnswerCross(playerID, serverGroupID, "    该分流场景地图没人!")
    return
def OnGetMergeParam(curPlayer):
    playerID = curPlayer.GetPlayerID()
    serverGroupID = GameWorld.GetServerGroupID()
    return [serverGroupID, playerID]
def __GetMapIDByRealMapID(dynamicLineMapDict, realMapID):
    for mapID, mapIDList in dynamicLineMapDict.items():
        if realMapID in mapIDList:
            return mapID
    return 0
def __GetMapRobotID(dynamicMapIDList):
    robotID = 0
    for realMapID in dynamicMapIDList:
        copyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0)
        for copyMapID in xrange(copyMapCount):
            key = (realMapID, copyMapID)
            if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
                continue
            copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] # CrossCopyMapInfo
            playerIDList = list(set(copyMapObj.waitPlayerDict.keys() + copyMapObj.fbPlayerDict.keys() + copyMapObj.offlinePlayerDict.keys()))
            for playerID in playerIDList:
                if playerID < 10000 and playerID > robotID:
                    robotID = playerID
    return robotID
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/test_OnDay.py
@@ -33,22 +33,16 @@
#  @return None
#  @remarks 函数详细说明. 有的任务一天只能做限定次数,或有的操作一天只能做一次
#  用来刷新任务状态或其它
def OnExec(curPlayer,playerList):
def OnExec(curPlayer, gmList):
    __DoExecOnDay(gmList)
    return
def __DoExecOnDay(gmList):
    timeStr = GameWorld.GetCurrentDataTimeStr()
    tick = GameWorld.GetGameWorld().GetTick()
    if len(playerList) == 0:
        DoLogic_GM_OnDay( timeStr , tick )
        return
    count = playerList[0]
    if count > 5:
        return
    for i in range( count ):
        DoLogic_GM_OnDay( timeStr , tick )
    count = 1 if not gmList else min(gmList[0], 5)
    for _ in xrange(count):
        DoLogic_GM_OnDay(timeStr , tick)
    return
## 世界服务器执行  test_OnHour test_OnDay 一次 并通知在线所有玩家
@@ -56,25 +50,34 @@
#  @param tick     当前tick
#  @return 无返回值
#  @remarks 函数详细说明:世界服务器执行 test_OnHour test_OnDay 一次
def DoLogic_GM_OnDay( timeStr , tick ):
def DoLogic_GM_OnDay(timeStr , tick):
    playerManager = GameWorld.GetPlayerManager()
    
    for i in range( 0, playerManager.GetPlayerCount() ):
        curPlayer = playerManager.GetPlayerByIndex( i )
    for i in range(0, playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
            continue
        
        mapID = GameWorld.GetQueryPlayerMapID( curPlayer )
        mapID = GameWorld.GetQueryPlayerMapID(curPlayer)
        
        if not mapID:
            continue
        
        playerManager.MapServer_QueryPlayer( 0, 0, curPlayer.GetID(), mapID, 'GMDateTime', 'GMOnDay', len("GMOnDay"))
        playerManager.MapServer_QueryPlayer(0, 0, curPlayer.GetID(), mapID, 'GMDateTime', 'GMOnDay', len("GMOnDay"))
        
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_OnDayEx, 0)
    
    GameWorldProcess.OnHour( timeStr , tick )
    GameWorldProcess.OnDay( timeStr , tick )
    GameWorldProcess.OnHour(timeStr , tick)
    GameWorldProcess.OnDay(timeStr , tick)
    GameWorldProcess.OnDayEx(tick)
    return
def OnGetMergeParam(curPlayer):
    ## 跨服命令额外参数
    return []
def OnMergeServerExec(gmList, tick):
    ## 跨服执行命令
    __DoExecOnDay(gmList)
    return
ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/test_OnWeek.py
@@ -31,22 +31,16 @@
#  @param playerList 命令执行的次数 小于=于3 
#  @return None
#  @remarks 函数详细说明. 世界服务器执行OnWeek
def OnExec(curPlayer,playerList):
def OnExec(curPlayer, gmList):
    __DoExecOnWeek(gmList)
    return
def __DoExecOnWeek(gmList):
    timeStr = GameWorld.GetCurrentDataTimeStr()
    tick = GameWorld.GetGameWorld().GetTick()
    if len(playerList) == 0:
        DoLogic_GM_OnWeek( timeStr , tick )
        return
    count = playerList[0]
    if count > 3:
        return
    for i in range( count ):
        DoLogic_GM_OnWeek( timeStr, tick )
    count = 1 if not gmList else min(gmList[0], 5)
    for _ in xrange(count):
        DoLogic_GM_OnWeek(timeStr , tick)
    return
## 全局函数简要说明:世界服务器执行  test_OnWeek  一次 并通知在线所有玩家
@@ -79,3 +73,12 @@
    GameWorldProcess.OnWeek( timeStr , tick )
    GameWorldProcess.OnWeekEx(tick)
    return
def OnGetMergeParam(curPlayer):
    ## 跨服命令额外参数
    return []
def OnMergeServerExec(gmList, tick):
    ## 跨服执行命令
    __DoExecOnWeek(gmList)
    return
ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
@@ -1126,6 +1126,24 @@
    curPlayer.DebugAnswer(text)
    return
def CrossServerMsg_DebugAnswer(msgData):
    playerID, text = msgData
    curPlayer = GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    curPlayer.DebugAnswer(text)
    return
def DebugAnswerCross(playerID, serverGroupID, text):
    DebugLog(text)
    text = text.decode(ShareDefine.Def_Game_Character_Encoding).encode(GetCharacterEncoding())
    import CrossRealmMsg
    dataMsg = [playerID, text]
    serverGroupIDList = [serverGroupID]
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_DebugAnswer, dataMsg, serverGroupIDList)
    return
def GetMap(mapID): return IpyGameDataPY.GetIpyGameData("ChinMap", mapID)
def GetNPCData(npcID): return IpyGameDataPY.GetIpyGameData("ChinNPC", npcID)
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBattlefield.py
New file
@@ -0,0 +1,1120 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package CrossBattlefield
#
# @todo:跨服战场
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服战场
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import datetime
import PyGameData
import CrossRealmPK
import CrossRealmMsg
import PlayerControl
import PyDataManager
import ChPyNetSendPack
import CrossRealmPlayer
import PlayerCompensation
import PlayerViewCache
import CrossBillboard
import IpyGameDataPY
import NetPackCommon
import ShareDefine
import GameWorld
import PlayerFB
import ChConfig
import operator
import time
'''
热更配置
修改开启时间,只能等当天都结束了才能更新,即隔天生效,不然可能导致已购买场次的玩家购买数据丢失
其他配置暂无影响,可直接热更
合服
开放时间同跨服PK一样,赛季结束期间不可购买,不可开启,给分区调整腾出空间,功能不考虑分区变更带来的问题
'''
Def_RecType_CrossBattlefieldBuy = ShareDefine.Def_UniversalGameRecType_CrossBattlefieldBuy
'''
玩家开启的跨服战场
ShareDefine.Def_UniversalGameRecType_CrossBattlefieldBuy
time:time                购买时间
value1:zoneID            分区ID
value2:hmNum            时分场次编号
value3:playerID        购买的玩家ID
value4:factionID        所选择的阵营ID
StrValue3:[callPlayerID,...]    召集来的玩家ID,包含自己
'''
class CrossBattlefieldBuy():
    ''' 跨服战场购买记录
    '''
    def __init__(self):
        self.Clear()
        return
    def Clear(self):
        self.buyTime = 0
        self.hmNum = 0
        self.zoneID = 0
        self.playerID = 0
        self.factionID = 0
        self.callPlayerIDList = []
        # 子服用,跨服服务器同步数据时负值; 不存库,玩家属性缓存信息,本服玩家取自己服务器最新缓存
        self.callPlayerDict = {} # {playerID:{attrName:value, ...}, ...}
        return
    def GetSyncClientServerString(self):
        self.callPlayerDict = {}
        for playerID in self.callPlayerIDList:
            curCache = PlayerViewCache.FindViewCache(playerID)
            if not curCache:
                continue
            cacheDict = PlayerViewCache.GetCachePropDataDict(curCache)
            self.callPlayerDict[playerID] = {"Name":cacheDict["Name"], "Job":cacheDict["Job"], "LV":cacheDict["LV"],
                                             "RealmLV":cacheDict["RealmLV"], "FightPower":cacheDict["FightPower"]}
        return {"buyTime":self.buyTime, "hmNum":self.hmNum, "zoneID":self.zoneID, "playerID":self.playerID, "factionID":self.factionID,
                "callPlayerIDList":self.callPlayerIDList, "callPlayerDict":self.callPlayerDict}
    def SetAttr(self, attrDict):
        for k, v in attrDict.items():
            setattr(self, k, v)
        return
def OnServerStart():
    if not GameWorld.IsCrossServer():
        return
    PyGameData.g_crossBattlefieldBuyInfo = {}
    universalRecMgr = GameWorld.GetUniversalRecMgr()
    recDataList = universalRecMgr.GetTypeList(Def_RecType_CrossBattlefieldBuy)
    GameWorld.Log("加载跨服战场购买记录! %s" % recDataList.Count())
    for index in xrange(recDataList.Count()):
        recData = recDataList.At(index)
        zoneID = recData.GetValue1()
        hmNum = recData.GetValue2()
        playerID = recData.GetValue3()
        factionID = recData.GetValue4()
        strValue3 = recData.GetStrValue3()
        buyRec = CrossBattlefieldBuy()
        buyRec.buyTime = recData.GetTime()
        buyRec.zoneID = zoneID
        buyRec.hmNum = hmNum
        buyRec.playerID = playerID
        buyRec.factionID = factionID
        buyRec.callPlayerIDList = eval(strValue3) if strValue3 else []
        buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
        buyPlayerInfo[playerID] = buyRec
        GameWorld.Log("    %s" % buyRec.GetSyncClientServerString())
    return
def OnServerClose():
    if not GameWorld.IsCrossServer():
        return
    universalRecMgr = GameWorld.GetUniversalRecMgr()
    universalRecMgr.Delete(Def_RecType_CrossBattlefieldBuy)
    GameWorld.Log("保存跨服战场购买记录!")
    recDataList = universalRecMgr.GetTypeList(Def_RecType_CrossBattlefieldBuy)
    for _, buyHMInfo in PyGameData.g_crossBattlefieldBuyInfo.items():
        for _, buyPlayerInfo in buyHMInfo.items():
            for buyRec in buyPlayerInfo.values():
                GameWorld.Log("    %s" % buyRec.GetSyncClientServerString())
                recData = recDataList.AddRec()
                recData.SetTime(buyRec.buyTime)
                recData.SetValue1(buyRec.zoneID)
                recData.SetValue2(buyRec.hmNum)
                recData.SetValue3(buyRec.playerID)
                recData.SetValue4(buyRec.factionID)
                recData.SetStrValue3(str(buyRec.callPlayerIDList).replace(" ", ""))
    return
def GetHMNum(openHour, openMinute): return openHour * 100 + openMinute
def GetHMByNum(hmNum): return hmNum / 100, hmNum % 100
def OnPlayerLogin(curPlayer):
    serverGroupID = GameWorld.GetServerGroupID()
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    SyncCrossBattlefieldBuyInfo(None, zoneID)
    return
def DoOnDayEx():
    if not GameWorld.IsCrossServer():
        return
    PyGameData.g_crossBattlefieldBuyInfo = {}
    Sync_CrossBattlefieldDataToClientServer()
    return
def DoOnWeekEx():
    if not GameWorld.IsCrossServer():
        return
    enterWeekOrderAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBillboard", 3, {}) # 周参与榜名次对应奖励物品列表 {"名次":[[物品ID,个数,是否拍品], ...], ...} , 名次配置支持段配置
    callWeekOrderAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBillboard", 4, {}) # 周召集榜名次对应奖励物品列表 {"名次":[[物品ID,个数,是否拍品], ...], ...} , 名次配置支持段配置
    scoreWeekOrderAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBillboard", 5, {}) # 周参与榜名次对应奖励物品列表 {"名次":[[物品ID,个数,是否拍品], ...], ...} , 名次配置支持段配置
    enterWeekOrderIntAwardDict = {int(k):v for k, v in enterWeekOrderAwardDict.items()}
    callWeekOrderIntAwardDict = {int(k):v for k, v in callWeekOrderAwardDict.items()}
    scoreWeekOrderIntAwardDict = {int(k):v for k, v in scoreWeekOrderAwardDict.items()}
    billboardDict = {
                     ShareDefine.Def_CBT_BattlefieldWJoin:[enterWeekOrderIntAwardDict, "CrossBattlefieldEnterOrderWeek"],
                     ShareDefine.Def_CBT_BattlefieldWCall:[callWeekOrderIntAwardDict, "CrossBattlefieldCallOrderWeek"],
                     ShareDefine.Def_CBT_BattlefieldWScore:[scoreWeekOrderIntAwardDict, "CrossBattlefieldScoreOrderWeek"],
                     }
    # 结算周榜
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    for billboardType, doInfo in billboardDict.items():
        awardDict, mailKey = doInfo
        groupList = billboardMgr.GetBillboardGroupList(billboardType)
        GameWorld.Log("过周结算跨服战场周榜奖励: billboardType=%s,groupList=%s" % (billboardType, groupList))
        for billboardType, groupValue1, groupValue2 in groupList:
            billboardObj = billboardMgr.GetCrossBillboard(billboardType, groupValue1, groupValue2)
            if not billboardObj:
                continue
            billboardObj.SortData()
            for i in xrange(billboardObj.GetCount()):
                billboardData = billboardObj.At(i)
                if not billboardData:
                    continue
                playerID = billboardData.ID
                rank = i + 1
                awardItemList = GameWorld.GetOrderValueByDict(awardDict, rank)
                paramList = [rank]
                PlayerCompensation.SendMailByKey(mailKey, [playerID], awardItemList, paramList, crossMail=True)
            billboardObj.ClearData()
    return
def GetCrossBattlefieldState():
    return GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield)
def IsBattlefieldCallPlayer(playerID):
    ## 是否战场召集玩家,包含被召集玩家
    for buyHMInfo in PyGameData.g_crossBattlefieldBuyInfo.values():
        for buyPlayerInfo in buyHMInfo.values():
            for buyRec in buyPlayerInfo.values():
                if playerID in buyRec.callPlayerIDList:
                    return True
    return False
def GetBuyPlayerInfo(zoneID, hmNum):
    ## 获取战场购买召集场次玩家信息
    ## @return: buyPlayerInfo={playerID:CrossBattlefieldBuy, ...}
    if zoneID not in PyGameData.g_crossBattlefieldBuyInfo:
        PyGameData.g_crossBattlefieldBuyInfo[zoneID] = {}
    buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo[zoneID]
    if hmNum not in buyHMInfo:
        buyHMInfo[hmNum] = {}
    buyPlayerInfo = buyHMInfo[hmNum]
    return buyPlayerInfo
def Sync_CrossBattlefieldDataToClientServer(serverGroupID=0):
    ''' 同步跨服战场数据到子服务器
    @param serverGroupID: 为0时同步所有子服
    '''
    GameWorld.Log("同步给子服跨服战场数据: syncServerGroupID=%s" % (serverGroupID))
    if serverGroupID:
        ipyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
        if not ipyData:
            return
        crossZoneList = [ipyData]
    else:
        crossZoneName = GameWorld.GetCrossZoneName()
        crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
    if not crossZoneList:
        return
    # 通知状态 - 全区一致
    battlefieldState = GetCrossBattlefieldState()
    dataMsg = {"battlefieldState":battlefieldState}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_BattlefieldState, dataMsg)
    for zoneIpyData in crossZoneList:
        zoneID = zoneIpyData.GetZoneID()
        serverGroupIDList = [serverGroupID] if serverGroupID else zoneIpyData.GetServerGroupIDList()
        # 通知购买信息
        Send_CrossServerMsg_BattlefieldBuy(zoneID, serverGroupIDList)
    return
def Send_CrossServerMsg_BattlefieldBuy(zoneID, serverGroupIDList, opData=None):
    # 通知子服购买信息
    buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo.get(zoneID, {})
    if opData:
        hmNum = GetHMNum(opData["openHour"], opData["openMinute"])
        hmNumList = [hmNum]
    else:
        hmNumList = buyHMInfo.keys()
    syncBuyHMInfo = {}
    for hmNum in hmNumList:
        if hmNum not in buyHMInfo:
            continue
        buyPlayerInfo = buyHMInfo[hmNum]
        syncBuyPlayerInfo = {}
        for playerID, buyRec in buyPlayerInfo.items():
            syncBuyPlayerInfo[playerID] = buyRec.GetSyncClientServerString()
        syncBuyHMInfo[hmNum] = syncBuyPlayerInfo
    sendMsg = {"zoneID":zoneID, "syncBuyHMInfo":syncBuyHMInfo, "opData":opData}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_BattlefieldBuy, sendMsg, serverGroupIDList)
    return
def OnMapServerInitOK():
    # 通知地图服务器状态
    SyncMapServerCrossBattlefieldBuyInfo()
    return
def SyncMapServerCrossBattlefieldBuyInfo():
    #if not GameWorld.IsCrossServer():
    #    return
    syncMapBuyInfo = {}
    for zoneID, buyHMInfo in PyGameData.g_crossBattlefieldBuyInfo.items():
        syncMapBuyInfo[zoneID] = {}
        for hmNum, buyPlayerInfo in buyHMInfo.items():
            buyInfo = {}
            for playerID, buyRec in buyPlayerInfo.items():
                buyInfo[playerID] = {"callPlayerIDList":buyRec.callPlayerIDList, "factionID":buyRec.factionID}
            syncMapBuyInfo[zoneID][hmNum] = buyInfo
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossBattlefieldCallTeamInfo, syncMapBuyInfo)
    return
def OnMinuteProcess():
    if not GameWorld.IsCrossServer():
        return
    Dispose_CrossBattlefieldState()
    return
def __GetCrossBattlefieldTime(isRefreshState=True):
    key = "CrossBattlefieldTimeInfo"
    CrossBattlefieldTimeInfo = IpyGameDataPY.GetConfigEx(key)
    serverTime = GameWorld.GetServerTime()
    reloadSign = "%d-%d-%d" % (serverTime.year, serverTime.month, serverTime.day)
    if CrossBattlefieldTimeInfo and CrossBattlefieldTimeInfo[0] == reloadSign:
        GameWorld.DebugLog("已经加载过本日跨服战场时间状态信息!reloadSign=%s" % reloadSign)
        return False, CrossBattlefieldTimeInfo[1]
    sysOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 1)
    callOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 2)
    allOpenHMList = sysOpenHMList + callOpenHMList
    dailyIpyData = IpyGameDataPY.GetIpyGameData("DailyAction", ShareDefine.DailyActionID_CrossBattlefield)
    fbTotalMinutes = dailyIpyData.GetDuration() if dailyIpyData else 10
    openNotifyMinuteList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 3)
    battleTimeInfoList = []
    startDateStr = "%d-%d-%d" % (serverTime.year, serverTime.month, serverTime.day)
    for openHour, openMinute in allOpenHMList:
        openDateTime = datetime.datetime.strptime("%s %02d:%02d:00" % (startDateStr, openHour, openMinute), ChConfig.TYPE_Time_Format)
        closeDateTime = openDateTime + datetime.timedelta(minutes=fbTotalMinutes)
        notifyOpenTimeDict = {}
        for notifyOpenMinute in openNotifyMinuteList:
            notifyOpenDateTime = openDateTime + datetime.timedelta(minutes=-notifyOpenMinute)
            notifyOpenTimeDict[notifyOpenDateTime] = notifyOpenMinute
        battleTimeInfoList.append([openHour, openMinute, openDateTime, closeDateTime, notifyOpenTimeDict])
    CrossBattlefieldTimeInfo = IpyGameDataPY.SetConfigEx(key, [reloadSign, battleTimeInfoList])
    GameWorld.Log("本日跨服战场时间状态信息加载完毕!reloadSign=%s,isRefreshState=%s" % (reloadSign, isRefreshState))
    GameWorld.Log("    allOpenHMList=%s,fbTotalMinutes=%s" % (allOpenHMList, fbTotalMinutes))
    GameWorld.Log("=============================================================")
    if isRefreshState:
        Dispose_CrossBattlefieldState(True)
    return True, CrossBattlefieldTimeInfo[1]
def Dispose_CrossBattlefieldState(reloadRefresh=False):
    isReload, battlefieldTimeList = __GetCrossBattlefieldTime(False)
    isReload = isReload or reloadRefresh
    # 这里时间需精确到分钟,不然后面的比较会匹配不到
    curDateTime = GameWorld.GetServerTime()
    curDateTime = datetime.datetime.strptime("%d-%d-%d %d:%d:00" % (curDateTime.year, curDateTime.month, curDateTime.day,
                                                                    curDateTime.hour, curDateTime.minute), ChConfig.TYPE_Time_Format)
    battlefieldState = 0
    for openHour, openMinute, openDateTime, closeDateTime, notifyOpenTimeDict in battlefieldTimeList:
        # 全服广播提示信息
        if curDateTime in notifyOpenTimeDict:
            notifyOpenMinute = notifyOpenTimeDict[curDateTime]
            __DoBattlefieldOpenNotify(openHour, openMinute, notifyOpenMinute)
        if openDateTime <= curDateTime < closeDateTime:
            battlefieldState = openHour * 100 + openMinute
    stateKey = ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield
    gameWorld = GameWorld.GetGameWorld()
    beforeState = gameWorld.GetDictByKey(stateKey)
    if not isReload and beforeState == battlefieldState:
        #已经是这个状态了
        return
    GameWorld.SendMapServerMsgEx(stateKey, battlefieldState) #通知Mapserver,设置字典
    gameWorld.SetDict(stateKey, battlefieldState) #更新字典值
    GameWorld.Log("跨服战场状态变更: beforeState=%s,battlefieldState=%s" % (beforeState, battlefieldState))
    # 开启副本
    if battlefieldState and beforeState != battlefieldState:
        # 移除已经存在的副本线路
        PyGameData.g_crossDynamicLineInfo.pop(ChConfig.Def_FBMapID_CrossBattlefield, None)
        crossZoneName = GameWorld.GetCrossZoneName()
        crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
        if not crossZoneList:
            crossZoneList = []
        hmNum = battlefieldState
        openHour, openMinute = GetHMByNum(hmNum)
        sysOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 1)
        for zoneIpyData in crossZoneList:
            zoneID = zoneIpyData.GetZoneID()
            seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
            if seasonState != 1:
                GameWorld.Log("    跨服PK赛季未开启中,跨服战场不开启! zoneID=%s" % zoneID)
                continue
            # 系统局确保每个等级段都有一场
            if [openHour, openMinute] in sysOpenHMList:
                dynamicLineLVRangeDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 4, {})
                lvRangeInfoList = dynamicLineLVRangeDict.get(ChConfig.Def_FBMapID_CrossBattlefield, [])
                GameWorld.Log("    开启战场系统局,确保每个等级段都有一场: zoneID=%s,lvRangeInfoList=%s" % (zoneID, lvRangeInfoList))
                if lvRangeInfoList:
                    funcLineIDList = range(len(lvRangeInfoList))
                    PlayerFB.OpenCrossDynamicLineBySys(zoneID, ChConfig.Def_FBMapID_CrossBattlefield, funcLineIDList, True)
            # 有购买的场次默认只开一场
            else:
                buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo.get(zoneID, {})
                if hmNum in buyHMInfo and len(buyHMInfo[hmNum]) > 0:
                    GameWorld.Log("    有召集的场次开启分区战场! zoneID=%s" % (zoneID))
                    funcLineIDList = [0]
                    PlayerFB.OpenCrossDynamicLineBySys(zoneID, ChConfig.Def_FBMapID_CrossBattlefield, funcLineIDList, True)
                else:
                    GameWorld.Log("    无召集的场次不开分区战场! zoneID=%s" % (zoneID))
    # 同步子服务器
    dataMsg = {"battlefieldState":battlefieldState}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_BattlefieldState, dataMsg)
    return
def __DoBattlefieldOpenNotify(openHour, openMinute, notifyOpenMinute):
    ''' 执行跨服战场开启广播
                    跨服PK赛季未开启状态下,跨服战场同步关闭,故也不广播
                    还要限制开服天开启
    '''
    gameWorld = GameWorld.GetGameWorld()
    sysOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 1)
    if [openHour, openMinute] in sysOpenHMList:
        crossZoneName = GameWorld.GetCrossZoneName()
        crossZoneList = IpyGameDataPY.GetIpyGameDataByCondition("CrossZonePK", {"CrossZoneName":crossZoneName}, True)
        if not crossZoneList:
            return
        for zoneIpyData in crossZoneList:
            zoneID = zoneIpyData.GetZoneID()
            seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
            if seasonState != 1:
                GameWorld.DebugLog("跨服PK赛季未开启中,跨服战场系统开启广播不处理!")
                continue
            notifyKey = "CrossBattlefieldOpenSys"
            paramList = [notifyOpenMinute]
            country = 0
            serverGroupIDList = []
            crossNotifyList = []
            crossNotifyList.append([ShareDefine.CrossNotify_World, [country, notifyKey, paramList]])
            PlayerControl.CrossNotifyEx(serverGroupIDList, crossNotifyList)
        return
    curHMNum = GetHMNum(openHour, openMinute)
    for zoneID, buyHMInfo in PyGameData.g_crossBattlefieldBuyInfo.items():
        seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
        if seasonState != 1:
            GameWorld.DebugLog("跨服PK赛季未开启中,跨服战场玩家开启广播不处理!")
            continue
        zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByZoneID(ChConfig.Def_FBMapID_CrossBattlefield, zoneID)
        if zoneIpyData:
            continue
        serverGroupIDList = zoneIpyData.GetServerGroupIDList()
        if curHMNum not in buyHMInfo:
            continue
        buyPlayerInfo = buyHMInfo[curHMNum]
        if not buyPlayerInfo:
            continue
        matchTickSortList = sorted(buyPlayerInfo.values(), key=operator.attrgetter("buyTime"))
        buyRec = matchTickSortList[0]
        notifyKey = "CrossBattlefieldOpenPlayer"
        paramList = [buyRec.playerName, notifyOpenMinute]
        country = 0
        crossNotifyList = []
        crossNotifyList.append([ShareDefine.CrossNotify_World, [country, notifyKey, paramList]])
        PlayerControl.CrossNotifyEx(serverGroupIDList, crossNotifyList)
    return
def GetCrossBattlefieldOpenTime(zoneID):
    ## 获取跨服战场副本当前是否开放的时间点
    #  @return: None-当前未开放;
    #  @return: hour, minute  -  当前开放中的时间时分,可进入
    gameWorld = GameWorld.GetGameWorld()
    hmNum = GetCrossBattlefieldState()
    if not hmNum:
        return
    seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
    if seasonState != 1:
        return
    openHour, openMinute = GetHMByNum(hmNum)
    sysOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 1)
    isCallBattle = [openHour, openMinute] not in sysOpenHMList
    if isCallBattle:
        if zoneID not in PyGameData.g_crossBattlefieldBuyInfo:
            GameWorld.DebugLog("该分区没有使用召集令! zoneID=%s" % zoneID)
            return
        buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo[zoneID]
        if hmNum not in buyHMInfo:
            GameWorld.DebugLog("该时段还未使用召集令! zoneID=%s,hmNum=%s" % (zoneID, hmNum))
            return
    return isCallBattle, openHour, openMinute
def GetCallPlayerCopymapObj(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, includeOffline, tick):
    ## 获取玩家召集令队伍对应的副本分线
    ## @return tagCopyMapObj
    hmNum = GetCrossBattlefieldState()
    if not hmNum:
        return
    if mapID not in PyGameData.g_crossDynamicLineInfo:
        PyGameData.g_crossDynamicLineInfo[mapID] = {}
    zoneLineDict = PyGameData.g_crossDynamicLineInfo[mapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...}
    zoneLineKey = (zoneID, funcLineID)
    if zoneLineKey not in zoneLineDict:
        zoneLineDict[zoneLineKey] = []
    funcLineObjList = zoneLineDict[zoneLineKey]
    if not funcLineObjList:
        return
    # 召集令场次,每个分区固定只有一场
    for _, funcLineObj in enumerate(funcLineObjList, 1):
        realMapID, copyMapID = funcLineObj.realMapID, funcLineObj.copyMapID
        if not realMapID:
            continue
        key = (realMapID, copyMapID)
        if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
            continue
        copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
        if copyMapObj.zoneID != zoneID:
            continue
        # 召集令成员不受人数限制
        buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo.get(zoneID, {})
        if hmNum not in buyHMInfo:
            continue
        buyPlayerInfo = buyHMInfo[hmNum]
        for buyRec in buyPlayerInfo.values():
            if playerID in buyRec.callPlayerIDList:
                GameWorld.DebugLog("召集令成员不受人数限制,可进入! playerID=%s" % playerID)
                return copyMapObj
        canEnter = copyMapObj.OnRequestEnterCrossCopyMap(playerID, tick, copyMapPlayerMax, includeOffline)
        if canEnter:
            return copyMapObj
    return
def ClientServerMsg_BattlefieldBuyOpen(serverGroupID, msgData):
    if not GameWorld.IsCrossServer():
        return
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    gameWorld = GameWorld.GetGameWorld()
    seasonState = gameWorld.GetDictByKey(ChConfig.Def_WorldKey_CrossPKZoneSeasonState % zoneID)
    if seasonState != 1:
        GameWorld.Log("跨服PK赛季未开启中,跨服战场也不能开启,无法购买!")
        return
    playerID = msgData["playerID"] # 角色ID
    playerName = msgData["playerName"] # 玩家名
    job = msgData["playerJob"] # ְҵ
    playerLV = msgData["playerLV"] # ְҵ
    realmLV = msgData["realmLV"] # 境界
    fightPower = msgData["fightPower"] # 战斗力
    buyOpenCountWeek = msgData["buyOpenCountWeek"] # 本周已购买召集场次
    openHour = msgData["openHour"]
    openMinute = msgData["openMinute"]
    faction = msgData["faction"]
    hmNum = GetHMNum(openHour, openMinute)
    if zoneID not in PyGameData.g_crossBattlefieldBuyInfo:
        PyGameData.g_crossBattlefieldBuyInfo[zoneID] = {}
    buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo[zoneID]
    if hmNum not in buyHMInfo:
        buyHMInfo[hmNum] = {}
    buyPlayerInfo = buyHMInfo[hmNum]
    if playerID in buyPlayerInfo:
        GameWorld.Log("玩家已经购买过该召集场次! zoneID=%s,openHour=%s,openMinute=%s" % (zoneID, openHour, openMinute), playerID)
        return
    curFactionCount, othFactionCount = 0, 0
    for callPlayerID, buyRec in buyPlayerInfo.items():
        if buyRec.factionID == faction:
            curFactionCount += 1
        else:
            othFactionCount += 1
        if playerID in buyRec.callPlayerIDList:
            GameWorld.Log("玩家已经在该召集场次阵营里! zoneID=%s,openHour=%s,openMinute=%s,callPlayerID=%s,callPlayerIDList=%s"
                          % (zoneID, openHour, openMinute, callPlayerID, buyRec.callPlayerIDList), playerID)
            return
    if curFactionCount > othFactionCount:
        GameWorld.Log("阵营平衡限制,不可再购买该跨服战场阵营! zoneID=%s,openHour=%s,openMinute=%s,faction=%s,curFactionCount(%s) > othFactionCount(%s)"
                      % (zoneID, openHour, openMinute, faction, curFactionCount, othFactionCount), playerID)
        return
    # ================ 可以购买,以下执行添加购买场次阵营逻辑  ================
    # 更新缓存
    curCache = PlayerViewCache.FindViewCache(playerID, True)
    if curCache:
        cacheDict = PlayerViewCache.GetCachePropDataDict(curCache)
        cacheDict["Name"] = playerName
        cacheDict["Job"] = job
        cacheDict["LV"] = playerLV
        cacheDict["RealmLV"] = realmLV
        cacheDict["FightPower"] = fightPower
    # 新增场次购买记录
    buyTime = int(time.time())
    buyRec = CrossBattlefieldBuy()
    buyRec.buyTime = buyTime
    buyRec.zoneID = zoneID
    buyRec.hmNum = hmNum
    buyRec.playerID = playerID
    buyRec.factionID = faction
    buyRec.callPlayerIDList = [playerID]
    buyPlayerInfo[playerID] = buyRec
    # 上榜
    billboardCallCountLimit = IpyGameDataPY.GetFuncCfg("CrossBattlefieldBillboard", 2) # 周召集榜上榜至少次数
    groupValue1, dataID, name1, name2 = zoneID, playerID, playerName, ""
    type2, value1, value2 = job, realmLV, 0
    cmpValue = buyOpenCountWeek + 1
    if cmpValue >= billboardCallCountLimit:
        CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_BattlefieldWCall, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue)
    SyncMapServerCrossBattlefieldBuyInfo()
    # 通知子服
    serverGroupIDList = zoneIpyData.GetServerGroupIDList()
    msgData.update({"opType":"BuyOpen", "buyTime":buyTime})
    Send_CrossServerMsg_BattlefieldBuy(zoneID, serverGroupIDList, msgData)
    return
def ClientServerMsg_BattlefieldCallJoin(serverGroupID, msgData):
    # "openHour":openHour, "openMinute":openMinute, "buyPlayerID":buyPlayerID, "tagPlayerID":tagPlayerID, "playerID":playerID
    openHour = msgData["openHour"]
    openMinute = msgData["openMinute"]
    buyPlayerID = msgData["buyPlayerID"]
    tagPlayerID = msgData["tagPlayerID"]
    playerID = msgData["playerID"]
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    hmNum = GetHMNum(openHour, openMinute)
    buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
    if buyPlayerID not in buyPlayerInfo:
        GameWorld.ErrLog("跨服战场不存在该玩家的召集队伍! hmNum=%s,buyPlayerID=%s" % (hmNum, buyPlayerID), playerID)
        return
    buyRec = buyPlayerInfo[buyPlayerID]
    callTeamMemMax = IpyGameDataPY.GetFuncCfg("CrossBattlefieldCall", 1)
    if len(buyRec.callPlayerIDList) >= callTeamMemMax:
        GameWorld.ErrLog("跨服战场召集人数已满! hmNum=%s,buyPlayerID=%s,callPlayerIDList=%s" % (hmNum, buyPlayerID, buyRec.callPlayerIDList), playerID)
        return
    if tagPlayerID not in buyRec.callPlayerIDList:
        buyRec.callPlayerIDList.append(tagPlayerID)
    SyncMapServerCrossBattlefieldBuyInfo()
    serverGroupIDList = zoneIpyData.GetServerGroupIDList()
    msgData.update({"opType":"CallJoin"})
    Send_CrossServerMsg_BattlefieldBuy(zoneID, serverGroupIDList, msgData)
    return
def ClientServerMsg_BattlefieldCallKick(serverGroupID, msgData):
    # "openHour":openHour, "openMinute":openMinute, "buyPlayerID":buyPlayerID, "tagPlayerID":tagPlayerID, "playerID":playerID
    openHour = msgData["openHour"]
    openMinute = msgData["openMinute"]
    buyPlayerID = msgData["buyPlayerID"]
    tagPlayerID = msgData["tagPlayerID"]
    playerID = msgData["playerID"]
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    hmNum = GetHMNum(openHour, openMinute)
    buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
    if buyPlayerID not in buyPlayerInfo:
        GameWorld.ErrLog("跨服战场不存在该玩家的召集队伍! hmNum=%s,buyPlayerID=%s" % (hmNum, buyPlayerID), playerID)
        return
    buyRec = buyPlayerInfo[buyPlayerID]
    if tagPlayerID not in buyRec.callPlayerIDList:
        GameWorld.ErrLog("跨服战场召集队伍没有该玩家! hmNum=%s,buyPlayerID=%s,tagPlayerID=%s not in callPlayerIDList=%s"
                           % (hmNum, buyPlayerID, tagPlayerID, buyRec.callPlayerIDList), playerID)
        return
    buyRec.callPlayerIDList.remove(tagPlayerID)
    SyncMapServerCrossBattlefieldBuyInfo()
    serverGroupIDList = zoneIpyData.GetServerGroupIDList()
    msgData.update({"opType":"CallKick"})
    Send_CrossServerMsg_BattlefieldBuy(zoneID, serverGroupIDList, msgData)
    return
def MapServer_CrossBattlefieldOver(msgList):
    ## 跨服战场地图结算
    overTime = int(time.time())
    hmNum = GetCrossBattlefieldState()
    fbPropertyID, zoneID, funcLineID, winnerFaction, superItemInfo, superItemPlayerID, superItemPlayerName, scoreKingID, scoreKingName, battlePlayerList = msgList
    GameWorld.Log("跨服战场地图同步结果: hmNum=%s,zoneID=%s,funcLineID=%s,winnerFaction=%s,superItemInfo=%s,superItemPlayerID=%s,scoreKingID=%s,battlePlayerCount=%s"
                  % (hmNum, zoneID, funcLineID, winnerFaction, superItemInfo, superItemPlayerID, scoreKingID, len(battlePlayerList)), fbPropertyID)
    winnerOrderAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAward", 2, {}) # 胜利方名次对应奖励物品列表 {"名次":[[物品ID,个数,是否拍品], ...], ...} , 名次配置支持段配置
    loserOrderAwardDict = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAward", 3, {}) # 失败方名次对应奖励物品列表 {"名次":[[物品ID,个数,是否拍品], ...], ...} , 名次配置支持段配置
    winnerAwardList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAward", 4) # 胜利方固定结算奖励列表 [[物品ID,个数,是否拍品], ...]
    loserAwardList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAward", 5) # 失败方固定结算奖励列表 [[物品ID,个数,是否拍品], ...]
    winnerOrderIntAwardDict = {int(k):v for k, v in winnerOrderAwardDict.items()}
    loserOrderIntAwardDict = {int(k):v for k, v in loserOrderAwardDict.items()}
    billboardEnterCountLimit = IpyGameDataPY.GetFuncCfg("CrossBattlefieldBillboard", 1) # 周参与榜上榜至少次数
    syncPlayerDataInfo = {}
    winnerPlayerIDList, loserPlayerIDList = [], []
    for playerInfo in battlePlayerList:
        faction, rank, playerID, job, realmLV, name, score, highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter = playerInfo
        isWinner = 0
        paramList = [rank]
        if faction == winnerFaction:
            isWinner = 1
            winnerPlayerIDList.append(playerID)
            orderAwardMailKey = "CrossBattlefieldOrderWin"
            orderAwardItemList = GameWorld.GetOrderValueByDict(winnerOrderIntAwardDict, rank)
            GameWorld.Log("    获胜阵营玩家: faction=%s,rank=%s,playerID=%s" % (faction, rank, playerID), fbPropertyID)
        else:
            loserPlayerIDList.append(playerID)
            orderAwardMailKey = "CrossBattlefieldOrderLose"
            orderAwardItemList = GameWorld.GetOrderValueByDict(loserOrderIntAwardDict, rank)
            GameWorld.Log("    失败阵营玩家: faction=%s,rank=%s,playerID=%s" % (faction, rank, playerID), fbPropertyID)
        # 排名奖励邮件
        PlayerCompensation.SendMailByKey(orderAwardMailKey, [playerID], orderAwardItemList, paramList, crossMail=True)
        # 更新周参与榜单
        groupValue1, dataID, name1, name2 = zoneID, playerID, name, ""
        type2, value1, value2 = job, realmLV, 0
        enterCountWeek += 1
        cmpValue = enterCountWeek
        if cmpValue >= billboardEnterCountLimit:
            CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_BattlefieldWJoin, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue)
        # 更新周高分榜单
        if score > highScoreToday:
            highScoreWeekTotal += (score - highScoreToday)
            highScoreToday = score
            cmpValue = highScoreWeekTotal
            CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_BattlefieldWScore, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue)
        GameWorld.Log("    战场阵营玩家: faction=%s,isWinner=%s,rank=%s,playerID=%s,score=%s,highScoreToday=%s,highScoreWeekTotal=%s,enterCountWeek=%s,isCallEnter=%s"
                      % (faction, isWinner, rank, playerID, score, highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter), fbPropertyID)
        syncPlayerDataInfo[playerID] = [highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter]
    # 参与奖励邮件
    if winnerPlayerIDList:
        PlayerCompensation.SendMailByKey("CrossBattlefieldJoinWin", winnerPlayerIDList, winnerAwardList, crossMail=True)
    if loserPlayerIDList:
        PlayerCompensation.SendMailByKey("CrossBattlefieldJoinLose", loserPlayerIDList, loserAwardList, crossMail=True)
    # 大奖获得者邮件
    superItemID, superItemCount = 0, 0
    if superItemPlayerID and superItemInfo and len(superItemInfo) == 3:
        superItemID, superItemCount = superItemInfo[0], superItemInfo[1]
        PlayerCompensation.SendMailByKey("CrossBattlefieldSuperAward", [superItemPlayerID], [superItemInfo], crossMail=True)
    crossZoneName = GameWorld.GetCrossZoneName()
    zoneIpyData = IpyGameDataPY.GetIpyGameData("CrossZonePK", crossZoneName, zoneID)
    serverGroupIDList = zoneIpyData.GetServerGroupIDList() if zoneIpyData else []
    # 通知子服更新参与玩家数据
    sendMsg = {"zoneID":zoneID, "overTime":overTime, "syncPlayerDataInfo":syncPlayerDataInfo}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_BattlefieldOver, sendMsg, serverGroupIDList)
    # 结算广播
    nextBattleTimeStr = ""
    openHour, openMinute = GetHMByNum(hmNum)
    sysOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 1)
    callOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 2)
    allOpenHMList = sysOpenHMList + callOpenHMList
    allOpenHMList.sort()
    if [openHour, openMinute] in allOpenHMList:
        nextOpenIndex = allOpenHMList.index([openHour, openMinute]) + 1
        nextOpenHour, nextOpenMinute = allOpenHMList[nextOpenIndex] if len(allOpenHMList) > nextOpenIndex else allOpenHMList[0]
        nextBattleTimeStr = "%02d:%02d" % (nextOpenHour, nextOpenMinute)
    # 本分区全服:XX阵营胜利,xxx为本场积分王,xxx获得了古神大奖XXX,下个场次预计将在XX点开放。
    if battlePlayerList:
        msgParamList = [winnerFaction, scoreKingName, superItemPlayerName, superItemID, superItemCount, nextBattleTimeStr]
        PlayerControl.WorldNotifyCross(serverGroupIDList, 0, "CrossBattlefieldOver", msgParamList)
    return
####################################################################################################
def CrossServerMsg_BattlefieldState(msgData):
    battlefieldState = msgData["battlefieldState"]
    gameWorld = GameWorld.GetGameWorld()
    seasonState = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKSeasonState)
    beforeState = GetCrossBattlefieldState()
    beforeState = beforeState if (beforeState and seasonState == 1) else 0
    realBattlefieldState = battlefieldState if (battlefieldState and seasonState == 1) else 0
    GameWorld.DebugLog("收到跨服服务器同步的战场状态: battlefieldState=%s,seasonState=%s,beforeState=%s,realBattlefieldState=%s"
                       % (battlefieldState, seasonState, beforeState, realBattlefieldState))
    key = ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield
    gameWorld.SetDict(key, realBattlefieldState)
    GameWorld.SendMapServerMsgEx(key, realBattlefieldState)
    return
def CrossServerMsg_BattlefieldOver(msgData):
    GameWorld.DebugLog("收到跨服服务器同步的战场结算信息: %s" % msgData)
    zoneID = msgData["zoneID"]
    overTime = msgData["overTime"]
    syncPlayerDataInfo = msgData["syncPlayerDataInfo"]
    gameWorld = GameWorld.GetGameWorld()
    pkZoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    if zoneID != pkZoneID:
        GameWorld.ErrLog("非本服所属分区的跨服战场购买信息! pkZoneID(%s) != zoneID(%s) %s" % (pkZoneID, zoneID, str(msgData)))
        return
    for playerID, playerData in syncPlayerDataInfo.items():
        highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter = playerData
        if PlayerControl.GetDBPlayerAccIDByID(playerID):
            msgInfo = ["BattlefieldOver", [overTime, highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter]]
            CrossRealmPlayer.MapServer_QueryCrossPlayerResult(playerID, "CrossBattlefield", msgInfo)
    return
def CrossServerMsg_BattlefieldBuy(msgData):
    GameWorld.DebugLog("收到跨服服务器同步的战场购买信息: %s" % msgData)
    zoneID = msgData["zoneID"]
    syncBuyHMInfo = msgData["syncBuyHMInfo"]
    opData = msgData.get("opData", {})
    gameWorld = GameWorld.GetGameWorld()
    pkZoneID = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossPKZoneID)
    if zoneID != pkZoneID:
        GameWorld.ErrLog("非本服所属分区的跨服战场购买信息! pkZoneID(%s) != zoneID(%s) %s" % (pkZoneID, zoneID, str(msgData)))
        return
    if not syncBuyHMInfo:
        PyGameData.g_crossBattlefieldBuyInfo.pop(zoneID, None)
    # 更新数据
    for hmNum, syncBuyPlayerInfo in syncBuyHMInfo.items():
        buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
        for buyPlayerID, attrDict in syncBuyPlayerInfo.items():
            buyRec = CrossBattlefieldBuy()
            buyRec.SetAttr(attrDict)
            buyPlayerInfo[buyPlayerID] = buyRec
    SyncMapServerCrossBattlefieldBuyInfo()
    if not opData or "opType" not in opData:
        SyncCrossBattlefieldBuyInfo(None, zoneID)
        return
    opType = opData["opType"]
    openHour = opData["openHour"]
    openMinute = opData["openMinute"]
    hmNum = GetHMNum(openHour, openMinute)
    SyncCrossBattlefieldBuyInfo(None, zoneID, hmNum)
    if opType == "BuyOpen":
        playerID = opData["playerID"]
        playerName = opData["playerName"]
        PlayerControl.WorldNotify(0, "CrossBattlefieldBuyOpen", [playerName, "%d:%02d" % (openHour, openMinute)])
        if PlayerControl.GetDBPlayerAccIDByID(playerID):
            faction = opData["faction"]
            buyTime = opData["buyTime"]
            todayBuyOpenCount = opData["todayBuyOpenCount"]
            msgInfo = ["BattlefieldBuy", [openHour, openMinute, faction, todayBuyOpenCount, buyTime]]
            CrossRealmPlayer.MapServer_QueryCrossPlayerResult(playerID, "CrossBattlefield", msgInfo)
    elif opType == "CallJoin":
        pass
    elif opType == "CallKick":
        pass
    return
def SyncCrossBattlefieldBuyInfo(curPlayer, zoneID, hmNum=None):
    ## 通知战场召集场次购买信息
    buyHMInfo = PyGameData.g_crossBattlefieldBuyInfo.get(zoneID, {})
    hmNumList = [hmNum] if hmNum != None else buyHMInfo.keys()
    clientPack = ChPyNetSendPack.tagGCCrossBattlefieldBuyInfo()
    clientPack.HMBuyList = []
    for hmNum in hmNumList:
        h, m = GetHMByNum(hmNum)
        hmPack = ChPyNetSendPack.tagGCCrossBattlefieldBuyHM()
        hmPack.Hour = h
        hmPack.Minute = m
        hmPack.BuyPlayerList = []
        buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
        for buyPlayerID, buyRec in buyPlayerInfo.items():
            buyPlayerPack = ChPyNetSendPack.tagGCCrossBattlefieldBuyPlayer()
            buyPlayerPack.BuyPlayerID = buyPlayerID
            buyPlayerPack.Faction = buyRec.factionID
            buyPlayerPack.FactionPlayerList = []
            for callPlayerID in buyRec.callPlayerIDList:
                playerPack = ChPyNetSendPack.tagGCCrossBattlefieldPlayer()
                playerPack.PlayerID = callPlayerID
                if callPlayerID in buyRec.callPlayerDict:
                    factionPlayerInfo = buyRec.callPlayerDict[callPlayerID]
                    fightPower = factionPlayerInfo["FightPower"]
                    playerPack.PlayerName = factionPlayerInfo["Name"]
                    playerPack.Job = factionPlayerInfo["Job"]
                    playerPack.LV = factionPlayerInfo["LV"]
                    playerPack.RealmLV = factionPlayerInfo["RealmLV"]
                    playerPack.FightPower = fightPower % ShareDefine.Def_PerPointValue
                    playerPack.FightPowerEx = fightPower / ShareDefine.Def_PerPointValue
                buyPlayerPack.FactionPlayerList.append(playerPack)
            buyPlayerPack.FactionPlayerCount = len(buyPlayerPack.FactionPlayerList)
            hmPack.BuyPlayerList.append(buyPlayerPack)
        hmPack.BuyPlayerCount = len(hmPack.BuyPlayerList)
        clientPack.HMBuyList.append(hmPack)
    clientPack.HMCount = len(clientPack.HMBuyList)
    if curPlayer:
        NetPackCommon.SendFakePack(curPlayer, clientPack)
    else:
        # 广播全服玩家
        playerManager = GameWorld.GetPlayerManager()
        for i in xrange(playerManager.GetPlayerCount()):
            curPlayer = playerManager.GetPlayerByIndex(i)
            if curPlayer == None or not curPlayer.GetInitOK() or PlayerControl.GetIsTJG(curPlayer):
                continue
            NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
#// C0 07 跨服战场加入召集场次 #tagCGCrossBattlefieldJoinByCall
#
#struct    tagCGCrossBattlefieldJoinByCall
#{
#    tagHead        Head;
#    BYTE    Hour;        //战场开启时
#    BYTE    Minute;        //战场开启分
#    DWORD    BuyPlayerID;    //加入目标玩家的召集队伍,即购买召集场的玩家ID
#};
def OnCrossBattlefieldJoinByCall(index, clientData, tick):
    if GameWorld.IsCrossServer():
        return
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    openHour = clientData.Hour
    openMinute = clientData.Minute
    buyPlayerID = clientData.BuyPlayerID
    tagPlayerID = playerID
    closeBuyMinute = IpyGameDataPY.GetFuncCfg("CrossBattlefieldOpen", 4) # 开启前X分钟后关闭购买
    crossServerTimeStr = GameWorld.GetCrossServerTimeStr()
    crossServerDateTime = GameWorld.ChangeStrToDatetime(crossServerTimeStr)
    startTimeStr = "%s-%s-%s %s:%s:00" % (crossServerDateTime.year, crossServerDateTime.month, crossServerDateTime.day, openHour, openMinute)
    startDateTime = GameWorld.ChangeStrToDatetime(startTimeStr)
    endBuyDateTime = startDateTime + datetime.timedelta(minutes=-closeBuyMinute)
    if crossServerDateTime >= endBuyDateTime:
        GameWorld.Log("该时间点战场已关闭召集,不能再召集加入! openHour=%s,openMinute=%s,crossServerDateTime(%s) >= endBuyDateTime(%s)"
                      % (openHour, openMinute, crossServerDateTime, endBuyDateTime), playerID)
        return
    serverGroupID = GameWorld.GetServerGroupID()
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    hmNum = GetHMNum(openHour, openMinute)
    buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
    if buyPlayerID not in buyPlayerInfo:
        GameWorld.ErrLog("不存在该玩家的跨服战场召集队伍! hmNum=%s,buyPlayerID=%s" % (hmNum, buyPlayerID), playerID)
        return
    buyRec = buyPlayerInfo[buyPlayerID]
    callTeamMemMax = IpyGameDataPY.GetFuncCfg("CrossBattlefieldCall", 1)
    if len(buyRec.callPlayerIDList) >= callTeamMemMax:
        GameWorld.DebugLog("召集人数已满! hmNum=%s,buyPlayerID=%s,callPlayerIDList=%s" % (hmNum, buyPlayerID, buyRec.callPlayerIDList), playerID)
        return
    # 请求查询跨服服务器
    dataMsg = {"openHour":openHour, "openMinute":openMinute, "buyPlayerID":buyPlayerID, "tagPlayerID":tagPlayerID, "playerID":playerID}
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_BattlefieldCallJoin, dataMsg)
    return
#// C0 08 跨服战场召集场次踢人 #tagCGCrossBattlefieldCallKick
#
#struct    tagCGCrossBattlefieldCallKick
#{
#    tagHead        Head;
#    BYTE    Hour;        //战场开启时
#    BYTE    Minute;        //战场开启分
#    DWORD    TagPlayerID;    //目标玩家ID,即要被踢出去的玩家ID
#};
def OnCrossBattlefieldCallKick(index, clientData, tick):
    if GameWorld.IsCrossServer():
        return
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    openHour = clientData.Hour
    openMinute = clientData.Minute
    tagPlayerID = clientData.TagPlayerID
    buyPlayerID = playerID
    if buyPlayerID == tagPlayerID:
        return
    closeBuyMinute = IpyGameDataPY.GetFuncCfg("CrossBattlefieldOpen", 4) # 开启前X分钟后关闭购买
    crossServerTimeStr = GameWorld.GetCrossServerTimeStr()
    crossServerDateTime = GameWorld.ChangeStrToDatetime(crossServerTimeStr)
    startTimeStr = "%s-%s-%s %s:%s:00" % (crossServerDateTime.year, crossServerDateTime.month, crossServerDateTime.day, openHour, openMinute)
    startDateTime = GameWorld.ChangeStrToDatetime(startTimeStr)
    endBuyDateTime = startDateTime + datetime.timedelta(minutes= -closeBuyMinute)
    if crossServerDateTime >= endBuyDateTime:
        GameWorld.Log("该时间点战场已关闭召集,不能再召集踢人! openHour=%s,openMinute=%s,crossServerDateTime(%s) >= endBuyDateTime(%s)"
                      % (openHour, openMinute, crossServerDateTime, endBuyDateTime), playerID)
        return
    serverGroupID = GameWorld.GetServerGroupID()
    zoneIpyData = CrossRealmPK.GetCrossPKServerGroupZone(serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    hmNum = GetHMNum(openHour, openMinute)
    buyPlayerInfo = GetBuyPlayerInfo(zoneID, hmNum)
    if buyPlayerID not in buyPlayerInfo:
        GameWorld.ErrLog("不存在该玩家的跨服战场召集队伍! hmNum=%s,buyPlayerID=%s" % (hmNum, buyPlayerID), playerID)
        return
    buyRec = buyPlayerInfo[buyPlayerID]
    if tagPlayerID not in buyRec.callPlayerIDList:
        GameWorld.DebugLog("该召集队伍没有该玩家! hmNum=%s,buyPlayerID=%s,tagPlayerID=%s not in callPlayerIDList=%s"
                           % (hmNum, buyPlayerID, tagPlayerID, buyRec.callPlayerIDList), playerID)
        return
    # 请求查询跨服服务器
    dataMsg = {"openHour":openHour, "openMinute":openMinute, "buyPlayerID":buyPlayerID, "tagPlayerID":tagPlayerID, "playerID":playerID}
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_BattlefieldCallKick, dataMsg)
    return
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py
@@ -282,10 +282,14 @@
    SyncCrossBillboardToClientServer(billboardType, groupValue1, groupValue2, [serverGroupID], queryData)
    return
def SyncCrossBillboardToClientServer(billboardType, groupValue1, groupValue2, serverGroupIDList=[], queryData={}):
def SyncCrossBillboardToClientServer(billboardType, groupValue1, groupValue2, serverGroupIDList=None, queryData=None):
    ## 同步跨服榜单到子服
    if not GameWorld.IsCrossServer():
        return
    if serverGroupIDList == None:
        serverGroupIDList = []
    if queryData == None:
        queryData = {}
    billboardMgr = PyDataManager.GetCrossBillboardManager()
    billboardObj = billboardMgr.GetCrossBillboard(billboardType, groupValue1, groupValue2)
    crossServerDataVer = billboardObj.GetCrossServerDataVer()
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
@@ -23,6 +23,7 @@
import CrossRealmPlayer
import PlayerCompensation
import CrossActionControl
import CrossBattlefield
import CrossBillboard
import GameWorldBoss
import CrossRealmPK
@@ -120,6 +121,15 @@
        elif msgType == ShareDefine.ClientServerMsg_AddBuff:
            MapServer_CrossAddBuff(msgData)
            
        elif msgType == ShareDefine.ClientServerMsg_BattlefieldBuyOpen:
            CrossBattlefield.ClientServerMsg_BattlefieldBuyOpen(serverGroupID, msgData)
        elif msgType == ShareDefine.ClientServerMsg_BattlefieldCallJoin:
            CrossBattlefield.ClientServerMsg_BattlefieldCallJoin(serverGroupID, msgData)
        elif msgType == ShareDefine.ClientServerMsg_BattlefieldCallKick:
            CrossBattlefield.ClientServerMsg_BattlefieldCallKick(serverGroupID, msgData)
        # 需要发送到地图服务器处理的
        elif msgType in [ShareDefine.ClientServerMsg_Reborn, ShareDefine.ClientServerMsg_CollectNPC]:
            MapServer_CrossServerReceiveMsg(msgType, msgData, serverGroupID)
@@ -149,6 +159,7 @@
    '''
    CrossRealmPlayer.Sync_CrossCommInitDataToClientServer(serverGroupID)
    CrossRealmPK.Sync_CrossPKInitDataToClientServer(tick, serverGroupID)
    CrossBattlefield.Sync_CrossBattlefieldDataToClientServer(serverGroupID)
    CrossBoss.Sync_CrossBossInitDataToClientServer(serverGroupID)
    CrossActionControl.Sync_CrossActInfoToClientServer(serverGroupID)
    CrossLuckyCloudBuy.Sync_LuckyCloudBuyDataToClientServer(tick, serverGroupID)
@@ -195,7 +206,7 @@
    return
## ================================================================================================
def SendMsgToClientServer(msgType, dataMsg, serverGroupIDList=[]):
def SendMsgToClientServer(msgType, dataMsg, serverGroupIDList=None):
    ''' 广播信息到子服务器上
        @param serverGroupIDList: 发送指定的服务器组ID列表,内部已经针对列表中组ID去重,
        所以外部逻辑可直接添加,不用考虑组ID重复问题,没有指定服务器组ID时,默认广播所有子服
@@ -204,7 +215,9 @@
        return
    if not dataMsg:
        return
    if serverGroupIDList == None:
        serverGroupIDList = []
    srcMsg = {"MsgType":msgType, "Data":dataMsg, "CrossServerTime":GameWorld.GetCurrentDataTimeStr()}
    sendMsg = cPickle.dumps(srcMsg, 2)
    if not GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_GameWorldInitOK):
@@ -289,6 +302,15 @@
        elif msgType == ShareDefine.CrossServerMsg_LuckyCloudBuyNum:
            CrossLuckyCloudBuy.CrossServerMsg_LuckyCloudBuyNum(msgData)
            
        elif msgType == ShareDefine.CrossServerMsg_BattlefieldState:
            CrossBattlefield.CrossServerMsg_BattlefieldState(msgData)
        elif msgType == ShareDefine.CrossServerMsg_BattlefieldBuy:
            CrossBattlefield.CrossServerMsg_BattlefieldBuy(msgData)
        elif msgType == ShareDefine.CrossServerMsg_BattlefieldOver:
            CrossBattlefield.CrossServerMsg_BattlefieldOver(msgData)
        elif msgType == ShareDefine.CrossServerMsg_SyncBillboard:
            CrossBillboard.CrossServerMsg_SyncBillboard(msgData, tick)
            
@@ -342,7 +364,10 @@
                tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
                if tagPlayer:
                    GameWorld.DebugAnswer(tagPlayer, "跨服服务器时间: %s" % GameWorld.GetCrossServerTimeStr())
        elif msgType == ShareDefine.CrossServerMsg_DebugAnswer:
            GameWorld.CrossServerMsg_DebugAnswer(msgData)
        else:
            GameWorld.ErrLog("没有该信息类型逻辑处理!")
            
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldActionControl.py
@@ -975,6 +975,10 @@
        dailyIpyData = ipyDataMgr.GetDailyActionByIndex(i)
        dailyID = dailyIpyData.GetDailyID()
        
        if dailyID in [ShareDefine.DailyActionID_CrossBattlefield]:
            GameWorld.Log("    不需要处理的日常活动! dailyID=%s" % dailyID)
            continue
        # 是当天开服天定制活动的不处理常规活动
        if dailyID in customDailyIDList:
            GameWorld.Log("    常规活动ID配置是今天的定制活动ID,不处理!: dailyID=%s" % dailyID)
ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
@@ -428,6 +428,40 @@
    PyGameData.g_crossSetPlayerAttr = {}
    return
def OnPlayerLogin(curPlayer):
    if not IsCrossServerOpen():
        return
    LoginDoUnNotifyCrossMsg(curPlayer)
    return
def MapServer_QueryCrossPlayerResult(playerID, callName, msgInfo):
    ## 同步地图跨服玩家处理信息,玩家可能不在线,缓存后等玩家上线处理,暂不考虑存档问题,服务器维护后未处理的命令将失效
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if curPlayer:
        msgInfo = str(msgInfo)
        curPlayer.MapServer_QueryPlayerResult(0, 0, callName, msgInfo, len(msgInfo))
    else:
        # 缓存起来,等上线后处理
        if playerID not in PyGameData.g_unNotifyPlayerCrossMsgDict:
            PyGameData.g_unNotifyPlayerCrossMsgDict[playerID] = []
        msgList = PyGameData.g_unNotifyPlayerCrossMsgDict[playerID]
        msgList.append([callName, msgInfo])
        GameWorld.Log("玩家不在线,添加未通知的跨服命令: %s, msgInfo=%s" % (callName, msgInfo), playerID)
    return
def LoginDoUnNotifyCrossMsg(curPlayer):
    playerID = curPlayer.GetPlayerID()
    msgList = PyGameData.g_unNotifyPlayerCrossMsgDict.pop(playerID, [])
    if not msgList:
        return
    for callName, msgInfo in msgList:
        GameWorld.Log("上线处理未通知的跨服命令: %s, msgInfo=%s" % (callName, msgInfo), playerID)
        msgInfo = str(msgInfo)
        curPlayer.MapServer_QueryPlayerResult(0, 0, callName, msgInfo, len(msgInfo))
    return
    
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerCompensation.py
@@ -309,7 +309,7 @@
    
def SendPersonalItemMailBatch(batchMailInfoList):
    ## 批量发送邮件
    mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource = batchMailInfoList
    mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource, crossMail = batchMailInfoList
    
    lenPlayerID = len(batchPlayerIDList)
    lenItem = len(batchAddItemList)
@@ -334,7 +334,7 @@
        silver = batchSilver[i] if lenSilver == lenPlayerID else 0
        detail = batchDetail[i] if lenDetail == lenPlayerID else ""
        content = "<MailTemplate>%s</MailTemplate>%s" % (mailTypeKey, str(paramList))
        SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource)
        SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource, crossMail=crossMail)
        
    return
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerEventCounter.py
@@ -32,6 +32,7 @@
import NetPackCommon
import PlayerDuJie
import PlayerCharm
import CrossBattlefield
#---------------------------------------------------------------------
#---------------------------------------------------------------------
@@ -87,6 +88,8 @@
    PlayerFairyDomain.OnDayEx()
    #竞技场
    GameWorldArena.OnDayEx()
    #跨服战场
    CrossBattlefield.DoOnDayEx()
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
@@ -126,6 +129,8 @@
    
    # 竞技场
    GameWorldArena.OnWeekEx()
    # 跨服战场
    CrossBattlefield.DoOnWeekEx()
    
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -31,19 +31,26 @@
import GameWorld
import ChConfig
import IPY_PlayerDefine
import CrossBattlefield
import CrossRealmPlayer
import CrossRealmMsg
import ShareDefine
import CrossBoss
import time
DynamicShuntType_No = 0 # 不分流
DynamicShuntType_Fill = 1    # 填满式分流,按存在的线路人数多的优先填充,都满后开启新线路
DynamicShuntType_Equally = 2 # 均摊式分流,按存在的线路人数少的优先填充,都满后开启新线路
#---------------------------------------------------------------------
## 跨服地图动态分配的功能线路,如果有人数上限的,则同分区同地图玩法的可能同时存在多个相同功能线路的数据
##  {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...}
class CrossFuncLineInfo():
    
    def __init__(self):
        self.realMapID = 0
        self.copyMapID = 0
        self.newFuncLineNum = 0
        self.funcLineDataCache = None # 功能线路自定义缓存数据
        return
    
@@ -52,37 +59,48 @@
        self.copyMapID = 0
        return
## 跨服地图动态分配的虚拟线路信息
## 跨服地图动态分配的虚拟线路信息 {(mapID, copyMapID):CrossCopyMapInfo, ...}
class CrossCopyMapInfo():
    
    def __init__(self, zoneID, funcLineID):
        self.zoneID = zoneID
        self.funcLineID = funcLineID
        self.newFuncLineNum = 0
        self.realMapID = 0
        self.copyMapID = 0
        self.openState = IPY_PlayerDefine.fbosClosed
        self.fbPlayerDict = {} # 副本中的玩家信息 {playerID:serverGroupID, ...}
        self.waitPlayerDict = {} # 等待进入的玩家信息 {playerID:[serverGroupID, tick], ...}
        self.offlinePlayerDict = {} # 掉线的玩家信息,非主动退出的 {playerID:serverGroupID, ...}
        self.enterPlayerIDList = [] # 有进入过此分线的玩家ID列表,不会清除 [playerID, ...]
        return
    
    def OnRequestEnterCrossCopyMap(self, playerID, serverGroupID, tick, copyMapPlayerMax):
        # 已经在请求队列里,可进入
        if playerID in self.waitPlayerDict or not copyMapPlayerMax:
            self.waitPlayerDict[playerID] = [serverGroupID, tick]
            return True
    def GetCopyMapPlayerCount(self, includeOffline, tick):
        ## 获取该分线玩家数
        # @param includeOffline: 是否包含离线玩家
        
        # 移除请求进入超时的玩家
        for waitPlayerID, playerInfo in self.waitPlayerDict.items():
            serverGroupID, requestTick = playerInfo
            _, requestTick = playerInfo
            if tick - requestTick > 60000: # 请求进入时间保留1分钟
                self.waitPlayerDict.pop(waitPlayerID)
                
        # 判断是否超过人数上限
        fbPlayerCount, waitPlayerCount = len(self.fbPlayerDict), len(self.waitPlayerDict)
        if fbPlayerCount + waitPlayerCount >= copyMapPlayerMax:
            return False
        totalPlayerCount = fbPlayerCount + waitPlayerCount
        if includeOffline:
            totalPlayerCount += len(self.offlinePlayerDict)
        return totalPlayerCount
    def IsMustCopyMapPlayer(self, playerID):
        ## 是否必定在此分线的玩家, 在请求队列里 或 曾经进入到该分线的,都强制认为属于该分线的玩家
        return playerID in self.waitPlayerDict or playerID in self.enterPlayerIDList
        
        self.waitPlayerDict[playerID] = [serverGroupID, tick]
        return True
    def OnRequestEnterCrossCopyMap(self, playerID, tick, copyMapPlayerMax, includeOffline):
        if not copyMapPlayerMax or self.IsMustCopyMapPlayer(playerID):
            return True
        return self.GetCopyMapPlayerCount(includeOffline, tick) < copyMapPlayerMax
    
#---------------------------------------------------------------------
def GetFBLineIpyData(mapID, lineID, isDefaultLine=True):
@@ -127,37 +145,127 @@
    playerID = msgData["PlayerID"]
    mapID = msgData["MapID"]
    funcLineID = msgData["FuncLineID"]
    playerLV = msgData["LV"]
    
    zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByServerGroupID(mapID, serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    
    dynamicLineMaxPlayerCountDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 2)
    copyMapPlayerMax = dynamicLineMaxPlayerCountDict.get(mapID, 0) # 0为不限制人数,默认不限制
    openHour, openMinute = None, None
    dynamicShuntType = DynamicShuntType_Fill
    includeOffline = False
    tagCopyMapObj = None
    # 基础验证是否可进入等 及 数据准备
    if mapID == ChConfig.Def_FBMapID_CrossDemonKing:
        bossID = msgData["BossID"]
        if not CrossBoss.GetCrossBossIsAliveOrCanReborn(zoneID, bossID):
            GameWorld.DebugLog("当前跨服妖王死亡状态,不可进入! serverGroupID=%s,funcLineID=%s,zoneID=%s,bossID=%s" % (serverGroupID, funcLineID, zoneID, bossID))
            GameWorld.ErrLog("当前跨服妖王死亡状态,不可进入! funcLineID=%s,zoneID=%s,bossID=%s" % (funcLineID, zoneID, bossID), playerID)
            return
        
    elif mapID in [ChConfig.Def_FBMapID_CrossGrasslandLing, ChConfig.Def_FBMapID_CrossGrasslandXian]:
        pass
    elif mapID == ChConfig.Def_FBMapID_CrossBattlefield:
        openTimeInfo = CrossBattlefield.GetCrossBattlefieldOpenTime(zoneID)
        if not openTimeInfo:
            PlayerControl.NotifyCodeCross(serverGroupID, playerID, "FBIsNotOpen")
            GameWorld.ErrLog("非活动时间或未开启! funcLineID=%s,zoneID=%s" % (funcLineID, zoneID), playerID)
            return
        dynamicShuntType = DynamicShuntType_Equally
        isCallBattle, openHour, openMinute = openTimeInfo
        if isCallBattle:
            # 召集场次默认 funcLineID 为0,不分等级,不分流
            funcLineID = 0
            dynamicShuntType = DynamicShuntType_No
            includeOffline = True
    else:
        return
    
    mapCopyLineInfo = __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, tick)
    if not mapCopyLineInfo:
    dynamicLineMaxPlayerCountDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 2)
    copyMapPlayerMin, copyMapPlayerMax = dynamicLineMaxPlayerCountDict.get(mapID, [0, 0]) # 0为不限制人数,默认不限制
    # 除个别地图外,最优先进入上次进入的未关闭分线
    if mapID not in []:
        for _, copyMapObj in PyGameData.g_crossDynamicLineCopyMapInfo.items():
            if copyMapObj.IsMustCopyMapPlayer(playerID):
                tagCopyMapObj = copyMapObj
                break
    # 如果没有进入过,则按功能看是否有特殊指定规则
    if tagCopyMapObj == None:
        if mapID == ChConfig.Def_FBMapID_CrossBattlefield:
            if isCallBattle:
                copyMapPlayerMax = IpyGameDataPY.GetFuncCfg("CrossBattlefieldCall", 2)
                tagCopyMapObj = CrossBattlefield.GetCallPlayerCopymapObj(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, includeOffline, tick)
    # 如果还没有取到对应的分流线,则按默认规则处理
    if tagCopyMapObj == None and dynamicShuntType:
        # 非特殊动态规则,走常规逻辑
        dynamicLineLVRangeDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 4)
        if mapID in dynamicLineLVRangeDict:
            lvRangeList = dynamicLineLVRangeDict[mapID]
            for lvFuncLineID, lvRange in enumerate(lvRangeList):
                if lvRange[0] <= playerLV <= lvRange[1]:
                    funcLineID = lvFuncLineID
                    copyMapPlayerMin, copyMapPlayerMax = lvRange[2], lvRange[3]
                    GameWorld.DebugLog("进入跨服地图等级自动适配功能线路ID: mapID=%s,playerLV=%s,funcLineID=%s,copyMapPlayerMin=%s,copyMapPlayerMax=%s"
                                       % (mapID, playerLV, funcLineID, copyMapPlayerMin, copyMapPlayerMax))
                    break
        shuntPlayerMax = copyMapPlayerMax
        minCountTimeDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 3) # 分流下限人数有效时间配置,单位秒,{dataMapID:秒, ...}
        if mapID in minCountTimeDict:
            playerMinTimeSet = minCountTimeDict[mapID]
            curTime = GameWorld.GetServerTime()
            if openHour == None or openMinute == None:
                GameWorld.ErrLog("副本开启时间未知! mapID=%s,funcLineID=%s,zoneID=%s" % (mapID, funcLineID, zoneID), playerID)
                return
            openDateTimeStr = "%d-%02d-%02d %02d:%02d:00" % (curTime.year, curTime.month, curTime.day, openHour, openMinute)
            openDateTime = GameWorld.ChangeStrToDatetime(openDateTimeStr)
            passTime = curTime - openDateTime
            '''
                                                在线(包含请求中)          <= 单场下限值
                                                在线(包含请求中)+ 离线 <= 单场上限值
                                                前X秒大于 单场下限值 开新一场
                                                任意时刻大于 单场上限值 必开新一场
            '''
            if passTime.seconds <= playerMinTimeSet:
                shuntPlayerMax = copyMapPlayerMin
                includeOffline = False
            else:
                shuntPlayerMax = copyMapPlayerMax
                includeOffline = True
        tagCopyMapObj = __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID,
                                                  shuntPlayerMax, copyMapPlayerMax, includeOffline, tick, dynamicShuntType)
    if not tagCopyMapObj:
        PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBFull")
        GameWorld.ErrLog("找不到可分流的副本线路! mapID=%s,funcLineID=%s,zoneID=%s" % (mapID, funcLineID, zoneID), playerID)
        return
    realMapID, copyMapID, openState = mapCopyLineInfo
    if openState != IPY_PlayerDefine.fbosOpen:
    realMapID, copyMapID, openState = tagCopyMapObj.realMapID, tagCopyMapObj.copyMapID, tagCopyMapObj.openState
    if openState == IPY_PlayerDefine.fbosWaitForClose:
        PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose")
        GameWorld.ErrLog("分流的副本线路关闭中! mapID=%s,funcLineID=%s,zoneID=%s,realMapID=%s,copyMapID=%s,openState=%s"
                         % (mapID, funcLineID, zoneID, realMapID, copyMapID, openState), playerID)
        return
    
    playerIDList = [playerID]
    retInfo = [playerIDList, mapID, realMapID, copyMapID, funcLineID]
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
    return
    tagCopyMapObj.waitPlayerDict[playerID] = [serverGroupID, tick]
    GameWorld.DebugLog("    分配进入跨服场景: realMapID=%s, copyMapID=%s, openState=%s" % (realMapID, copyMapID, openState), playerID)
    if openState == IPY_PlayerDefine.fbosOpen:
        funcLineID = tagCopyMapObj.funcLineID
        playerIDList = [playerID]
        retInfo = [playerIDList, mapID, realMapID, copyMapID, funcLineID]
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
    return tagCopyMapObj
def CrossServerMsg_EnterFBRet(msgData, tick):
    ## 收到跨服服务器动态分配的跨服副本进入信息
@@ -172,26 +280,50 @@
        
    return
def __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, tick):
def __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, shuntPlayerMax, copyMapPlayerMax, includeOffline, tick, dynamicShuntType):
    '''获取跨服分区对应动态分配的副本地图虚拟线路信息, 由于需要支持多地图分流,所以直接由GameServer管理分配
            每个功能线路支持按人数分流,超过最大人数后可开启一条相同功能线路的虚拟线路进行分流,所以同一分区同一地图的功能线路ID可能对应多条虚拟线路
        分流方式:
            DynamicShuntType_Fill = 1    # 填满式分流,按存在的线路人数多的优先填充,都满后开启新线路
            DynamicShuntType_Equally = 2 # 均摊式分流,按存在的线路人数少的优先填充,都满后开启新线路
        分流规则:
                    时间仅决定分流人数,不影响常规分配逻辑
        1. 优先分配到人数小于分流人数的场次
        2. 超过分流人数的场次依次往人数少的场次分配
        3. 当当前已开放的场次都达到人数分流人数,则开启新场次,没有空闲的场,则往未达到人数上限的场次依次分配,直到达到所有场次上限
            关于 shuntPlayerMax 的选择: 可根据副本规则制定
            如前X分钟内可设定一个小于  copyMapPlayerMax 的分流人数值快速铺满各分流场次
            当大于X分钟后则可设置 shuntPlayerMax = copyMapPlayerMax 进行饱和分流
            当所有分流场次达到  shuntPlayerMax 后,可尝试开启新分流线路,进行分流
         shuntPlayerMax < copyMapPlayerMax 的情况,如果没有办法开启新分流线路,则可继续强制根据分流类型分配线路,只要未达到 copyMapPlayerMax 人数,还是可以进入副本的
        shuntPlayerMax >= copyMapPlayerMax 的情况,如果没有办法开启新分流线路,则标识副本所有线路已达到饱和状态,不能再进入副本了
            当  shuntPlayerMax 为 0 时,达标不限制人数上限,及不分流,都在同一条线路,一般跨服副本不建议设置为0,人数太多,不合理
    @param shuntPlayerMax: 分流最大人数限制
    @param copyMapPlayerMax: 实际最大可容纳的人数限制,一般大于等于分流人数限制
    @param includeOffline: 是否包含本线路离线玩家
    @param dynamicShuntType: 分流类型,可选择  填满式分流  或 均摊式分流
    '''
    
    zoneLineKey = (zoneID, funcLineID)
    if mapID not in PyGameData.g_crossDynamicLineInfo:
        PyGameData.g_crossDynamicLineInfo[mapID] = {}
    zoneLineDict = PyGameData.g_crossDynamicLineInfo[mapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...}
    if zoneLineKey not in zoneLineDict:
        zoneLineDict[zoneLineKey] = []
    funcLineObjList = zoneLineDict[zoneLineKey]
    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})
    funcLineObjList = zoneLineDict.get(zoneLineKey, [])
    isPlayerFullMax = (shuntPlayerMax >= copyMapPlayerMax)
    
    newFuncLineNum = None
    newFuncLineObj = None
    for index, funcLineObj in enumerate(funcLineObjList, 1):
    GameWorld.DebugLog("获取动态分流线路: serverGroupID=%s,mapID=%s,funcLineID=%s,zoneID=%s,shuntPlayerMax=%s,copyMapPlayerMax=%s,includeOffline=%s,dynamicShuntType=%s"
                       % (serverGroupID, mapID, funcLineID, zoneID, shuntPlayerMax, copyMapPlayerMax, includeOffline, dynamicShuntType), playerID)
    #GameWorld.DebugLog("    funcLineObjList=%s" % funcLineObjList, playerID)
    canUseShuntLine = False # 是否直接使用分流线路,如果否的话,当人数未达到真正饱和时,则还可直接分配对应分流类型的线路
    minPlayerCount, maxPlayerCount = 0, 0
    minCopyMapObj, maxCopyMapObj = None, None
    for _, funcLineObj in enumerate(funcLineObjList, 1):
        realMapID, copyMapID = funcLineObj.realMapID, funcLineObj.copyMapID
        #GameWorld.DebugLog("    realMapID=%s, copyMapID=%s" % (realMapID, copyMapID))
        if not realMapID:
            if not newFuncLineObj:
                newFuncLineNum, newFuncLineObj = index, funcLineObj
            continue
        
        key = (realMapID, copyMapID)
@@ -203,59 +335,161 @@
        copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
        openState = copyMapObj.openState
        if openState == IPY_PlayerDefine.fbosWaitForClose:
            if not copyMapPlayerMax:
                PlayerControl.CrossNotifyCode(serverGroupID, playerID, "HazyRegionClose")
            # 没有限制分流人数的情况,代表都在同一场,这种情况下当副本已经在关闭的状态下,则代表已经结束了,不可再进入
            if not shuntPlayerMax:
                PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose")
                return
            #GameWorld.DebugLog("    虚拟线路等待关闭中! index=%s,realMapID=%s,copyMapID=%s" % (index, realMapID, copyMapID))
            #GameWorld.DebugLog("    虚拟线路等待关闭中! realMapID=%s,copyMapID=%s" % (realMapID, copyMapID))
            continue
        
        canEnter = copyMapObj.OnRequestEnterCrossCopyMap(playerID, serverGroupID, tick, copyMapPlayerMax)
        if canEnter:
            #GameWorld.DebugLog("可进入动态分布的虚拟线路! realMapID=%s,copyMapID=%s,openState=%s" % (realMapID, copyMapID, openState))
            #GameWorld.DebugLog("    副本中的玩家ID: %s" % copyMapObj.fbPlayerDict)
            #GameWorld.DebugLog("    等待中的玩家ID: %s" % copyMapObj.waitPlayerDict)
            return realMapID, copyMapID, openState
        if not shuntPlayerMax or copyMapObj.IsMustCopyMapPlayer(playerID):
            return copyMapObj
        
        playerCount = copyMapObj.GetCopyMapPlayerCount(includeOffline, tick)
        if minCopyMapObj == None or playerCount < minPlayerCount:
            minPlayerCount = playerCount
            minCopyMapObj = copyMapObj
        if maxCopyMapObj == None or playerCount > maxPlayerCount:
            maxPlayerCount = playerCount
            maxCopyMapObj = copyMapObj
        # 存在线路未达到规定的分流人数,则可直接使用分流线路
        if playerCount < shuntPlayerMax:
            canUseShuntLine = True
    #GameWorld.DebugLog("    isPlayerFullMax=%s,canUseShuntLine=%s" % (isPlayerFullMax, canUseShuntLine))
    dynamicShuntCopyMap = None # 分流类型决定的分流线路
    # 均摊式
    if dynamicShuntType == DynamicShuntType_Equally:
        dynamicShuntCopyMap = minCopyMapObj
    # 填满式
    elif dynamicShuntType == DynamicShuntType_Fill:
        dynamicShuntCopyMap = maxCopyMapObj
    else:
        return
    shuntCopyMap = None
    if canUseShuntLine:
        shuntCopyMap = dynamicShuntCopyMap
    #GameWorld.DebugLog("    shuntCopyMap=%s" % shuntCopyMap)
    if not shuntCopyMap:
        isLog = isPlayerFullMax
        shuntCopyMap = __OpenNewFuncLine(mapID, zoneID, funcLineID, isLog)
        # 即 shuntPlayerMax < copyMapPlayerMax 的情况
        if not shuntCopyMap and not isPlayerFullMax:
            shuntCopyMap = dynamicShuntCopyMap
    if not shuntCopyMap:
        return
    shuntCopyMap.waitPlayerDict[playerID] = [serverGroupID, tick]
    return shuntCopyMap
def __OpenNewFuncLine(mapID, zoneID, funcLineID, isLog=True):
    ## 新开功能线路分流
    if mapID not in PyGameData.g_crossDynamicLineInfo:
        PyGameData.g_crossDynamicLineInfo[mapID] = {}
    zoneLineDict = PyGameData.g_crossDynamicLineInfo[mapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...}
    zoneLineKey = (zoneID, funcLineID)
    if zoneLineKey not in zoneLineDict:
        zoneLineDict[zoneLineKey] = []
    funcLineObjList = zoneLineDict[zoneLineKey]
    dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1)
    dynamicMapIDList = dynamicLineMapDict.get(mapID, [mapID])
    
    openMapID, openCopyMapID = 0, 0
    for realMapID in dynamicMapIDList:
        maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0)
        for copyMapID in xrange(maxCopyMapCount):
    maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(dynamicMapIDList[0], 0)
    # 外层为虚拟线路总数遍历,内层为分流地图,这样可以均匀分流到各个分流地图,减少单地图压力
    for copyMapID in xrange(maxCopyMapCount):
        for realMapID in dynamicMapIDList:
            if copyMapID >= PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0):
                continue
            if (realMapID, copyMapID) not in PyGameData.g_crossDynamicLineCopyMapInfo:
                openMapID, openCopyMapID = realMapID, copyMapID
                break
        if openMapID:
            break
    if not openMapID:
        GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本!mapID=%s, funcLineID=%s, zoneID=%s, dynamicMapIDList=%s"
                         % (mapID, funcLineID, zoneID, dynamicMapIDList))
        if isLog:
            GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本! mapID=%s,zoneID=%s,funcLineID=%s,dynamicMapIDList=%s"
                             % (mapID, zoneID, funcLineID, dynamicMapIDList))
        return
    
    realMapID, copyMapID = openMapID, openCopyMapID
    newFuncLineObj = None
    for funcLineObj in funcLineObjList:
        if not funcLineObj.realMapID:
            newFuncLineObj = funcLineObj
            break
    if newFuncLineObj == None:
        newFuncLineObj = CrossFuncLineInfo()
        funcLineObjList.append(newFuncLineObj)
        newFuncLineNum = len(funcLineObjList)
    realMapID, copyMapID = openMapID, openCopyMapID
    newFuncLineNum = 1
    lineNumList = [lineObj.newFuncLineNum for lineObj in funcLineObjList]
    for num in xrange(1, len(lineNumList) + 1):
        if num not in lineNumList:
            newFuncLineNum = num
            break
    GameWorld.DebugLog("    lineNumList=%s,newFuncLineNum=%s" % (lineNumList, newFuncLineNum))
    newFuncLineObj.realMapID = realMapID
    newFuncLineObj.copyMapID = copyMapID
    funcLineDataCache = newFuncLineObj.funcLineDataCache
    newFuncLineObj.newFuncLineNum = newFuncLineNum
    copyMapObj = CrossCopyMapInfo(zoneID, funcLineID)
    copyMapObj.realMapID = realMapID
    copyMapObj.copyMapID = copyMapID
    copyMapObj.newFuncLineNum = newFuncLineNum
    
    key = (realMapID, copyMapID)
    copyMapObj = CrossCopyMapInfo(zoneID, funcLineID)
    PyGameData.g_crossDynamicLineCopyMapInfo[key] = copyMapObj
    copyMapObj.waitPlayerDict[playerID] = [serverGroupID, tick]
    openState = copyMapObj.openState
    
    propertyID = int("%d%03d%d" % (zoneID, funcLineID, newFuncLineNum))
    GameWorld.DebugLog("不存在该分区功能线路ID,重新分配: zoneID=%s,funcLineID=%s,realMapID=%s,copyMapID=%s,propertyID=%s"
                       % (zoneID, funcLineID, realMapID, copyMapID, propertyID))
    propertyID = int("%d%03d%02d" % (zoneID, funcLineID, newFuncLineNum))
    GameWorld.Log("    新开分区动态副本功能线路: zoneID=%s,funcLineID=%s,newFuncLineNum=%s,realMapID=%s,copyMapID=%s,propertyID=%s"
                  % (zoneID, funcLineID, newFuncLineNum, realMapID, copyMapID, propertyID))
    
    # 通知地图开启新的地图虚拟分线
    funcLineDataCache = newFuncLineObj.funcLineDataCache
    msgInfo = str([copyMapID, propertyID, funcLineDataCache])
    GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, realMapID, "OpenFB", msgInfo, len(msgInfo))
    return realMapID, copyMapID, openState
    return copyMapObj
def OpenCrossDynamicLineBySys(zoneID, mapID, funcLineIDList, checkExist):
    ## 系统开启跨服动态线路
    GameWorld.Log("    系统开启跨服动态线路: zoneID=%s, mapID=%s, funcLineIDList=%s, checkExist=%s" % (zoneID, mapID, funcLineIDList, checkExist))
    for funcLineID in funcLineIDList:
        if checkExist:
            fincLineObj = None
            zoneLineKey = (zoneID, funcLineID)
            zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})
            funcLineObjList = zoneLineDict.get(zoneLineKey, [])
            for funcLineObj in funcLineObjList:
                if funcLineObj.realMapID:
                    fincLineObj = funcLineObj
                    break
            if fincLineObj:
                GameWorld.ErrLog("已经存在开放中的线路,不重复开启动态副本线路! mapID=%s, funcLineID=%s, zoneID=%s, realMapID=%s, copyMapID=%s"
                                 % (mapID, funcLineID, zoneID, funcLineObj.realMapID, funcLineObj.copyMapID))
                continue
        __OpenNewFuncLine(mapID, zoneID, funcLineID)
    return
def GetCrossDynamicLineZoneID(mapID, realMapID, copyMapID):
    ## 获取跨服动态分配的虚拟线路对应分区ID
@@ -345,6 +579,8 @@
                funcLineObj.OnCopyMapClose()
                zoneID, funcLineID = key
                GameWorld.Log("    分区对应功能线路虚拟分线关闭: zoneID=%s,dataMapID%s,funcLineID=%s" % (zoneID, dataMapID, funcLineID))
                if not funcLineObj.funcLineDataCache:
                    funcLineObjList.remove(funcLineObj)
                break
    
    key = (mapID, copyMapID)
@@ -401,6 +637,8 @@
    copyMapObj.waitPlayerDict.pop(playerID, None)
    copyMapObj.offlinePlayerDict.pop(playerID, None)
    copyMapObj.fbPlayerDict[playerID] = serverGroupID
    if playerID not in copyMapObj.enterPlayerIDList:
        copyMapObj.enterPlayerIDList.append(playerID)
    
    #GameWorld.DebugLog("玩家登录动态分配的跨服地图: GetMapID=%s,GetRealMapID=%s,GetFBID()=%s,serverGroupID=%s" 
    #                   % (curPlayer.GetMapID(), mapID, copyMapID, serverGroupID), playerID)
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -70,6 +70,7 @@
import CrossRealmPlayer
import CrossRealmMsg
import CrossRealmPK
import CrossBattlefield
import ChPyNetSendPack
import NetPackCommon
import AuctionHouse
@@ -508,10 +509,10 @@
        return
    
    if callName == "SendMail":
        title, content, getDays, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource = eval(resultName)
        title, content, getDays, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource, crossMail = eval(resultName)
        limitTime = str(GameWorld.GetDatetimeByDiffDays(getDays))
        limitTime = limitTime.split(".")[0]
        PlayerCompensation.SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource)
        PlayerCompensation.SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource, crossMail=crossMail)
        return
    
    if callName == "SendMailBatch":
@@ -649,6 +650,11 @@
        CrossRealmPlayer.OnCrossRealmRegOK(srcPlayerID, eval(resultName), tick)
        return
    
    # 跨服战场结算
    if callName =="CrossBattlefieldOver":
        CrossBattlefield.MapServer_CrossBattlefieldOver(eval(resultName))
        return
    #py喇叭聊天
    if callName == 'PYSpeaker':
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -24,6 +24,7 @@
import GameWorldArena
import ChPyNetSendPack
import PlayerFBHelpBattle
import CrossBattlefield
import PyGameDataStruct
import IpyGameDataPY
import PyDataManager
@@ -44,6 +45,9 @@
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    
    if CrossBattlefield.IsBattlefieldCallPlayer(playerID):
        return True
    SaveDBLimitLV = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 1)
    #校验玩家等级
    if playerLV < SaveDBLimitLV:
@@ -58,6 +62,9 @@
        return True
    
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    if CrossBattlefield.IsBattlefieldCallPlayer(playerID):
        return True
    
    NeedCheckBillBoardType = IpyGameDataPY.GetFuncEvalCfg("PlayerViewCache", 2)
@@ -116,7 +123,7 @@
    ## 获取缓存基础属性字典信息
    if not hasattr(curCache, "PropDataDict"):
        curCache.PropDataDict = {}
    if not curCache.PropDataDict:
    if not curCache.PropDataDict and curCache.PropData:
        curCache.PropDataDict = eval(curCache.PropData)
    return curCache.PropDataDict
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -139,6 +139,10 @@
g_crossFBFuncLinePlayerCountInfo = {} # 跨服副本功能线路人数信息,本服缓存 {mapID:{funcLineID:[playerCount], ...}, ...}
g_unNotifyPlayerCrossMsgDict = {} # 未通知玩家的跨服命令 {playerID:{msgType:[msgInfo], ...}, ...}
g_crossBattlefieldBuyInfo = {} # 跨服战场购买记录 {zoneID:{hmNum:{playerID:CrossBattlefieldBuy, ...}, ...}, ...}
g_familyTalkCache = {} #{familyID:[[time,content,extras],..]}
g_worldTalkCache = [] #[[time,name, playerID, content,extras],..]
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -650,6 +650,18 @@
PacketSubCMD_1=0x10
PacketCallFunc_1=OnLuckyCloudBuy
;跨服战场
[PlayerCrossBattlefield]
ScriptName = Player\PlayerCrossBattlefield.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 1
PacketCMD_1=0xC1
PacketSubCMD_1=0x09
PacketCallFunc_1=OnCrossBattlefieldBuyOpen
;改名功能
[UpdatePlayerName]
ScriptName = Player\UpdatePlayerName.py
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -3987,6 +3987,126 @@
#------------------------------------------------------
# C0 08 跨服战场召集场次踢人 #tagCGCrossBattlefieldCallKick
class  tagCGCrossBattlefieldCallKick(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("TagPlayerID", c_int),    #目标玩家ID,即要被踢出去的玩家ID
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC0
        self.SubCmd = 0x08
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC0
        self.SubCmd = 0x08
        self.Hour = 0
        self.Minute = 0
        self.TagPlayerID = 0
        return
    def GetLength(self):
        return sizeof(tagCGCrossBattlefieldCallKick)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C0 08 跨服战场召集场次踢人 //tagCGCrossBattlefieldCallKick:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                TagPlayerID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.TagPlayerID
                                )
        return DumpString
m_NAtagCGCrossBattlefieldCallKick=tagCGCrossBattlefieldCallKick()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGCrossBattlefieldCallKick.Cmd,m_NAtagCGCrossBattlefieldCallKick.SubCmd))] = m_NAtagCGCrossBattlefieldCallKick
#------------------------------------------------------
# C0 07 跨服战场加入召集场次 #tagCGCrossBattlefieldJoinByCall
class  tagCGCrossBattlefieldJoinByCall(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("BuyPlayerID", c_int),    #加入目标玩家的召集队伍,即购买召集场的玩家ID
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC0
        self.SubCmd = 0x07
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC0
        self.SubCmd = 0x07
        self.Hour = 0
        self.Minute = 0
        self.BuyPlayerID = 0
        return
    def GetLength(self):
        return sizeof(tagCGCrossBattlefieldJoinByCall)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C0 07 跨服战场加入召集场次 //tagCGCrossBattlefieldJoinByCall:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                BuyPlayerID:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.BuyPlayerID
                                )
        return DumpString
m_NAtagCGCrossBattlefieldJoinByCall=tagCGCrossBattlefieldJoinByCall()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGCrossBattlefieldJoinByCall.Cmd,m_NAtagCGCrossBattlefieldJoinByCall.SubCmd))] = m_NAtagCGCrossBattlefieldJoinByCall
#------------------------------------------------------
# C0 03 强制退出跨服状态 #tagCGForceQuitCrossState
class  tagCGForceQuitCrossState(Structure):
@@ -18941,6 +19061,66 @@
#------------------------------------------------------
# C1 09 跨服战场购买开启场次 #tagCMCrossBattlefieldBuyOpen
class  tagCMCrossBattlefieldBuyOpen(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("Hour", c_ubyte),    #战场开启时
                  ("Minute", c_ubyte),    #战场开启分
                  ("Faction", c_ubyte),    #阵营 1-红;2-蓝
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC1
        self.SubCmd = 0x09
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC1
        self.SubCmd = 0x09
        self.Hour = 0
        self.Minute = 0
        self.Faction = 0
        return
    def GetLength(self):
        return sizeof(tagCMCrossBattlefieldBuyOpen)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C1 09 跨服战场购买开启场次 //tagCMCrossBattlefieldBuyOpen:
                                Cmd:%s,
                                SubCmd:%s,
                                Hour:%d,
                                Minute:%d,
                                Faction:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.Hour,
                                self.Minute,
                                self.Faction
                                )
        return DumpString
m_NAtagCMCrossBattlefieldBuyOpen=tagCMCrossBattlefieldBuyOpen()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMCrossBattlefieldBuyOpen.Cmd,m_NAtagCMCrossBattlefieldBuyOpen.SubCmd))] = m_NAtagCMCrossBattlefieldBuyOpen
#------------------------------------------------------
# C1 06 跨服NPC对话 #tagCMCrossNPCTalk
class  tagCMCrossNPCTalk(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -12288,6 +12288,283 @@
#------------------------------------------------------
# C0 09 跨服战场玩家购买战场信息 #tagGCCrossBattlefieldBuyInfo
class  tagGCCrossBattlefieldPlayer(Structure):
    PlayerID = 0    #(DWORD PlayerID)
    PlayerName = ""    #(char PlayerName[33])
    Job = 0    #(BYTE Job)
    LV = 0    #(WORD LV)//等级
    RealmLV = 0    #(WORD RealmLV)//境界
    FightPower = 0    #(DWORD FightPower)//战力求余亿部分
    FightPowerEx = 0    #(DWORD FightPowerEx)//战力整除亿部分
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.PlayerName,_pos = CommFunc.ReadString(_lpData, _pos,33)
        self.Job,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.LV,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.RealmLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        return _pos
    def Clear(self):
        self.PlayerID = 0
        self.PlayerName = ""
        self.Job = 0
        self.LV = 0
        self.RealmLV = 0
        self.FightPower = 0
        self.FightPowerEx = 0
        return
    def GetLength(self):
        length = 0
        length += 4
        length += 33
        length += 1
        length += 2
        length += 2
        length += 4
        length += 4
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteDWORD(data, self.PlayerID)
        data = CommFunc.WriteString(data, 33, self.PlayerName)
        data = CommFunc.WriteBYTE(data, self.Job)
        data = CommFunc.WriteWORD(data, self.LV)
        data = CommFunc.WriteWORD(data, self.RealmLV)
        data = CommFunc.WriteDWORD(data, self.FightPower)
        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
        return data
    def OutputString(self):
        DumpString = '''
                                PlayerID:%d,
                                PlayerName:%s,
                                Job:%d,
                                LV:%d,
                                RealmLV:%d,
                                FightPower:%d,
                                FightPowerEx:%d
                                '''\
                                %(
                                self.PlayerID,
                                self.PlayerName,
                                self.Job,
                                self.LV,
                                self.RealmLV,
                                self.FightPower,
                                self.FightPowerEx
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyPlayer(Structure):
    BuyPlayerID = 0    #(DWORD BuyPlayerID)//购买的玩家ID,即召集人
    Faction = 0    #(BYTE Faction)//阵营 1-红;2-蓝
    FactionPlayerCount = 0    #(BYTE FactionPlayerCount)
    FactionPlayerList = list()    #(vector<tagGCCrossBattlefieldPlayer> FactionPlayerList)//阵营所有玩家列表,包含召集人
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.BuyPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.Faction,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.FactionPlayerCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.FactionPlayerCount):
            temFactionPlayerList = tagGCCrossBattlefieldPlayer()
            _pos = temFactionPlayerList.ReadData(_lpData, _pos)
            self.FactionPlayerList.append(temFactionPlayerList)
        return _pos
    def Clear(self):
        self.BuyPlayerID = 0
        self.Faction = 0
        self.FactionPlayerCount = 0
        self.FactionPlayerList = list()
        return
    def GetLength(self):
        length = 0
        length += 4
        length += 1
        length += 1
        for i in range(self.FactionPlayerCount):
            length += self.FactionPlayerList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteDWORD(data, self.BuyPlayerID)
        data = CommFunc.WriteBYTE(data, self.Faction)
        data = CommFunc.WriteBYTE(data, self.FactionPlayerCount)
        for i in range(self.FactionPlayerCount):
            data = CommFunc.WriteString(data, self.FactionPlayerList[i].GetLength(), self.FactionPlayerList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                BuyPlayerID:%d,
                                Faction:%d,
                                FactionPlayerCount:%d,
                                FactionPlayerList:%s
                                '''\
                                %(
                                self.BuyPlayerID,
                                self.Faction,
                                self.FactionPlayerCount,
                                "..."
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyHM(Structure):
    Hour = 0    #(BYTE Hour)//战场开启时
    Minute = 0    #(BYTE Minute)//战场开启分
    BuyPlayerCount = 0    #(BYTE BuyPlayerCount)
    BuyPlayerList = list()    #(vector<tagGCCrossBattlefieldBuyPlayer> BuyPlayerList)//购买本场次的玩家信息列表
    data = None
    def __init__(self):
        self.Clear()
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        self.Hour,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.Minute,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.BuyPlayerCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.BuyPlayerCount):
            temBuyPlayerList = tagGCCrossBattlefieldBuyPlayer()
            _pos = temBuyPlayerList.ReadData(_lpData, _pos)
            self.BuyPlayerList.append(temBuyPlayerList)
        return _pos
    def Clear(self):
        self.Hour = 0
        self.Minute = 0
        self.BuyPlayerCount = 0
        self.BuyPlayerList = list()
        return
    def GetLength(self):
        length = 0
        length += 1
        length += 1
        length += 1
        for i in range(self.BuyPlayerCount):
            length += self.BuyPlayerList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteBYTE(data, self.Hour)
        data = CommFunc.WriteBYTE(data, self.Minute)
        data = CommFunc.WriteBYTE(data, self.BuyPlayerCount)
        for i in range(self.BuyPlayerCount):
            data = CommFunc.WriteString(data, self.BuyPlayerList[i].GetLength(), self.BuyPlayerList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                Hour:%d,
                                Minute:%d,
                                BuyPlayerCount:%d,
                                BuyPlayerList:%s
                                '''\
                                %(
                                self.Hour,
                                self.Minute,
                                self.BuyPlayerCount,
                                "..."
                                )
        return DumpString
class  tagGCCrossBattlefieldBuyInfo(Structure):
    Head = tagHead()
    HMCount = 0    #(BYTE HMCount)// 为0时清空重置,其他为增量更新
    HMBuyList = list()    #(vector<tagGCCrossBattlefieldBuyHM> HMBuyList)//购买场次列表
    data = None
    def __init__(self):
        self.Clear()
        self.Head.Cmd = 0xC0
        self.Head.SubCmd = 0x09
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        _pos = self.Head.ReadData(_lpData, _pos)
        self.HMCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        for i in range(self.HMCount):
            temHMBuyList = tagGCCrossBattlefieldBuyHM()
            _pos = temHMBuyList.ReadData(_lpData, _pos)
            self.HMBuyList.append(temHMBuyList)
        return _pos
    def Clear(self):
        self.Head = tagHead()
        self.Head.Clear()
        self.Head.Cmd = 0xC0
        self.Head.SubCmd = 0x09
        self.HMCount = 0
        self.HMBuyList = list()
        return
    def GetLength(self):
        length = 0
        length += self.Head.GetLength()
        length += 1
        for i in range(self.HMCount):
            length += self.HMBuyList[i].GetLength()
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
        data = CommFunc.WriteBYTE(data, self.HMCount)
        for i in range(self.HMCount):
            data = CommFunc.WriteString(data, self.HMBuyList[i].GetLength(), self.HMBuyList[i].GetBuffer())
        return data
    def OutputString(self):
        DumpString = '''
                                Head:%s,
                                HMCount:%d,
                                HMBuyList:%s
                                '''\
                                %(
                                self.Head.OutputString(),
                                self.HMCount,
                                "..."
                                )
        return DumpString
m_NAtagGCCrossBattlefieldBuyInfo=tagGCCrossBattlefieldBuyInfo()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCCrossBattlefieldBuyInfo.Head.Cmd,m_NAtagGCCrossBattlefieldBuyInfo.Head.SubCmd))] = m_NAtagGCCrossBattlefieldBuyInfo
#------------------------------------------------------
# C0 07 跨服排行榜信息 #tagGCCrossBillboardInfo
class  tagGCCrossBillboardData(Structure):
@@ -40971,6 +41248,62 @@
#------------------------------------------------------
# C1 07 跨服战场玩家信息 #tagMCCrossBattlefieldPlayerInfo
class  tagMCCrossBattlefieldPlayerInfo(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("BuyOpenCountToday", c_ubyte),    # 今日已购买开启战场次数
                  ("HighScoreToday", c_int),    # 今日最高积分
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xC1
        self.SubCmd = 0x07
        return
    def ReadData(self, stringData, _pos=0, _len=0):
        self.Clear()
        memmove(addressof(self), stringData[_pos:], self.GetLength())
        return _pos + self.GetLength()
    def Clear(self):
        self.Cmd = 0xC1
        self.SubCmd = 0x07
        self.BuyOpenCountToday = 0
        self.HighScoreToday = 0
        return
    def GetLength(self):
        return sizeof(tagMCCrossBattlefieldPlayerInfo)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// C1 07 跨服战场玩家信息 //tagMCCrossBattlefieldPlayerInfo:
                                Cmd:%s,
                                SubCmd:%s,
                                BuyOpenCountToday:%d,
                                HighScoreToday:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.BuyOpenCountToday,
                                self.HighScoreToday
                                )
        return DumpString
m_NAtagMCCrossBattlefieldPlayerInfo=tagMCCrossBattlefieldPlayerInfo()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCCrossBattlefieldPlayerInfo.Cmd,m_NAtagMCCrossBattlefieldPlayerInfo.SubCmd))] = m_NAtagMCCrossBattlefieldPlayerInfo
#------------------------------------------------------
# C1 02 跨服PK玩家奖励记录 #tagMCCrossRealmPKAwardState
class  tagMCCrossRealmPKAwardState(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/CrossBattle.py
New file
@@ -0,0 +1,118 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GM.Commands.CrossBattle
#
# @todo:跨服战场
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服战场
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import GameWorld
import GameLogic_CrossBattlefield
import ShareDefine
import ChConfig
import FBCommon
## 逻辑实现
#  @param curPlayer
#  @param cmdList 参数列表
#  @return None
def OnExec(curPlayer, cmdList):
    if not cmdList:
        GameWorld.DebugAnswer(curPlayer, "--------------------------------")
        GameWorld.DebugAnswer(curPlayer, "设置玩家击杀: CrossBattle 1 击杀数 [可选玩家ID]")
        GameWorld.DebugAnswer(curPlayer, "设置玩家积分: CrossBattle 2 总积分 [可选玩家ID]")
        GameWorld.DebugAnswer(curPlayer, "设置阵营击杀: CrossBattle 3 击杀数 [可选阵营ID]")
        GameWorld.DebugAnswer(curPlayer, "设置阵营积分: CrossBattle 4 总积分 [可选阵营ID]")
        GameWorld.DebugAnswer(curPlayer, "重置大奖信息: CrossBattle 5 [可选是否重新随机]")
        GameWorld.DebugAnswer(curPlayer, "可选玩家/阵营ID没填则默认自身")
        return
    tick = GameWorld.GetGameWorld().GetTick()
    playerID = curPlayer.GetPlayerID()
    mapID = FBCommon.GetRecordMapID(GameWorld.GetMap().GetMapID())
    value1 = cmdList[0]
    if value1 in [1, 2, 3, 4, 5, 6] and not GameWorld.IsCrossServer() or mapID != ChConfig.Def_FBMapID_CrossBattlefield:
        GameWorld.DebugAnswer(curPlayer, "该命令需在跨服战场使用CrossServer发送")
        return
    # 设置玩家击杀
    if value1 == 1:
        setCount = cmdList[1] if len(cmdList) > 1 else 1
        tagPlayerID = cmdList[2] if len(cmdList) > 2 else playerID
        battleObj = GameLogic_CrossBattlefield.GetBattlePlayerObj(tagPlayerID)
        battleObj.killCount = setCount
        GameWorld.DebugAnswer(curPlayer, "玩家(%s)击杀数: %s" % (tagPlayerID, battleObj.killCount))
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, GameLogic_CrossBattlefield.DoFBHelp, 0, GameLogic_CrossBattlefield.refreshFactionPlayer) # 触发刷新FBHelp
        return
    # 设置玩家积分
    if value1 == 2:
        setScore = cmdList[1] if len(cmdList) > 1 else 1
        tagPlayerID = cmdList[2] if len(cmdList) > 2 else playerID
        battleObj = GameLogic_CrossBattlefield.GetBattlePlayerObj(tagPlayerID)
        battleObj.score = setScore
        GameWorld.DebugAnswer(curPlayer, "玩家(%s)积分: %s" % (tagPlayerID, battleObj.score))
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, GameLogic_CrossBattlefield.DoFBHelp, 0, GameLogic_CrossBattlefield.refreshFactionPlayer) # 触发刷新FBHelp
        return
    # 设置阵营击杀
    if value1 == 3:
        setCount = cmdList[1] if len(cmdList) > 1 else 1
        tagFaction = cmdList[2] if len(cmdList) > 2 else 0
        if not tagFaction or tagFaction not in ShareDefine.CampTypeList:
            battleObj = GameLogic_CrossBattlefield.GetBattlePlayerObj(playerID)
            tagFaction = battleObj.faction
        factionObj = GameLogic_CrossBattlefield.GetBattleFactionObj(tagFaction)
        factionObj.killCount = setCount
        GameWorld.DebugAnswer(curPlayer, "阵营(%s)击杀数: %s" % (tagFaction, factionObj.killCount))
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, GameLogic_CrossBattlefield.DoFBHelp, 0, GameLogic_CrossBattlefield.refreshFactionPlayer) # 触发刷新FBHelp
        return
    # 设置阵营积分
    if value1 == 4:
        setScore = cmdList[1] if len(cmdList) > 1 else 1
        tagFaction = cmdList[2] if len(cmdList) > 2 else 0
        if not tagFaction or tagFaction not in ShareDefine.CampTypeList:
            battleObj = GameLogic_CrossBattlefield.GetBattlePlayerObj(playerID)
            tagFaction = battleObj.faction
        factionObj = GameLogic_CrossBattlefield.GetBattleFactionObj(tagFaction)
        factionObj.score = setScore
        GameWorld.DebugAnswer(curPlayer, "阵营(%s)积分: %s" % (tagFaction, factionObj.score))
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, GameLogic_CrossBattlefield.DoFBHelp, 0, GameLogic_CrossBattlefield.refreshFactionPlayer) # 触发刷新FBHelp
        return
    # 重置大奖信息
    if value1 == 5:
        isRand = cmdList[1] if len(cmdList) > 1 else 1
        worldObj = GameLogic_CrossBattlefield.GetBattleWorld()
        worldObj.superItemPlayerID = 0
        worldObj.superItemPlayerName = ""
        if isRand:
            worldObj.RandSuperTask()
        for faction in ShareDefine.CampTypeList:
            if not faction:
                continue
            factionObj = GameLogic_CrossBattlefield.GetBattleFactionObj(faction)
            factionObj.superTaskValue = 0
            factionObj.superTaskFinishCount = 0
            factionObj.setSuperTaskValueMax(worldObj)
            for battleObj in factionObj.factionPlayerDict.values():
                battleObj.superTaskValue = 0
                battleObj.superTaskFinishCount = 0
                battleObj.setSuperTaskValueMax(worldObj)
        GameWorld.DebugAnswer(curPlayer, "重置大奖信息OK!")
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, GameLogic_CrossBattlefield.DoFBHelp, 0, GameLogic_CrossBattlefield.refreshFactionPlayer) # 触发刷新FBHelp
        return
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -1766,6 +1766,15 @@
    #DebugLog("日期相差天数大于1,不同一天!")
    return False
def CheckTimeIsSameWeek(checkTime):
    '''判断指定time值与当天时间对比是否为游戏内的同一周;特殊时间点过天后才算不同天
    @return: 是否同一周
    '''
    checkDate = ChangeTimeNumToDatetime(checkTime)
    checkWeek = datetime.datetime.isocalendar(checkDate)[1]
    curWeek = GetWeekOfYear()
    return checkWeek == curWeek
## 获取玩家的区服名,仅在跨服有效
#  @param curPlayer 玩家实例
#  @return: 区服名
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -917,7 +917,7 @@
    gameFB = GameWorld.GetGameFB()
    gameFB.SetFBStep(step)
    gameFB.SetFBStepTick(tick)
    GameWorld.Log("SetFBStep %s, tick=%s" % (step, tick))
    GameWorld.Log("SetFBStep %s, tick=%s" % (step, tick), GameWorld.GetGameWorld().GetPropertyID())
    return
#---------------------------------------------------------------------
## 给客户端弹消息
@@ -2595,13 +2595,16 @@
def GetFBAreaRewardTechPoint(gameWorld, playerID):
    return gameWorld.GetGameWorldDictByKey(ChConfig.Map_Player_AreaReward_GetTechPoint%playerID)
def NotifyCopyMapPlayerFBHelp(tick, fbHelpFunc, interval=10000):
def NotifyCopyMapPlayerFBHelp(tick, fbHelpFunc, interval=10000, befLogicFunc=None):
    gameFB = GameWorld.GetGameFB()
    lastTick = gameFB.GetGameFBDictByKey(ChConfig.Def_FB_NotifyFBHelpTick)
    if tick - lastTick < interval:
        return
    gameFB.SetGameFBDict(ChConfig.Def_FB_NotifyFBHelpTick, tick)
    
    if befLogicFunc:
        befLogicFunc(tick)
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
@@ -2612,9 +2615,9 @@
def GetCrossDynamicLineMapZoneID():
    ## 获取跨服动态线路地图本线路跨服分区
    return GameWorld.GetGameWorld().GetPropertyID() / 10000
    return GameWorld.GetGameWorld().GetPropertyID() / 100000
def GetCrossDynamicLineMapFuncLineID():
    ## 获取跨服动态线路地图本线路功能线路ID
    return GameWorld.GetGameWorld().GetPropertyID() % 10000 / 10
    return GameWorld.GetGameWorld().GetPropertyID() % 100000 / 100
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_CrossBattlefield.py
New file
@@ -0,0 +1,890 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GameWorldLogic.FBProcess.GameLogic_CrossBattlefield
#
# @todo:跨服战场
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服战场
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import FBCommon
import GameWorld
import NPCCommon
import PyGameData
import IPY_GameWorld
import IpyGameDataPY
import PlayerActivity
import GameWorldProcess
import PlayerControl
import ShareDefine
import SkillCommon
import BuffSkill
import ChConfig
import GameMap
import operator
import random
import time
#当前副本地图的状态
(
FB_Step_Open, # 地图开启
FB_Step_Prepare, # 地图准备
FB_Step_Fighting, # 战斗中
FB_Step_LeaveTime, # 自由时间
FB_Step_Over, # 副本关闭
) = range(5)
(
Time_Prepare, # 副本准备时间 0
Time_Fight, # 副本战斗时间 1
Time_Leave, # 副本离开时间 2
) = range(3)
# 大奖任务类型
SuperTaskList = (
SuperTaskType_Kill, # 击杀 1
SuperTaskType_Score, # 积分 2
) = range(1, 1 + 2)
GameFBData_BattleWorld = "BattleWorld"
GameFBData_FactionInfo = "FactionInfo"
GameFBData_PlayerInfo = "PlayerInfo"
## 战场公共世界管理类
class BattleWorld():
    def __init__(self):
        self.superItemInfo = [] # 大奖信息 [物品ID,个数,是否拍品]
        self.superItemPlayerID = 0 # 大奖中奖者玩家ID
        self.superItemPlayerName = "" # 大奖中奖者玩家名
        self.superTaskType = 0 # 大奖任务类型
        self.RandSuperTask()
        return
    def RandSuperTask(self):
        # 随机生成大奖任务
        fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
        superItemWeightList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAwardSuper", 1)
        superItemInfo = GameWorld.GetResultByWeightList(superItemWeightList)
        self.superItemInfo = superItemInfo if superItemInfo else []
        self.superTaskType = random.choice(SuperTaskList)
        GameWorld.Log("随机战场大奖: superTaskType=%s,superItemInfo=%s" % (self.superTaskType, self.superItemInfo), fbPropertyID)
        return
## 战斗实体基类
class BattleBase(object):
    BattleType_Player = "Player"
    BattleType_Faction = "Faction"
    def __init__(self, ID):
        self.fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
        self.battleType = ""
        self.ID = ID
        self.name = ""
        self.score = 0 # 积分
        self.scoreSortTime = 0 # 积分变更排序time值,用于同积分时,先到排名靠前
        self.superTaskValue = 0 # 大奖任务当前进度
        self.superTaskValueMax = 0 # 大奖任务完成需要的进度值
        self.superTaskFinishCount = 0 # 大奖任务完成次数
        self.killCount = 0 # 击杀数
        self.continueKillCount = 0 # 连杀数
        self.beKilledCount = 0 # 被击杀数
        return
    def addScore(self, worldObj, addValue):
        self.score += addValue
        calcTime = 3471264000 #GameWorld.ChangeTimeStrToNum("2080-01-01 00:00:00")
        self.scoreSortTime = max(0, calcTime - int(time.time()))
        GameWorld.DebugLog("    增加积分: battleType=%s,ID=%s,addValue=%s,updScore=%s" % (self.battleType, self.ID, addValue, self.score), self.fbPropertyID)
        self.addSuperTaskValue(worldObj, SuperTaskType_Score, addValue)
        return
    def addKillCount(self, worldObj, addCount):
        self.killCount += addCount
        self.continueKillCount += addCount # 同步增加连杀
        self.addSuperTaskValue(worldObj, SuperTaskType_Kill, addCount)
        return
    def addBeKilledCount(self, addCount):
        self.beKilledCount += addCount
        self.continueKillCount = 0 # 被击杀时,连杀重置
        return
    def setSuperTaskValueMax(self, worldObj):
        if worldObj == None:
            worldObj = GetBattleWorld()
        taskType = worldObj.superTaskType
        if taskType == SuperTaskType_Kill:
            superTaskValueMaxList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAwardSuper2", 1)
        elif taskType == SuperTaskType_Score:
            superTaskValueMaxList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldAwardSuper2", 2)
        else:
            return
        if self.battleType == self.BattleType_Player:
            curValueMaxList = superTaskValueMaxList[0]
        elif self.battleType == self.BattleType_Faction:
            curValueMaxList = superTaskValueMaxList[1]
        else:
            return
        if not curValueMaxList:
            return
        if self.superTaskFinishCount >= len(curValueMaxList):
            valueMax = curValueMaxList[-1]
        else:
            valueMax = curValueMaxList[self.superTaskFinishCount]
        self.superTaskValueMax = valueMax
        GameWorld.Log("    更新大奖任务进度完成所需值! battleType=%s,ID=%s,taskType=%s,superTaskFinishCount=%s,superTaskValueMax=%s"
                      % (self.battleType, self.ID, taskType, self.superTaskFinishCount, self.superTaskValueMax), self.fbPropertyID)
        return
    def addSuperTaskValue(self, worldObj, taskType, addValue):
        if taskType != worldObj.superTaskType:
            #GameWorld.DebugLog("    非战场大奖任务类型,不处理! taskType=%s != superTaskType(%s)" % (taskType, worldObj.superTaskType), self.fbPropertyID)
            return
        if len(worldObj.superItemInfo) != 3:
            GameWorld.ErrLog("大奖任务物品异常,不处理! taskType=%s,superItemInfo=%s" % (taskType, worldObj.superItemInfo), self.fbPropertyID)
            return
        if worldObj.superItemPlayerID:
            GameWorld.DebugLog("    大奖已经产出,不再处理! superItemPlayerID=%s" % worldObj.superItemPlayerID, self.fbPropertyID)
            return
        if not self.superTaskValueMax:
            self.setSuperTaskValueMax(worldObj)
        if not self.superTaskValueMax:
            return
        self.superTaskValue += addValue
        if self.superTaskValue < self.superTaskValueMax:
            GameWorld.DebugLog("    更新大奖进度! battleType=%s,ID=%s,taskType=%s,addValue=%s,superTaskValue=%s < %s"
                               % (self.battleType, self.ID, taskType, addValue, self.superTaskValue, self.superTaskValueMax), self.fbPropertyID)
            return
        self.superTaskValue -= self.superTaskValueMax
        self.superTaskFinishCount += 1
        GameWorld.Log("    完成大奖任务! battleType=%s,ID=%s,taskType=%s,superTaskFinishCount=%s"
                      % (self.battleType, self.ID, taskType, self.superTaskFinishCount), self.fbPropertyID)
        self.setSuperTaskValueMax(worldObj)
        superRate = self.getSuperItemRate()
        tick = GameWorld.GetGameWorld().GetTick()
        if not GameWorld.CanHappen(superRate):
            GameWorld.Log("        大奖没有中奖! battleType=%s,ID=%s,taskType=%s,superRate=%s"
                          % (self.battleType, self.ID, taskType, superRate), self.fbPropertyID)
            FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0, refreshFactionPlayer)
            return
        superItemPlayerID = self.getSuperItemPlayerID()
        GameWorld.Log("        大奖中奖! battleType=%s,ID=%s,taskType=%s,superRate=%s,superItemPlayerID=%s"
                      % (self.battleType, self.ID, taskType, superRate, superItemPlayerID), self.fbPropertyID)
        if not superItemPlayerID:
            return
        worldObj.superItemPlayerID = superItemPlayerID
        itemID, itemCount = worldObj.superItemInfo[0], worldObj.superItemInfo[1]
        battleObj = GetBattlePlayerObj(superItemPlayerID)
        worldObj.superItemPlayerName = battleObj.name
        PlayerControl.FBNotify("CrossBattlefieldSuperItemPlayer", [battleObj.faction, battleObj.name, itemID, itemCount])
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0, refreshFactionPlayer)
        return
    def getSuperItemRate(self): return 0
    def getSuperItemPlayerID(self): return 0
## 战场阵营类
class BattleFaction(BattleBase):
    def __init__(self, faction):
        super(BattleFaction, self).__init__(faction)
        self.faction = faction
        self.battleType = self.BattleType_Faction
        self.factionPlayerDict = {} # {playerID:BattlePlayer, ...}
        self.battlePlayerSortList = [] # 阵营积分排名玩家列表 [BattlePlayer, ...]
        self.scoreKingIDList = [] # 前x名积分王ID列表 [playerID, ...] ,只算在线的,所以不一定是积分排名前x名
        self.onlineFightPowerTotal = 0 # 在线人数总战力
        self.onlinePlayerIDList = [] # 在线玩家ID列表 [playerID, ...]
        self.setSuperTaskValueMax(None)
        return
    def getSuperItemRate(self):
        single = IpyGameDataPY.GetFuncCfg("CrossBattlefieldAwardSuper", 3)
        return single * len(self.onlinePlayerIDList)
    def getSuperItemPlayerID(self):
        if not self.onlinePlayerIDList:
            return 0
        return random.choice(self.onlinePlayerIDList)
    def addScore(self, worldObj, addValue):
        super(BattleFaction, self).addScore(worldObj, addValue)
        battleOverScore = IpyGameDataPY.GetFuncCfg("CrossBattlefieldFB", 2)
        if self.score < battleOverScore:
            return
        GameWorld.Log("阵营积分达到获胜积分,获胜! faction=%s,updScore=%s" % (self.faction, self.score), self.fbPropertyID)
        tick = GameWorld.GetGameWorld().GetTick()
        DoOver(self.faction, tick)
        return
## 战场玩家类
class BattlePlayer(BattleBase):
    def __init__(self, playerID):
        super(BattlePlayer, self).__init__(playerID)
        self.battleType = self.BattleType_Player
        self.faction = 0
        self.accID = ""
        self.job = 1
        self.realmLV = 0
        self.fightPower = 0
        self.highScoreToday = 0 # 本日最高积分
        self.highScoreWeekTotal = 0 # 本周每日最高分累计
        self.enterCountWeek = 0 # 本周累计进入次数
        self.onlineCalcTick = 0 # 在线统计tick
        self.onlineTimes = 0 # 活动累计在线时长,毫秒
        self.setSuperTaskValueMax(None)
        return
    def getSuperItemRate(self): return IpyGameDataPY.GetFuncCfg("CrossBattlefieldAwardSuper", 2)
    def getSuperItemPlayerID(self): return self.ID
def GetBattleWorld():
    worldObj = FBCommon.GetGameFBData(GameFBData_BattleWorld)
    if not worldObj:
        worldObj = BattleWorld()
        FBCommon.SetGameFBData(GameFBData_BattleWorld, worldObj)
    return worldObj
def GetBattleFactionObj(faction):
    factionObj = None
    factionInfoDict = FBCommon.GetGameFBData(GameFBData_FactionInfo)
    if faction in factionInfoDict:
        factionObj = factionInfoDict[faction]
    else:
        factionObj = BattleFaction(faction)
        factionInfoDict[faction] = factionObj
    return factionObj
def GetBattlePlayerObj(playerID):
    playerObj = None
    playerInfoDict = FBCommon.GetGameFBData(GameFBData_PlayerInfo)
    if playerID in playerInfoDict:
        playerObj = playerInfoDict[playerID]
    else:
        playerObj = BattlePlayer(playerID)
        playerInfoDict[playerID] = playerObj
    return playerObj
def GetBFStepTime(): return IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldFB", 1) # 阶段时间
def OnOpenFB(tick):
    #fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    FBCommon.SetGameFBData(GameFBData_BattleWorld, None)
    FBCommon.SetGameFBData(GameFBData_FactionInfo, {})
    FBCommon.SetGameFBData(GameFBData_PlayerInfo, {})
    GetBattleWorld()
    GetBattleFactionObj(ShareDefine.CampType_Justice)
    GetBattleFactionObj(ShareDefine.CampType_Evil)
    FBCommon.SetFBStep(FB_Step_Prepare, tick)
    return
def OnCloseFB(tick):
    GameWorld.GetGameWorld().SetPropertyID(0)
    FBCommon.SetGameFBData(GameFBData_BattleWorld, None)
    FBCommon.SetGameFBData(GameFBData_FactionInfo, None)
    FBCommon.SetGameFBData(GameFBData_PlayerInfo, None)
    return
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    if GameWorld.IsCrossServer():
        return True
    playerID = curPlayer.GetPlayerID()
    hmNum = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield)
    for zoneID, hmCallTeamInfo in PyGameData.g_crossBattlefieldCallTeamInfo.items():
        callTeamInfo = hmCallTeamInfo.get(hmNum, {})
        for buyPlayerID, callTeam in callTeamInfo.items():
            if playerID in callTeam["callPlayerIDList"]:
                GameWorld.DebugLog("    在战场召集队伍里,免费进入! zoneID=%s,hmNum=%s,buyPlayerID=%s,callPlayerIDList=%s"
                                   % (zoneID, hmNum, buyPlayerID, callTeam["callPlayerIDList"]), playerID)
                return True
    remainCnt = PlayerActivity.GetDailyActionrRemainCnt(curPlayer, ShareDefine.DailyActionID_CrossBattlefield)
    GameWorld.DebugLog("    战场剩余可进入次数! hmNum=%s,remainCnt=%s" % (hmNum, remainCnt), playerID)
    return remainCnt > 0
def OnChangeMapAsk(ask, tick):
    return IPY_GameWorld.cmeAccept
##副本玩家进入点, 玩家分散在半径3格范围
def OnGetFBEnterPos(curPlayer, mapID, lineId, ipyEnterPosInfo, tick):
    return random.choice(IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldPos", 1))
def DoEnterFB(curPlayer, tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    playerID = curPlayer.GetPlayerID()
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    if fbStep not in [FB_Step_Prepare, FB_Step_Fighting]:
        GameWorld.Log("DoEnterFB... fbPropertyID=%s,fbStep=%s PlayerLeaveFB" % (fbPropertyID, fbStep), playerID)
        PlayerControl.PlayerLeaveFB(curPlayer)
        return
    fightPower = PlayerControl.GetFightPower(curPlayer)
    battleObj = GetBattlePlayerObj(playerID)
    battleObj.job = curPlayer.GetJob()
    battleObj.accID = curPlayer.GetAccID()
    battleObj.name = curPlayer.GetPlayerName()
    battleObj.realmLV = curPlayer.GetOfficialRank()
    battleObj.fightPower = fightPower
    battleObj.highScoreToday = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_HighScoreToday)
    battleObj.highScoreWeekTotal = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_HighScoreTotalWeek)
    battleObj.enterCountWeek = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_EnterCountWeek)
    GameWorld.Log("DoEnterFB... fbPropertyID=%s,fbStep=%s,faction=%s" % (fbPropertyID, fbStep, battleObj.faction), playerID)
    if fbStep == FB_Step_Prepare:
        notify_tick = GetBFStepTime()[Time_Prepare] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttWaitStart, 0, max(notify_tick, 0), True)
    elif fbStep == FB_Step_Fighting:
        notify_tick = GetBFStepTime()[Time_Fight] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, max(notify_tick, 0), True)
        isToSafePos = not battleObj.faction
        allotPlayerFaction(playerID, fightPower, curPlayer, isToSafePos, tick)
    return
##获得副本帮助信息, 用于通知阵营比分条
def DoFBHelp(curPlayer, tick):
    #gameWorld = GameWorld.GetGameWorld()
    playerID = curPlayer.GetPlayerID()
    worldObj = GetBattleWorld()
    battleObj = GetBattlePlayerObj(playerID)
    playerInfo = {"score":battleObj.score, "superTaskValue":battleObj.superTaskValue,
                  "superTaskValueMax":battleObj.superTaskValueMax, "superTaskFinishCount":battleObj.superTaskFinishCount}
    factionInfo = {}
    for faction in [ShareDefine.CampType_Justice, ShareDefine.CampType_Evil]:
        factionObj = GetBattleFactionObj(faction)
        factionInfo[str(faction)] = {"score":factionObj.score, "superTaskValue":factionObj.superTaskValue,
                                     "superTaskValueMax":factionObj.superTaskValueMax, "superTaskFinishCount":factionObj.superTaskFinishCount}
    worldInfo = {"superTaskType":worldObj.superTaskType, "superItemPlayerName":worldObj.superItemPlayerName, "superItemInfo":worldObj.superItemInfo}
    helpDict = {"playerInfo":playerInfo, "factionInfo":factionInfo, "worldInfo":worldInfo}
    #GameWorld.DebugLog("DoFBHelp %s" % helpDict, playerID)
    FBCommon.Notify_FBHelp(curPlayer, helpDict)
    return
##玩家退出副本
def DoExitFB(curPlayer, tick):
    gameFB = GameWorld.GetGameFB()
    fbStep = gameFB.GetFBStep()
    if fbStep != FB_Step_Fighting:
        return
    playerID = curPlayer.GetPlayerID()
    faction = curPlayer.GetFaction()
    fightPower = PlayerControl.GetFightPower(curPlayer)
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("DoExitFB... playerID=%s,faction=%s,fightPower=%s,fbStep=%s"
                  % (playerID, faction, fightPower, fbStep), fbPropertyID)
    battleObj = GetBattlePlayerObj(playerID)
    battleObj.onlineCalcTick = 0
    if not faction:
        return
    factionObj = GetBattleFactionObj(faction)
    factionObj.onlineFightPowerTotal = max(0, factionObj.onlineFightPowerTotal - fightPower)
    if playerID in factionObj.onlinePlayerIDList:
        factionObj.onlinePlayerIDList.remove(playerID)
    GameWorld.Log("    faction=%s,onlineFightPowerTotal=%s,onlinePlayerIDList=%s"
                  % (faction, factionObj.onlineFightPowerTotal, factionObj.onlinePlayerIDList), fbPropertyID)
    return
##玩家主动离开副本.
def DoPlayerLeaveFB(curPlayer, tick):
    return
##副本总逻辑计时器
# @param tick 时间戳
# @return 无意义
# @remarks 副本总逻辑计时器
def OnProcess(tick):
    fbStep = GameWorld.GetGameFB().GetFBStep()
    # 副本准备
    if fbStep == FB_Step_Prepare:
        __DoLogic_FB_Prepare(tick)
    # 副本进行中
    elif fbStep == FB_Step_Fighting:
        __DoLogic_FB_Fighting(tick)
    # 副本结束
    elif fbStep == FB_Step_LeaveTime:
        __DoLogic_FB_Over(tick)
    return
def __DoLogic_FB_Prepare(tick):
    remaindTick = GetBFStepTime()[Time_Prepare] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
    if remaindTick > 0:
        return
    FBCommon.SetFBStep(FB_Step_Fighting, tick)
    playerInfoDict = {}
    playerInfoList = []
    fightTime = GetBFStepTime()[Time_Fight] * 1000
    playerManager = GameWorld.GetMapCopyPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        playerID = curPlayer.GetPlayerID()
        if not playerID:
            continue
        curPlayer.Sync_TimeTick(IPY_GameWorld.tttTowerTake, 0, fightTime, True)
        fightPower = PlayerControl.GetFightPower(curPlayer)
        playerInfo = {"playerID":playerID, "fightPower":fightPower, "curPlayer":curPlayer}
        playerInfoList.append(playerInfo)
        playerInfoDict[playerID] = playerInfo
#    ##--------- 山寨分配测试代码 --------------
#    for i in xrange(15):
#        playerID = i + 1
#        fightPower = random.randint(100000, 10000000000)
#        playerInfoList.append({"playerID":playerID, "fightPower":fightPower, "curPlayer":None})
#    ##--------- 山寨分配测试代码 --------------
    # 按战力排序
    # 当超过副本下限人数时,往人数低的阵营划分; 否则 往战力低的阵营划分
    playerInfoList.sort(key=operator.itemgetter("fightPower"), reverse=True)
    isToSafePos = True
    # 先分配召集队伍
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    zoneID = FBCommon.GetCrossDynamicLineMapZoneID()
    hmNum = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield)
    hmCallTeamInfo = PyGameData.g_crossBattlefieldCallTeamInfo.get(zoneID, {})
    callTeamInfo = hmCallTeamInfo.get(hmNum, {})
    GameWorld.Log("准备开始战斗,分配阵营: zoneID=%s,hmNum=%s,callTeamInfo=%s,playerCountTotal=%s" % (zoneID, hmNum, callTeamInfo, len(playerInfoList)), fbPropertyID)
    callPlayerIDList = []
    for callTeam in callTeamInfo.values():
        for playerID in callTeam["callPlayerIDList"]:
            callPlayerIDList.append(playerID)
            if playerID not in playerInfoDict:
                continue
            playerInfo = playerInfoDict[playerID]
            playerID = playerInfo["playerID"]
            fightPower = playerInfo["fightPower"]
            curPlayer = playerInfo["curPlayer"]
            allotPlayerFaction(playerID, fightPower, curPlayer, isToSafePos, tick)
    for playerInfo in playerInfoList:
        playerID = playerInfo["playerID"]
        fightPower = playerInfo["fightPower"]
        curPlayer = playerInfo["curPlayer"]
        if playerID in callPlayerIDList:
            continue
        allotPlayerFaction(playerID, fightPower, curPlayer, isToSafePos, tick)
    return
def allotPlayerFaction(playerID, fightPower, curPlayer, isToSafePos, tick):
    ## 分配玩家阵营
    zoneID = FBCommon.GetCrossDynamicLineMapZoneID()
    hmNum = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield)
    hmCallTeamInfo = PyGameData.g_crossBattlefieldCallTeamInfo.get(zoneID, {})
    callTeamInfo = hmCallTeamInfo.get(hmNum, {})
    callFaction = None
    for callTeam in callTeamInfo.values():
        if playerID in callTeam["callPlayerIDList"]:
            callFaction = callTeam["factionID"]
            break
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    battleObj = GetBattlePlayerObj(playerID)
    faction = battleObj.faction
    if callFaction:
        faction = callFaction # 召集阵营为固定阵营
    if not faction:
        jFactionObj = GetBattleFactionObj(ShareDefine.CampType_Justice)
        eFactionObj = GetBattleFactionObj(ShareDefine.CampType_Evil)
        onlinePlayerTotal = len(jFactionObj.onlinePlayerIDList) + len(eFactionObj.onlinePlayerIDList)
        fbPlayerCountSet = IpyGameDataPY.GetFuncCfg("CrossBattlefieldFB", 3) # 副本下限人数设定
        # 当超过副本下限人数时,往人数低的阵营划分; 否则 往战力低的阵营划分
        if onlinePlayerTotal > fbPlayerCountSet:
            faction = ShareDefine.CampType_Justice if len(jFactionObj.onlinePlayerIDList) <= len(eFactionObj.onlinePlayerIDList) else ShareDefine.CampType_Evil
        else:
            faction = ShareDefine.CampType_Justice if jFactionObj.onlineFightPowerTotal <= eFactionObj.onlineFightPowerTotal else ShareDefine.CampType_Evil
    battleObj.faction = faction
    battleObj.onlineCalcTick = tick
    factionObj = GetBattleFactionObj(faction)
    if playerID not in factionObj.factionPlayerDict:
        factionObj.factionPlayerDict[playerID] = battleObj
    # 在线才会添加,所以处理在线相关
    factionObj.onlineFightPowerTotal += fightPower
    if playerID not in factionObj.onlinePlayerIDList:
        factionObj.onlinePlayerIDList.append(playerID)
    GameWorld.Log("    分配阵营: callFaction=%s,faction=%s,playerID=%s,fightPower=%s,onlineFightPowerTotal=%s,onlinePlayerIDList=%s,isToSafePos=%s"
                  % (callFaction, faction, playerID, fightPower, factionObj.onlineFightPowerTotal, factionObj.onlinePlayerIDList, isToSafePos), fbPropertyID)
    if curPlayer:
        curPlayer.SetFaction(faction)
        if isToSafePos:
            __RandFactionSafeArea(curPlayer)
    return
## 重置副本复活玩家坐标点
def OnResetFBRebornPlacePos(curPlayer, rebornPlace, tick):
    __RandFactionSafeArea(curPlayer)
    return
def __RandFactionSafeArea(curPlayer):
    faction = curPlayer.GetFaction()
    factionSafeAreaRandPosList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldPos", 2)
    if faction and faction <= len(factionSafeAreaRandPosList):
        safePosX, safePosY, radius = random.choice(factionSafeAreaRandPosList[faction - 1])
        posPoint = GameMap.GetEmptyPlaceInArea(safePosX, safePosY, radius)
        posX, posY = posPoint.GetPosX(), posPoint.GetPosY()
    else:
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
    curPlayer.ResetPos(posX, posY)
    return
def __DoLogic_FB_Fighting(tick):
    remaindTick = GetBFStepTime()[Time_Fight] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
    if remaindTick > 0:
        FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 5000, refreshFactionPlayer)
        return
    jFactionObj = GetBattleFactionObj(ShareDefine.CampType_Justice)
    eFactionObj = GetBattleFactionObj(ShareDefine.CampType_Evil)
    jFactionScore = jFactionObj.score
    eFactionScore = eFactionObj.score
    winnerFaction = ShareDefine.CampType_Justice if jFactionScore >= eFactionScore else ShareDefine.CampType_Evil
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    GameWorld.Log("副本时间到,积分高的阵营获胜! winnerFaction=%s,jFactionScore=%s,eFactionScore=%s"
                  % (winnerFaction, jFactionScore, eFactionScore), fbPropertyID)
    DoOver(winnerFaction, tick)
    return
def __DoLogic_FB_Over(tick):
    remaindTick = GetBFStepTime()[Time_Leave] * 1000 - (tick - GameWorld.GetGameFB().GetFBStepTick())
    if remaindTick > 0:
        return
    GameWorldProcess.CloseFB(tick)
    FBCommon.SetFBStep(FB_Step_Over, tick)
    return
##处理副本中杀死玩家逻辑
def DoFBOnKill_Player(curPlayer, defender, tick):
    playerID = curPlayer.GetPlayerID()
    tagPlayerID = defender.GetPlayerID()
    faction = curPlayer.GetFaction()
    tagFaction = defender.GetFaction()
    curBattleObj = GetBattlePlayerObj(playerID)
    tagBattleObj = GetBattlePlayerObj(tagPlayerID)
    if not faction or not tagFaction:
        GameWorld.ErrLog("击杀玩家没有阵营! playerID=%s,faction=%s,tagPlayerID=%s,tagFaction=%s"
                         % (playerID, faction, tagPlayerID, tagFaction), playerID)
        return
    worldObj = GetBattleWorld()
    curFactionObj = GetBattleFactionObj(faction)
    tagFactionObj = GetBattleFactionObj(tagFaction)
    GameWorld.DebugLog("击杀玩家! playerID=%s,faction=%s,tagPlayerID=%s,tagFaction=%s"
                       % (playerID, faction, tagPlayerID, tagFaction), playerID)
    # 1. 处理玩家
    killPlayerScore = IpyGameDataPY.GetFuncCfg("CrossBattlefieldKill", 1)
    addPlayerScore = 0
    addPlayerScore += killPlayerScore
    #addPlayerScore += ... # 其他加分
    curBattleObj.addScore(worldObj, addPlayerScore)
    curBattleObj.addKillCount(worldObj, 1)
    tagBattleObj.addBeKilledCount(1)
    # 2. 处理阵营
    addFactionScore = 0
    addFactionScore += addPlayerScore # 阵营积分同步增加玩家得分
    # 击杀积分王,阵营积分额外增加
    for index, kingID in enumerate(tagFactionObj.scoreKingIDList):
        if kingID == tagPlayerID:
            killScoreKingScoreList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldScoreKing", 3)
            if index < len(killScoreKingScoreList):
                kingScore = killScoreKingScoreList[index]
                addFactionScore += kingScore
                GameWorld.DebugLog("    对方是积分王,阵营额外获得积分:  index=%s,kingScore=%s" % (index, kingScore), playerID)
            killScoreKingNotifyList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldScoreKing", 4)
            if index < len(killScoreKingNotifyList):
                msgMark = killScoreKingNotifyList[index]
                defMapID = defender.GetMapID()
                defPosX = defender.GetPosX()
                defPosY = defender.GetPosY()
                PlayerControl.FBNotify(msgMark, [faction, curPlayer.GetPlayerName(), tagFaction, defender.GetPlayerName(), defMapID, defPosX, defPosY])
            break
    #addFactionScore += ... # 其他加分
    curFactionObj.addScore(worldObj, addFactionScore)
    curFactionObj.addKillCount(worldObj, 1)
    tagFactionObj.addBeKilledCount(1)
    return True
def refreshFactionPlayer(tick):
    ## 刷新阵营玩家相关
    scoreKingScoreMin = IpyGameDataPY.GetFuncCfg("CrossBattlefieldScoreKing", 1)
    scoreKingBuffIDList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldScoreKing", 2)
    scoreKingCount = len(scoreKingBuffIDList)
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    for faction in [ShareDefine.CampType_Justice, ShareDefine.CampType_Evil]:
        factionObj = GetBattleFactionObj(faction)
        befKingIDList = factionObj.scoreKingIDList
        factionObj.battlePlayerSortList = factionObj.factionPlayerDict.values()
        factionObj.battlePlayerSortList.sort(key=operator.attrgetter("score", "scoreSortTime"), reverse=True)
        aftKingIDList = []
        aftKingObjList = []
        for batObj in factionObj.battlePlayerSortList:
            playerID = batObj.ID
            curPlayer = copyMapMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            if batObj.onlineCalcTick:
                batObj.onlineTimes += max(0, tick - batObj.onlineCalcTick)
                batObj.onlineCalcTick = tick
            if batObj.score < scoreKingScoreMin:
                continue
            if len(aftKingIDList) < scoreKingCount:
                aftKingIDList.append(playerID)
                aftKingObjList.append([curPlayer, batObj])
        if befKingIDList == aftKingIDList:
            #GameWorld.DebugLog("    阵营积分王不变: faction=%s,befKingIDList=%s,aftKingIDList=%s" % (faction, befKingIDList, aftKingIDList))
            continue
        GameWorld.DebugLog("    阵营积分王变更: faction=%s,befKingIDList=%s,aftKingIDList=%s" % (faction, befKingIDList, aftKingIDList))
        # 更新buff
        for index, objInfo in enumerate(aftKingObjList):
            curPlayer, batObj = objInfo
            playerID = curPlayer.GetPlayerID()
            addBuffID = scoreKingBuffIDList[index] if index < len(scoreKingBuffIDList) else 0
            if playerID in befKingIDList:
                befIndex = befKingIDList.index(playerID)
                if index == befIndex:
                    GameWorld.DebugLog("        积分王名次不变,不需要变更buff! index=%s" % index, playerID)
                    continue
                delBuffID = scoreKingBuffIDList[befIndex] if befIndex < len(scoreKingBuffIDList) else 0
                if delBuffID:
                    GameWorld.DebugLog("        积分王名次变更! 删除旧buff! befIndex=%s,delBuffID=%s" % (befIndex, delBuffID), playerID)
                    BuffSkill.DelBuffBySkillID(curPlayer, delBuffID, tick)
            if addBuffID:
                GameWorld.DebugLog("        积分王名次变更! 添加新buff! index=%s,addBuffID=%s" % (index, addBuffID), playerID)
                SkillCommon.AddBuffBySkillType_NoRefurbish(curPlayer, addBuffID, tick)
        for befIndex, playerID in enumerate(befKingIDList):
            if playerID in aftKingIDList:
                continue
            delBuffID = scoreKingBuffIDList[befIndex] if befIndex < len(scoreKingBuffIDList) else 0
            if delBuffID:
                GameWorld.DebugLog("        积分王被挤掉! 删除旧buff! befIndex=%s,delBuffID=%s" % (befIndex, delBuffID), playerID)
                BuffSkill.DelBuffBySkillID(curPlayer, delBuffID, tick)
        factionObj.scoreKingIDList = aftKingIDList
    return
def DoOver(winnerFaction, tick):
    fbPropertyID = GameWorld.GetGameWorld().GetPropertyID()
    zoneID = FBCommon.GetCrossDynamicLineMapZoneID()
    funcLineID = FBCommon.GetCrossDynamicLineMapFuncLineID()
    GameWorld.Log("跨服战场结算! zoneID=%s,funcLineID=%s,winnerFaction=%s" % (zoneID, funcLineID, winnerFaction), fbPropertyID)
    FBCommon.NotifyCopyMapPlayerFBHelp(tick, DoFBHelp, 0, refreshFactionPlayer) # 结算前强刷一次
    #awardOnlineTimes = IpyGameDataPY.GetFuncCfg("CrossBattlefieldAward", 1) # 结算奖励需参与活动时长,秒钟
    #gameFB = GameWorld.GetGameFB()
    worldObj = GetBattleWorld()
    leaveTime = GetBFStepTime()[Time_Leave] * 1000
    copyMapMgr = GameWorld.GetMapCopyPlayerManager()
    superItemPlayerID = worldObj.superItemPlayerID
    superItemPlayerName = worldObj.superItemPlayerName
    # 没人中奖则随机给其中一位在线的玩家
    if not superItemPlayerID:
        onlinePlayerIDList = []
        for index in xrange(copyMapMgr.GetPlayerCount()):
            curPlayer = copyMapMgr.GetPlayerByIndex(index)
            playerID = curPlayer.GetPlayerID()
            if not playerID:
                continue
            onlinePlayerIDList.append(playerID)
        if onlinePlayerIDList:
            superItemPlayerID = random.choice(onlinePlayerIDList)
            superPlayerObj = GetBattlePlayerObj(superItemPlayerID)
            superItemPlayerName = superPlayerObj.name
            worldObj.superItemPlayerID = superItemPlayerID
            worldObj.superItemPlayerName = superItemPlayerName
            GameWorld.Log("没人中大奖,则随机其中一位在线玩家! superItemPlayerID=%s,onlinePlayerIDList=%s"
                          % (superItemPlayerID, onlinePlayerIDList), fbPropertyID)
    GameWorld.Log("大奖获奖信息: superItemInfo=%s,superItemPlayerID=%s" % (worldObj.superItemInfo, superItemPlayerID), fbPropertyID)
    hmNum = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_CrossBattlefield)
    hmCallTeamInfo = PyGameData.g_crossBattlefieldCallTeamInfo.get(zoneID, {})
    callTeamInfo = hmCallTeamInfo.get(hmNum, {})
    allCallPlayerIDList = []
    for callTeam in callTeamInfo.values():
        allCallPlayerIDList.extend(callTeam["callPlayerIDList"])
    scoreKingID, scoreKingName = 0, "" # 本场积分王: 获胜方在线第一名积分
    battlePlayerList = []
    for faction in [ShareDefine.CampType_Justice, ShareDefine.CampType_Evil]:
        factionObj = GetBattleFactionObj(faction)
        factionScore = factionObj.score
        isWinner = (faction == winnerFaction)
        scoreKingIDList = factionObj.scoreKingIDList
        GameWorld.Log("结算阵营! faction=%s,factionScore=%s,isWinner=%s,playerCount=%s,onlineFightPowerTotal=%s,onlinePlayerIDList=%s,scoreKingIDList=%s"
                      % (faction, factionScore, isWinner, len(factionObj.battlePlayerSortList), factionObj.onlineFightPowerTotal, factionObj.onlinePlayerIDList, scoreKingIDList), fbPropertyID)
        if isWinner and scoreKingIDList:
            scoreKingID = scoreKingIDList[0]
            scoreKingObj = GetBattlePlayerObj(scoreKingID)
            scoreKingName = scoreKingObj.name
        rankPlayerList = []
        for battleObj in factionObj.battlePlayerSortList[:20]:
            rankPlayerList.append({"Name":battleObj.name, "Job":battleObj.job, "Score":battleObj.score})
        overDict = {"rankPlayerList":rankPlayerList, "faction":faction, "superItemPlayerName":superItemPlayerName, "scoreKingName":scoreKingName}
        for rank, battleObj in enumerate(factionObj.battlePlayerSortList, 1):
            playerID = battleObj.ID
            score = battleObj.score
            job = battleObj.job
            realmLV = battleObj.realmLV
            name = battleObj.name
            highScoreToday = battleObj.highScoreToday
            highScoreWeekTotal = battleObj.highScoreWeekTotal
            enterCountWeek = battleObj.enterCountWeek
            onlineTimes = battleObj.onlineTimes / 1000
            GameWorld.Log("     rank=%s,playerID=%s,score=%s,fightPower=%s,onlineTimes=%s,accID=%s"
                          % (rank, playerID, score, battleObj.fightPower, onlineTimes, battleObj.accID), fbPropertyID)
            #服务端暂不做参与时长奖励限制
            #if onlineTimes < awardOnlineTimes:
            #    GameWorld.Log("    活动时长不足,不给奖励! faction=%s,playerID=%s,isWinner=%s" % (faction, playerID, isWinner), fbPropertyID)
            #    continue
            isCallEnter = 1 if playerID in allCallPlayerIDList else 0 # 是否召集进入的
            playerInfo = [faction, rank, playerID, job, realmLV, name, score, highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter]
            battlePlayerList.append(playerInfo)
            player = copyMapMgr.FindPlayerByID(playerID)
            if not player:
                continue
            player.Sync_TimeTick(IPY_GameWorld.tttLeaveMap, 0, leaveTime, True)
            lineID = 0
            overDict.update({FBCommon.Over_rank:rank, "score":score, "highScoreToday":highScoreToday, "onlineTimes":onlineTimes})
            FBCommon.NotifyFBOver(player, ChConfig.Def_FBMapID_CrossBattlefield, lineID, isWinner, overDict)
    GameWorld.Log("本场最终结算积分王: scoreKingID=%s" % scoreKingID, fbPropertyID)
    # 同步GameServer 比赛结果
    superItemInfo = worldObj.superItemInfo
    msgInfo = str([fbPropertyID, zoneID, funcLineID, winnerFaction, superItemInfo, superItemPlayerID, superItemPlayerName, scoreKingID, scoreKingName, battlePlayerList])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CrossBattlefieldOver", msgInfo, len(msgInfo))
    FBCommon.SetFBStep(FB_Step_LeaveTime, tick)
    return
## 执行副本杀怪逻辑
def DoFB_Player_KillNPC(curPlayer, curNPC, tick):
    #curNPC.SetDict(ChConfig.Def_NPC_Dict_Faction, 0)
    npcFaction = NPCCommon.GetFaction(curNPC)
    if npcFaction:
        __OnPlayerKillOtherFactionRobot(curPlayer, npcFaction, tick)
    else:
        __OnPlayerKillNeutralNPC(curPlayer, curNPC, tick)
    return
def __OnPlayerKillOtherFactionRobot(curPlayer, npcFaction, tick):
    ## 玩家击杀其他阵营机器人玩家
    return
def __OnPlayerKillNeutralNPC(curPlayer, curNPC, tick):
    ## 玩家击杀中立怪物
    return
## 检查是否可攻击, 主判定不可攻击的情况,其他逻辑由外层决定
def CheckCanAttackTagObjInFB(attacker, defender):
    gameFB = GameWorld.GetGameFB()
    if gameFB.GetFBStep() != FB_Step_Fighting:
        return False
    return True
## 玩家攻击玩家是否有惩罚
def DoFBAttackHasPunish(atkPlayer, defPlayer):
    return False
## 是否副本复活
def OnPlayerReborn():
    return True
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
@@ -184,7 +184,8 @@
    return
def GameServer_DynamicLineMapStateChange(gameWorld, state):
    if gameWorld.GetMapID() not in ChConfig.Def_CrossDynamicLineMap:
    mapID = FBCommon.GetRecordMapID(gameWorld.GetMapID())
    if mapID not in ChConfig.Def_CrossDynamicLineMap:
        return
    
    realMapID, copyMapID = gameWorld.GetRealMapID(), gameWorld.GetCopyMapID()
@@ -620,7 +621,8 @@
        msgInfo = str([gameWorld.GetMapID(), gameWorld.GetLineID(), gameWorld.GetRealMapID(), gameWorld.GetCopyMapID()])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "CommMapServerInitOK", msgInfo, len(msgInfo))
        
    if gameWorld.GetMapID() in ChConfig.Def_CrossDynamicLineMap and gameWorld.GetCopyMapID() == gameWorld.GetGameWorldCount() - 1:
    dataMapID = FBCommon.GetRecordMapID(gameWorld.GetMapID())
    if dataMapID in ChConfig.Def_CrossDynamicLineMap and gameWorld.GetCopyMapID() == gameWorld.GetGameWorldCount() - 1:
        msgInfo = str([gameWorld.GetRealMapID(), gameWorld.GetGameWorldCount()])
        GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "DynamicLineMapInitOK", msgInfo, len(msgInfo))
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/NPC/NPCCommon.py
@@ -135,7 +135,9 @@
def SetSuppressFightPower(curNPC, value): return curNPC.SetThunderDef(min(value, ShareDefine.Def_UpperLimit_DWord))
def GetCommendFightPower(curNPC): return curNPC.GetFireDef() # 火防代表推荐战力
def GetDropOwnerType(curNPC): return curNPC.GetThunderAtk() # 雷攻代表掉落归属类型
def GetFaction(curNPC): return curNPC.GetCountry()
def GetFaction(curNPC):
    faction = curNPC.GetDictByKey(ChConfig.Def_NPC_Dict_Faction)
    return faction if faction else curNPC.GetCountry()
def GetSkillAtkRate(curNPC): return curNPC.GetPoisionAtk() # 毒攻代表NPC技能伤害加成万分率
def GetFinalHurt(curNPC): return curNPC.GetFireAtk() # 火攻代表NPC最终固定伤害加成, 普攻也有效果
def SetFinalHurt(curNPC, hurt): return curNPC.SetFireAtk(hurt) # 火攻代表NPC最终固定伤害加成, 普攻也有效果
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -73,6 +73,7 @@
import PlayerGatherSoul
import PlayerFairyDomain
import PlayerCrossRealmPK
import PlayerCrossBattlefield
import GameFuncComm
import PlayerMagicWeapon
import GameLogic_TrialTower
@@ -713,6 +714,7 @@
    
    # 跨服PK
    PlayerCrossRealmPK.DoPlayerLogin(curPlayer)
    PlayerCrossBattlefield.DoPlayerLogin(curPlayer)
    
    # 幸运云购
    PlayerLuckyCloudBuy.OnPlayerLogin(curPlayer)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActivity.py
@@ -82,6 +82,9 @@
            curPBCnt = __GetPDictValue(curPlayer, key)  #单次进度值
            if curPBCnt:
                __SetPDictValue(curPlayer, key, 0)
            key = ChConfig.Def_PDict_Activity_AddTotal % ipyData.GetID()
            if __GetPDictValue(curPlayer, key):
                __SetPDictValue(curPlayer, key, 0)
        #总活跃度重置
        __SetPDictValue(curPlayer, ChConfig.Def_PDict_Activity_TotalPoint, 0)
        # 重置领奖记录
@@ -238,10 +241,18 @@
    onceActivityTime = dailyQuestData.GetOnceActivityTime()
    if not onceActivity:
        return
    if maxActiveValue and finishCnt > maxActiveValue / onceActivity * onceActivityTime:
        #GameWorld.DebugLog("活跃度可完成次数已达到上限,activityNum=%s" % (activityNum))
        return
    curAddTotal = None
    if maxActiveValue:
        if dailyQuestData.GetRelatedType() == RelatedType_1 and dailyQuestData.GetRelatedID() in [ShareDefine.DailyActionID_CrossBattlefield]:
            curAddTotal = __GetPDictValue(curPlayer, ChConfig.Def_PDict_Activity_AddTotal % activityNum)  #单次进度值
            if curAddTotal >= maxActiveValue:
                GameWorld.DebugLog("活跃度可完成次数已达到上限,activityNum=%s,curAddTotal=%s >= %s" % (activityNum, curAddTotal, maxActiveValue))
                return
        else:
            if finishCnt > maxActiveValue / onceActivity * onceActivityTime:
                #GameWorld.DebugLog("活跃度可完成次数已达到上限,activityNum=%s" % (activityNum))
                return
    key = ChConfig.Def_PDict_Activity_FinishCnt % activityNum
    curPBCnt = __GetPDictValue(curPlayer, key)  #单次进度值
    
@@ -250,6 +261,10 @@
    
    __SetPDictValue(curPlayer, key, addPbCnt)
    
    if curAddTotal != None:
        curAddTotal += addValue
        __SetPDictValue(curPlayer, ChConfig.Def_PDict_Activity_AddTotal % activityNum, curAddTotal)
    #
    DoAddActivity(curPlayer, addValue, True)
    return
@@ -476,7 +491,7 @@
    
    # 由GameServer决定
    # 目前跨服PK暂不需要判断,因为跨服PK次数结算在本服,玩家可能上次未结算离线,等非匹配期间上线,也需要加上,所以暂不判断
    if dailyID not in [ShareDefine.DailyActionID_CrossReamPK]:
    if dailyID not in [ShareDefine.DailyActionID_CrossReamPK, ShareDefine.DailyActionID_CrossBattlefield]:
        if not GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % dailyID):
            GameWorld.DebugLog("日常活动未开启!dailyID=%s" % dailyID)
            return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -328,7 +328,7 @@
    return
#---------------------------------------------------------------------
def SendMailBatch(mailTypeKey, batchPlayerIDList, batchAddItemList=[], batchParamList=[], batchGold=[], batchGoldPaper=[], batchSilver=[], batchDetail=[], moneySource=ChConfig.Def_GiveMoney_Mail):
def SendMailBatch(mailTypeKey, batchPlayerIDList, batchAddItemList=[], batchParamList=[], batchGold=[], batchGoldPaper=[], batchSilver=[], batchDetail=[], moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    '''批量发送邮件, 用于瞬间需要发送多封(大量)邮件的,比如一些公共副本活动等结算时
    @param mailTypeKey: 邮件模板key
    @param batchPlayerIDList: [playerIDList, playerIDList, ...]
@@ -352,17 +352,17 @@
                bGoldPaper = [batchGoldPaper[i]] if len(batchGoldPaper) > i else []
                bSilver = [batchSilver[i]] if len(batchSilver) > i else []
                bDetail = [batchDetail[i]] if len(batchDetail) > i else []
                AddUnLoginOKPlayerMailCache(playerID, "ByMailTemplate", [mailTypeKey, bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource])
                AddUnLoginOKPlayerMailCache(playerID, "ByMailTemplate", [mailTypeKey, bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource, crossMail])
                playerIDList.pop(playerIDList.index(playerID))
                continue
            
    msgInfo = str([mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource])
    msgInfo = str([mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource, crossMail])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "SendMailBatch", msgInfo, len(msgInfo))
    GameWorld.Log("SendMailBatch %s, batchPlayerIDList=%s, batchAddItemList=%s, batchParamList=%s, batchGold=%s, batchGoldPaper=%s, batchSilver=%s" 
                  % (mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver))
    return
def SendMailByKey(mailTypeKey, playerIDList, addItemList, paramList=[], gold=0, goldPaper=0, silver=0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
def SendMailByKey(mailTypeKey, playerIDList, addItemList, paramList=[], gold=0, goldPaper=0, silver=0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    '''
    @param detail: 记录邮件流向用
    '''
@@ -370,7 +370,7 @@
        mailTypeKey = ShareDefine.DefaultLackSpaceMailType
    
    content = "<MailTemplate>%s</MailTemplate>%s" % (mailTypeKey, json.dumps(paramList, ensure_ascii=False))
    SendMail("", content, 30, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource)
    SendMail("", content, 30, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource, crossMail)
    return
def SendCrossMail(serverGroupID, mailTypeKey, playerIDList, addItemList, paramList=[]):
@@ -412,7 +412,7 @@
## 功能发放物品补偿/奖励邮件
#  @param addItemList [(itemID, itemCnt, 是否拍品), {或物品信息字典}, ...]
#  @return
def SendMail(title, content, getDays, playerIDList, addItemList, gold=0, goldPaper=0, silver=0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
def SendMail(title, content, getDays, playerIDList, addItemList, gold=0, goldPaper=0, silver=0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    if not playerIDList:
        return
    
@@ -424,7 +424,7 @@
        return
    
    # 跨服服务器不允许发送邮件
    if GameWorld.IsCrossServer():
    if GameWorld.IsCrossServer() and not crossMail:
        return
    
    sendPlayerIDList = []
@@ -437,7 +437,7 @@
        sendPlayerIDList.append(playerID)
        
    combineItemList = CombineMailItem(addItemList)
    cmdList = [title, content, getDays, sendPlayerIDList, combineItemList, gold, goldPaper, silver, detail, moneySource]
    cmdList = [title, content, getDays, sendPlayerIDList, combineItemList, gold, goldPaper, silver, detail, moneySource, crossMail]
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "SendMail", '%s' % (cmdList), len(str(cmdList)))
    return True
@@ -476,8 +476,8 @@
            title, content, getDays, addItemList, gold, goldPaper, silver, detail, moneySource = mailInfo
            SendMail(title, content, getDays, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource)
        elif cacheType == "ByMailTemplate":
            mailTypeKey, bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource = mailInfo
            SendMailBatch(mailTypeKey, [playerIDList], bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource)
            mailTypeKey, bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource, crossMail = mailInfo
            SendMailBatch(mailTypeKey, [playerIDList], bAddItemList, bParamList, bGold, bGoldPaper, bSilver, bDetail, moneySource, crossMail)
    return
def CombineMailItem(addItemList):
@@ -1785,7 +1785,10 @@
        
        if not FBLogic.OnEnterFBEvent(curPlayer, mapID, lineID, tick):
            GameWorld.DebugLog("    OnEnterFBEvent False!", curPlayer.GetPlayerID())
            NotifyCode(curPlayer, "SingleEnterDefaul")
            if mapID in [ChConfig.Def_FBMapID_CrossBattlefield]:
                NotifyCode(curPlayer, "GeRen_chenxin_268121")
            else:
                NotifyCode(curPlayer, "SingleEnterDefaul")
            return
        
    # 需要动态分布线路的地图,发送到跨服服务器进行分配
@@ -1797,7 +1800,7 @@
                return
            extendInfo["BossID"] = bossID
            
        msgDict = {"PlayerID":curPlayer.GetPlayerID(), "MapID":mapID, "FuncLineID":lineID}
        msgDict = {"PlayerID":curPlayer.GetPlayerID(), "MapID":mapID, "FuncLineID":lineID, "LV":curPlayer.GetLV()}
        if extendInfo:
            msgDict.update(extendInfo)
        GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_EnterFB, msgDict)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossBattlefield.py
New file
@@ -0,0 +1,232 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.PlayerCrossBattlefield
#
# @todo:跨服战场
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服战场
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import GameWorld
import ShareDefine
import PlayerControl
import IpyGameDataPY
import datetime
import CrossRealmPlayer
import ChConfig
import ItemCommon
import IPY_GameWorld
import ChPyNetSendPack
import NetPackCommon
import ItemControler
import PlayerActivity
def DoPlayerLogin(curPlayer):
    if GameWorld.IsCrossServer():
        return
    SyncCrossBattlefieldPlayerInfo(curPlayer)
    return
def DoPlayerOnDay(curPlayer):
    if GameWorld.IsCrossServer():
        return
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_HighScoreToday, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_BuyOpenCountToday, 0)
    SyncCrossBattlefieldPlayerInfo(curPlayer)
    return
def DoPlayerOnWeek(curPlayer):
    if GameWorld.IsCrossServer():
        return
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_EnterCountWeek, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_BuyOpenCountWeek, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_HighScoreTotalWeek, 0)
    return
#// C1 09 跨服战场购买开启场次 #tagCMCrossBattlefieldBuyOpen
#
#struct    tagCMCrossBattlefieldBuyOpen
#{
#    tagHead        Head;
#    BYTE    Hour;        //战场开启时
#    BYTE    Minute;        //战场开启分
#    BYTE    Faction;        //阵营 1-红;2-蓝
#};
def OnCrossBattlefieldBuyOpen(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    hour = clientData.Hour
    minute = clientData.Minute
    faction = clientData.Faction
    if GameWorld.IsCrossServer():
        GameWorld.DebugLog("跨服服务器无法发起匹配!", playerID)
        return
    if not CrossRealmPlayer.IsCrossServerOpen():
        PlayerControl.NotifyCode(curPlayer, "CrossMatching18")
        return
    if faction not in [ChConfig.CampType_Justice, ChConfig.CampType_Evil]:
        GameWorld.DebugLog("没有该战场阵营选择! faction=%s" % faction, playerID)
        return
    callOpenHMList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldOpen", 2)
    if [hour, minute] not in callOpenHMList:
        GameWorld.Log("非可召集的战场场次! hour=%s,minute=%s,callOpenHMList=%s" % (hour, minute, callOpenHMList), playerID)
        return
    closeBuyMinute = IpyGameDataPY.GetFuncCfg("CrossBattlefieldOpen", 4) # 开启前X分钟后关闭购买
    crossServerTimeStr = GameWorld.GetCrossServerTimeStr()
    crossServerDateTime = GameWorld.ChangeStrToDatetime(crossServerTimeStr)
    startTimeStr = "%s-%s-%s %s:%s:00" % (crossServerDateTime.year, crossServerDateTime.month, crossServerDateTime.day, hour, minute)
    startDateTime = GameWorld.ChangeStrToDatetime(startTimeStr)
    endBuyDateTime = startDateTime + datetime.timedelta(minutes= -closeBuyMinute)
    if crossServerDateTime >= endBuyDateTime:
        GameWorld.Log("该时间点战场已关闭召集,不能再召集! hour=%s,minute=%s,crossServerDateTime(%s) >= endBuyDateTime(%s)"
                      % (hour, minute, crossServerDateTime, endBuyDateTime), playerID)
        return
    moneyBuyMaxCount, moneyType, moneyCount = 0, 0, 0
    todayBuyOpenCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_BuyOpenCountToday)
    buyOpenMoneyInfo = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBuyOpen", 1)
    if len(buyOpenMoneyInfo) == 3:
        moneyBuyMaxCount, moneyType, moneyCount = buyOpenMoneyInfo
    # 购买消耗货币
    if todayBuyOpenCount < moneyBuyMaxCount:
        if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyCount):
            GameWorld.DebugLog("跨服战场召集开启货币不足! moneyType=%s,moneyCount=%s" % (moneyType, moneyCount), playerID)
            return
    else:
        costItemID = IpyGameDataPY.GetFuncCfg("CrossBattlefieldBuyOpen", 2)
        costItem = ItemCommon.FindItemInPackByItemID(curPlayer, costItemID, IPY_GameWorld.rptItem)
        if not ItemCommon.CheckItemCanUse(costItem):
            GameWorld.DebugLog("跨服战场召集开启道具不足! costItemID=%s" % costItemID, playerID)
            return
    if not GameWorld.SetPlayerTickTime(curPlayer, ChConfig.TYPE_Player_Tick_Battlefield, tick):
        PlayerControl.NotifyCode(curPlayer, "RequestLater")
        return
    dataMsg = {"openHour":hour, "openMinute":minute, "faction":faction, "todayBuyOpenCount":todayBuyOpenCount,
               "accID":curPlayer.GetAccID(),
               "playerID":playerID,
               "playerName":CrossRealmPlayer.GetCrossPlayerName(curPlayer),
               "playerJob":curPlayer.GetJob(),
               "playerLV":curPlayer.GetLV(),
               "realmLV":curPlayer.GetOfficialRank(),
               "fightPower":PlayerControl.GetFightPower(curPlayer),
               "buyOpenCountWeek":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_BuyOpenCountWeek)
               }
    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_BattlefieldBuyOpen, dataMsg)
    return
def GameServer_CrossBattlefield_DoResult(curPlayer, msgData):
    msgType, dataMsg = msgData[:2]
    #ret = msgData[2] if len(msgData) > 2 else None
    ## 战场购买开启场次
    if msgType == "BattlefieldBuy":
        __DoBattlefieldBuy(curPlayer, dataMsg)
    elif msgType == "BattlefieldOver":
        __DoBattlefieldOver(curPlayer, dataMsg)
    return
def __DoBattlefieldBuy(curPlayer, dataMsg):
    playerID = curPlayer.GetPlayerID()
    openHour, openMinute, faction, todayBuyOpenCount, buyTime = dataMsg
    updTodayBuyOpenCount, updWeekBuyOpenCount = 0, 0
    isToday = GameWorld.CheckTimeIsSameServerDayEx(buyTime)
    if isToday:
        todayBuyOpenCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_BuyOpenCountToday)
        updTodayBuyOpenCount = todayBuyOpenCount + 1
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_BuyOpenCountToday, updTodayBuyOpenCount)
        SyncCrossBattlefieldPlayerInfo(curPlayer)
    isSameWeek = GameWorld.CheckTimeIsSameWeek(buyTime)
    if isSameWeek:
        weekBuyOpenCount = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_BuyOpenCountWeek)
        updWeekBuyOpenCount = weekBuyOpenCount + 1
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_BuyOpenCountWeek, updWeekBuyOpenCount)
    GameWorld.Log("购买召集跨服战场结果: openHour=%s,openMinute=%s,faction=%s,updTodayBuyOpenCount=%s,updWeekBuyOpenCount=%s,buyTime=%s,isToday=%s,isSameWeek=%s"
                  % (openHour, openMinute, faction, updTodayBuyOpenCount, updWeekBuyOpenCount, GameWorld.ChangeTimeNumToStr(buyTime), isToday, isSameWeek), playerID)
    moneyBuyMaxCount, moneyType, moneyCount = 0, 0, 0
    buyOpenMoneyInfo = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBuyOpen", 1)
    if len(buyOpenMoneyInfo) == 3:
        moneyBuyMaxCount, moneyType, moneyCount = buyOpenMoneyInfo
    infoDict = {ChConfig.Def_Cost_Reason_SonKey:"BattlefieldBuyOpen", "buyTime":buyTime, "isToday":isToday, "isSameWeek":isSameWeek,
                "updTodayBuyOpenCount":updTodayBuyOpenCount, "updWeekBuyOpenCount":updWeekBuyOpenCount}
    # 购买消耗货币
    if todayBuyOpenCount < moneyBuyMaxCount:
        if not PlayerControl.PayMoney(curPlayer, moneyType, moneyCount, ChConfig.Def_Cost_CrossBattlefield, infoDict):
            return
    else:
        costItemID = IpyGameDataPY.GetFuncCfg("CrossBattlefieldBuyOpen", 2)
        costItem = ItemCommon.FindItemInPackByItemID(curPlayer, costItemID, IPY_GameWorld.rptItem)
        if not ItemCommon.CheckItemCanUse(costItem):
            costItem = ItemCommon.FindItemInPackByItemID(curPlayer, costItemID, IPY_GameWorld.rptWarehouse)
        if not ItemCommon.CheckItemCanUse(costItem):
            return
        ItemCommon.DelItem(curPlayer, costItem, 1, True, "CrossBattlefield", infoDict)
    # 固定礼包
    buyAwardItemList = IpyGameDataPY.GetFuncEvalCfg("CrossBattlefieldBuyOpen", 3)
    ItemControler.GivePlayerItemOrMail(curPlayer, buyAwardItemList)
    return
def __DoBattlefieldOver(curPlayer, dataMsg):
    playerID = curPlayer.GetPlayerID()
    overTime, highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter = dataMsg
    isToday = GameWorld.CheckTimeIsSameServerDayEx(overTime)
    isSameWeek = GameWorld.CheckTimeIsSameWeek(overTime)
    GameWorld.Log("跨服战场结算玩家结果: highScoreToday=%s,highScoreWeekTotal=%s,enterCountWeek=%s,isCallEnter=%s,overTime=%s,isToday=%s,isSameWeek=%s"
                  % (highScoreToday, highScoreWeekTotal, enterCountWeek, isCallEnter, GameWorld.ChangeTimeNumToStr(overTime), isToday, isSameWeek), playerID)
    if isToday:
        addCnt = 1
        # 非召集进入的需要增加日常次数
        if not isCallEnter:
            PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_CrossBattlefield, addCnt)
        # 召集进入由于是免费进入,不许要增加日常次数,直接增加日常活跃
        else:
            activityNum = PlayerActivity.GetActivityNum(PlayerActivity.RelatedType_1, ShareDefine.DailyActionID_CrossBattlefield)
            PlayerActivity.AddActivityFinishCnt(curPlayer, activityNum, None, addCnt)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_HighScoreToday, highScoreToday)
        SyncCrossBattlefieldPlayerInfo(curPlayer)
    if isSameWeek:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_EnterCountWeek, enterCountWeek)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_Battlefield_HighScoreTotalWeek, highScoreWeekTotal)
    return
def SyncCrossBattlefieldPlayerInfo(curPlayer):
    clientPack = ChPyNetSendPack.tagMCCrossBattlefieldPlayerInfo()
    clientPack.BuyOpenCountToday = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_BuyOpenCountToday)
    clientPack.HighScoreToday = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_Battlefield_HighScoreToday)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -61,6 +61,7 @@
import PlayerGoldInvest
import PlayerNewGuyCard
import PlayerCrossRealmPK
import PlayerCrossBattlefield
import PlayerPet
import BossHurtMng
import PlayerRecover
@@ -576,6 +577,8 @@
        FBHelpBattle.DoPlayerOnDay(curPlayer)
        #跨服竞技场
        PlayerCrossRealmPK.DoPlayerOnDay(curPlayer)
        #跨服战场
        PlayerCrossBattlefield.DoPlayerOnDay(curPlayer)
        #缥缈仙域
        PlayerFairyDomain.OnDay(curPlayer)
        #仙盟宴会
@@ -716,7 +719,8 @@
        #竞技场
        PlayerArena.OnWeekEx(curPlayer)
        PlayerFamily.OnWeekEx(curPlayer)
        PlayerCrossBattlefield.DoPlayerOnWeek(curPlayer)
    # 以下为支持两种重置模式切换配置的
    FBCommon.FBOnWeek(curPlayer, onEventType)
    
@@ -1490,6 +1494,10 @@
            PlayerLuckyCloudBuy.OnLuckyCloudBuyChange()
            return
        
        if key == ShareDefine.Def_Notify_WorldKey_CrossBattlefieldCallTeamInfo:
            PyGameData.g_crossBattlefieldCallTeamInfo = eval(msgValue)
            return
        if msgValue.isdigit():
            value = int(msgValue)
        else:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_CrossBattlefield.py
New file
@@ -0,0 +1,48 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package Player.RemoteQuery.GY_Query_CrossBattlefield
#
# @todo:跨服战场
# @author hxp
# @date 2022-01-06
# @version 1.0
#
# 详细描述: 跨服战场
#
#-------------------------------------------------------------------------------
#"""Version = 2022-01-06 20:30"""
#-------------------------------------------------------------------------------
import GameWorld
import ChConfig
import PlayerCrossBattlefield
#------------------------------------------------------------------------------
## 执行逻辑
#  @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):
    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_Battlefield, 0)
    msgData = eval(funResult)
    playerID = curPlayer.GetPlayerID()
    GameWorld.Log("GameServer_CrossBattlefield_DoResult msgData=%s" % (msgData), playerID)
    PlayerCrossBattlefield.GameServer_CrossBattlefield_DoResult(curPlayer, msgData)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -101,6 +101,8 @@
g_crossPlayerItemsChangeInfo = {} #跨服玩家物品变化信息 {playerID:{"背包类型-物品位":itemMD5, ...}, ...}
g_crossPlayerSkillsChangeInfo = {} #跨服玩家技能变化信息 {playerID:[技能ID], ...}
g_crossBattlefieldCallTeamInfo = {} # 跨服战场召集队伍信息 {zoneID:{hmNum:{playerID:[召集队伍玩家ID列表], ...}, ...}, ...}
g_ZhuXianBossPlayerHurtDict = {} #诛仙BOSS玩家伤害排行信息
g_Qudao_DoubleBill = {} # 渠道删档充值返利