hxp
2024-10-17 f1c2ac9588321aad9e075c1440b52eb155b8ba18
10263 【越南】【英文】后端支持NPC仿真实玩家战斗和快速战斗

# Conflicts:
# PySysDB/PySysDBPY.h
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
# ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
88个文件已修改
3个文件已添加
2656 ■■■■■ 已修改文件
PySysDB/PySysDBPY.h 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py 449 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py 841 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py 113 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py 111 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py 98 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
PySysDB/PySysDBPY.h
@@ -69,6 +69,14 @@
    list        StarAttrValue;    //累计总属性值
};
//技能搭配表
struct    tagSkillMatch
{
    BYTE        _IDIndex;
    list        Skills;    //主动技能(职业1id|职业2id)
};
//创角表
struct    tagCreateRole
ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -440,6 +440,12 @@
Def_PlayerKey_PrivateTalk = 'PrivateTalk'          #追踪密频(1:开启 0:关闭)
#---------------------------------------------------------------------
# GameWorld.GetGameWorld().SendDBLogic(BYTE queryType, DWORD id, char* sData, int nDataLen)
# GameServer请求db逻辑类型,db接受 OnGameServerToDBLogic,回调 RecvDGDBLogicResult
(
gstDBLogic_PlayerPackData, # 请求打包玩家数据 0
) = range(1)
#GM工具回复值
Def_GMTool_Succeed = 1
Def_GMTool_Fail = 250
@@ -448,7 +454,7 @@
Def_Billboard_MaxCnt = 100
#---------------------------------------------------------------------
#请求类型(需要和MapServer中的一致)
Def_QueryType_Count = 56
Def_QueryType_Count = 55
(
queryType_sqtPlayer,                  #查询玩家
queryType_sqtFamilyWar,               #家族战
@@ -505,7 +511,6 @@
queryType_EnterFB,                    #进入副本
queryType_NPCInfo,                    #查询NPCInfo
queryType_NPCCnt,                     #查询NPC数量
queryType_MirrorPlayer,               #镜像玩家
) = range(0, Def_QueryType_Count)
#------------------------------------------------------------------------------ 
#家族某行为类型保存的条数
ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py
@@ -719,6 +719,11 @@
    ItemData19 = ""    #(String ItemData19)
    ItemDataSize20 = 0    #(WORD ItemDataSize20)
    ItemData20 = ""    #(String ItemData20)
    PackDataSyncState = 0    #(BYTE PackDataSyncState)// 打包数据同步状态: 0-不同步;个位-是否同步本服;十位-是否同步跨服
    PackDataLen = 0    #(DWORD PackDataLen)
    PackData = ""    #(String PackData)
    PackMsgLen = 0    #(WORD PackMsgLen)
    PackMsg = ""    #(String PackMsg)
    data = None
    def __init__(self):
@@ -778,6 +783,11 @@
        self.ItemData19,_pos = CommFunc.ReadString(_lpData, _pos,self.ItemDataSize19)
        self.ItemDataSize20,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.ItemData20,_pos = CommFunc.ReadString(_lpData, _pos,self.ItemDataSize20)
        self.PackDataSyncState,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.PackDataLen,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.PackData,_pos = CommFunc.ReadString(_lpData, _pos,self.PackDataLen)
        self.PackMsgLen,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.PackMsg,_pos = CommFunc.ReadString(_lpData, _pos,self.PackMsgLen)
        return _pos
    def Clear(self):
@@ -833,6 +843,11 @@
        self.ItemData19 = ""
        self.ItemDataSize20 = 0
        self.ItemData20 = ""
        self.PackDataSyncState = 0
        self.PackDataLen = 0
        self.PackData = ""
        self.PackMsgLen = 0
        self.PackMsg = ""
        return
    def GetLength(self):
@@ -886,6 +901,11 @@
        length += len(self.ItemData19)
        length += 2
        length += len(self.ItemData20)
        length += 1
        length += 4
        length += len(self.PackData)
        length += 2
        length += len(self.PackMsg)
        return length
@@ -940,6 +960,11 @@
        data = CommFunc.WriteString(data, self.ItemDataSize19, self.ItemData19)
        data = CommFunc.WriteWORD(data, self.ItemDataSize20)
        data = CommFunc.WriteString(data, self.ItemDataSize20, self.ItemData20)
        data = CommFunc.WriteBYTE(data, self.PackDataSyncState)
        data = CommFunc.WriteDWORD(data, self.PackDataLen)
        data = CommFunc.WriteString(data, self.PackDataLen, self.PackData)
        data = CommFunc.WriteWORD(data, self.PackMsgLen)
        data = CommFunc.WriteString(data, self.PackMsgLen, self.PackMsg)
        return data
    def OutputString(self):
@@ -992,7 +1017,12 @@
                                ItemDataSize19:%d,
                                ItemData19:%s,
                                ItemDataSize20:%d,
                                ItemData20:%s
                                ItemData20:%s,
                                PackDataSyncState:%d,
                                PackDataLen:%d,
                                PackData:%s,
                                PackMsgLen:%d,
                                PackMsg:%s
                                '''\
                                %(
                                self.Head.OutputString(),
@@ -1043,7 +1073,12 @@
                                self.ItemDataSize19,
                                self.ItemData19,
                                self.ItemDataSize20,
                                self.ItemData20
                                self.ItemData20,
                                self.PackDataSyncState,
                                self.PackDataLen,
                                self.PackData,
                                self.PackMsgLen,
                                self.PackMsg
                                )
        return DumpString
ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
@@ -5887,6 +5887,54 @@
#------------------------------------------------------
# A1 09 同步打包玩家数据 #tagCMSycnPlayerPackData
class  tagCMSycnPlayerPackData(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xA1
        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 = 0xA1
        self.SubCmd = 0x09
        return
    def GetLength(self):
        return sizeof(tagCMSycnPlayerPackData)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// A1 09 同步打包玩家数据 //tagCMSycnPlayerPackData:
                                Cmd:%s,
                                SubCmd:%s
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd
                                )
        return DumpString
m_NAtagCMSycnPlayerPackData=tagCMSycnPlayerPackData()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMSycnPlayerPackData.Cmd,m_NAtagCMSycnPlayerPackData.SubCmd))] = m_NAtagCMSycnPlayerPackData
#------------------------------------------------------
#A1 03 设置是否成年 #tagCMAdult
class  tagCMAdult(Structure):
@@ -21070,6 +21118,70 @@
#------------------------------------------------------
# B4 11 镜像战斗 #tagCMMirrorFight
class  tagCMMirrorFight(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("MapID", c_int),    # 自定义地图ID,如竞技场等
                  ("FuncLineID", c_ushort),
                  ("TagPlayeID", c_int),    # 目标玩家ID,支持跨服玩家ID
                  ("CmdType", c_ubyte),    # 命令类型: 0-创建战斗;1-开始战斗;2-战斗中跳过;3-不创建战斗直接得结果
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB4
        self.SubCmd = 0x11
        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 = 0xB4
        self.SubCmd = 0x11
        self.MapID = 0
        self.FuncLineID = 0
        self.TagPlayeID = 0
        self.CmdType = 0
        return
    def GetLength(self):
        return sizeof(tagCMMirrorFight)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 11 镜像战斗 //tagCMMirrorFight:
                                Cmd:%s,
                                SubCmd:%s,
                                MapID:%d,
                                FuncLineID:%d,
                                TagPlayeID:%d,
                                CmdType:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.MapID,
                                self.FuncLineID,
                                self.TagPlayeID,
                                self.CmdType
                                )
        return DumpString
m_NAtagCMMirrorFight=tagCMMirrorFight()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMMirrorFight.Cmd,m_NAtagCMMirrorFight.SubCmd))] = m_NAtagCMMirrorFight
#------------------------------------------------------
# B4 0F 回收私有专属木桩怪 #tagCMRecyclePriWoodPile
class  tagCMRecyclePriWoodPile(Structure):
ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
@@ -421,6 +421,31 @@
    ## 服务器组ID,必须唯一,代表这台物理服务器
    return ToIntDef(ReadChConfig.GetPyMongoConfig("platform", "GroupID"), 0)
def GetMainServerID(serverID):
    ## 获取服务器ID所属主服ID
    ServerIDMainServerDict = IpyGameDataPY.GetConfigEx("ServerIDMainServerDict")
    if ServerIDMainServerDict == None:
        filePath = ChConfig.GetDBPath() + ("\\MixServerMap_%s.json" % GetPlatform())
        if not os.path.isfile(filePath):
            SendGameErrorEx("GetMainServerIDError", "file can not found. %s" % filePath)
        else:
            fileObj = open(filePath, 'rb')
            content = fileObj.read()
            fileObj.close()
            MixServerMapDict = eval(content)
            ServerIDMainServerDict = {}
            for mainServerIDStr, serverIDList in MixServerMapDict.items():
                mainServerID = int(mainServerIDStr)
                for sID in serverIDList:
                    ServerIDMainServerDict[sID] = mainServerID
            IpyGameDataPY.SetConfigEx("ServerIDMainServerDict", ServerIDMainServerDict)
            Log("加载 ServerIDMainServerDict=%s" % ServerIDMainServerDict)
    if not ServerIDMainServerDict:
        return serverID
    return ServerIDMainServerDict.get(serverID, serverID)
def GetPlatformServerNum(platform):
    # 获取服务器的平台编号
    platformNumDict = ReadChConfig.GetDBEvalChConfig("DBPlatformNum")
@@ -460,7 +485,7 @@
def GetPlayerServerID(curPlayer): return GetAccIDServerID(curPlayer.GetAccID())
def GetAccIDServerID(accID):
    infoList = accID.split(Def_AccID_Split_Sign)
    return 0 if len(infoList) < 3 else int(infoList[-1][1:])
    return 0 if len(infoList) < 3 else ToIntDef(infoList[-1][1:])
def GetPlayerServerSID(curPlayer):
    # 返回含s的serverID
@@ -1292,6 +1317,14 @@
        SendGameError("GameServerRaiseException", errorMsg)
    return
def SendGameErrorEx(errType, msgInfo="", playerID=0):
    ErrLog("SendGameErrorEx: %s -> %s" % (errType, msgInfo), playerID)
    if GetGameWorld().GetDebugLevel():
        raise Exception("%s -> %s" % (errType, msgInfo))
    else:
        SendGameError(errType, msgInfo)
    return
def SendGameError(errType, msgInfo=""):
    ''' 向运维发送邮件,用于需要紧急处理的信息
    @param errType: 错误类型,自定义即可
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
@@ -19,6 +19,7 @@
import ShareDefine
import PlayerAssist
import PlayerControl
import PlayerPackData
import PlayerFuncTeam
import CrossLuckyCloudBuy
import IPY_GameServer
@@ -123,6 +124,12 @@
            
        elif msgType == ShareDefine.ClientServerMsg_ViewPlayerCache:
            CrossRealmPlayer.ClientServerMsg_ViewPlayerCache(serverGroupID, msgData)
        elif msgType == ShareDefine.ClientServerMsg_PullOtherPlayerPackData:
            PlayerPackData.ClientServerMsg_PullOtherPlayerPackData(serverGroupID, msgData)
        elif msgType == ShareDefine.ClientServerMsg_PlayerPackData:
            PlayerPackData.ClientServerMsg_PlayerPackData(serverGroupID, msgData)
            
        elif msgType == ShareDefine.ClientServerMsg_QueryNPCInfo:
            PlayerQuery.ClientServerMsg_QueryNPCInfo(serverGroupID, msgData)
@@ -357,6 +364,15 @@
        elif msgType == ShareDefine.CrossServerMsg_ViewPlayerCacheRet:
            CrossRealmPlayer.CrossServerMsg_ViewPlayerCacheRet(msgData, tick)
            
        elif msgType == ShareDefine.CrossServerMsg_PlayerPackDataState:
            PlayerPackData.CrossServerMsg_PlayerPackDataState(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PullPlayerPackData:
            PlayerPackData.CrossServerMsg_PullPlayerPackData(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PushPlayerPackData:
            PlayerPackData.CrossServerMsg_PushPlayerPackData(msgData)
        elif msgType == ShareDefine.CrossServerMsg_PKMatchReqRet:
            CrossRealmPK.CrossServerMsg_PKMatchReqRet(msgData)
            
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py
@@ -6,7 +6,7 @@
#
# @todo:GameServer向DB请求的回复信息
import GameWorld
import PlayerPackData
import IPY_GameServer
import ChConfig
@@ -35,16 +35,12 @@
    if result == 0:
        return
    queryType = dbResultPack.GetQueryType()
    if queryType == 0:
        data = eval(dbResultPack.GetData())
        playerID = data['id'] #发起请求的玩家ID
        mapID = data['mapid']
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not curPlayer:
            return
    if queryType == ChConfig.gstDBLogic_PlayerPackData:
        mirrorID = dbResultPack.GetID()
        playerData = dbResultPack.GetResultSet()
        msgInfo = eval(dbResultPack.GetData())
        PlayerPackData.OnDBPlayerPackData(mirrorID, playerData, msgInfo)
        
        data['playerData'] = dbResultPack.GetResultSet()
        dateStr = str(data)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, ChConfig.queryType_MirrorPlayer, playerID, mapID,
                        "PlayerMirror", dateStr, len(dateStr), curPlayer.GetRouteServerIndex())
    return
    return
ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -78,6 +78,7 @@
import PlayerFuncTeam
import PyDataManager
import GameWorldMineArea
import PlayerPackData
import PlayerRecData
import GameWorship
import GameXiangong
@@ -296,7 +297,7 @@
        CrossYaomoBoss.OnPlayerLogin(curPlayer)
        #玩家记录
        PlayerRecData.OnPlayerLogin(curPlayer)
        PlayerPackData.OnPlayerLogin(curPlayer)
        #在线状态变更,放最后
        __OnPlayerOnlineStateChange(curPlayer, True)
        
@@ -700,11 +701,15 @@
    cacheBase = msgData["cacheBase"]
    
    isLogout = not isOnline
    olMgr = GetOnlinePlayerMgr()
    olMgr.SetOnlineState(playerID, isOnline, cacheBase.get("ServerGroupID", 0))
    PlayerViewCache.UpdCrossCacheBase(playerID, cacheBase, isLogout)
    serverID = GameWorld.GetAccIDServerID(cacheBase["AccID"])
    
    # 上线
    if isOnline:
        PlayerPackData.OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID)
        GameXiangong.OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID)
        
    # 下线
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
New file
@@ -0,0 +1,449 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package PlayerPackData
#
# @todo:玩家打包数据
# @author hxp
# @date 2024-10-17
# @version 1.0
#
# 详细描述: 玩家打包数据
#
# 应用场景:主要用于PK类功能,与镜像PK
#    1. 既定功能类型:
#            如匹配竞技场等,这种一般需要先报名或登记等,或者已经在积分排行榜上的
#            所以打包数据表一般会保存玩家打包数据直到活动结算结束
#            报名、登记、同步: 这种一般是通过玩家主动自主发起的行为
#            排行榜:这种相当于被动式报名,即首次上榜触发拉取数据
#    2. 系统自动PK功能类:
#            如根据某个资格规则,系统自动生成n个玩家随机匹配PK的功能,玩家两两镜像系统自动对打晋级加积分决出名次等
#            这种为被动式,即目标玩家可能不存在打包数据表中,需要取拉取
#    3. 随意PK类型:
#            如切磋一下,玩家可以在任意场景对任意本服或跨服玩家发起切磋,与其镜像进行一场友谊PK,纯娱乐
#            这种为被动式,即目标玩家可能不存在打包数据表中,需要取拉取
#
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import CommFunc
import GameWorld
import PyDataManager
import PlayerViewCache
import PyGameDataStruct
import CrossRealmMsg
import PlayerControl
import ShareDefine
import PyGameData
import ChConfig
import time
class DBPlayerPackDataManager():
    ## 玩家打包数据管理
    def __init__(self):
        self.Clear()
        return
    def Clear(self):
        self.playerPackDataDict = {} # {playerID:tagDBPlayerPackData, ...}
        return
    def GetPlayerPackObj(self, playerID, isAddNew=False):
        packDataObj = None
        if playerID in self.playerPackDataDict:
            packDataObj = self.playerPackDataDict[playerID]
        elif isAddNew:
            packDataObj = PyGameDataStruct.tagDBPlayerPackData()
            packDataObj.PlayerID = playerID
            self.playerPackDataDict[playerID] = packDataObj
        return packDataObj
    def UpdPlayerPackData(self, playerID, packData):
        if not packData:
            return
        packObj = self.GetPlayerPackObj(playerID, True)
        packObj.UpdTime = int(time.time())
        packObj.PackData = packData
        packObj.PackDataSize = len(packObj.PackData)
        return
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for dbData in self.playerPackDataDict.values():
            cnt += 1
            savaData += dbData.getBuffer()
        GameWorld.Log("Save DBPlayerPackData count :%s len=%s" % (cnt, len(savaData)))
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load DBPlayerPackData count :%s" % cnt)
        self.Clear()
        for _ in xrange(cnt):
            dbData = PyGameDataStruct.tagDBPlayerPackData()
            pos += dbData.readData(datas, pos, dataslen)
            self.playerPackDataDict[dbData.PlayerID] = dbData
        return pos
def IsSaveDB(packDataObj):
    ## 是否入库
    if not packDataObj:
        return False
    # 功能固定需要的
    # ...
    maxDays = 7 # 默认7天
    MaxTime = maxDays * 3600 * 24
    curTime = int(time.time())
    passTime = curTime - packDataObj.UpdTime
    if passTime < MaxTime:
        return True
    return False
def DelOutofTimePackData():
    ## 删除过期
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    playerPackDataDict = packDataMgr.playerPackDataDict
    for playerID, packDataObj in playerPackDataDict.items():
        if IsSaveDB(packDataObj):
            continue
        playerPackDataDict.pop(playerID)
    return
def IsPackDataPlayer(playerID):
    return playerID in PyDataManager.GetDBPlayerPackDataManager().playerPackDataDict
def OnPlayerLogin(curPlayer):
    ## 本服登录逻辑
    playerID = curPlayer.GetPlayerID()
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    if playerID in packDataMgr.playerPackDataDict:
        isCross, isNeed = 0, 1
        QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", [isCross, isNeed])
    return
def OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID):
    ## 跨服登录逻辑
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    if playerID in packDataMgr.playerPackDataDict:
        dataMsg = {"playerID":playerID}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PlayerPackDataState, dataMsg, [serverGroupID])
    return
def SetNeedPackData(playerIDList):
    ## 设置需要打包数据的玩家ID列表,如果没有数据,则会主动去拉取数据,跨服本服通用
    if not playerIDList:
        return
    pullPlayerIDList = []
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in playerIDList:
        if playerID in packDataMgr.playerPackDataDict:
            continue
        pullPlayerIDList.append(playerID)
    if not pullPlayerIDList:
        return
    OnPullPlayerPackData(pullPlayerIDList)
    return
def OnPullPlayerPackData(pullPlayerIDList, msgInfo=None):
    '''请求拉取玩家数据,该函数仅在打包数据表中不存在玩家数据时才需要调用,跨服本服通用
    1. 跨服拉子服玩家数据: 直接推送给子服拉对应玩家数据,拉取到数据后同时存入打包数据表中
    2. 子服拉其他子服玩家数据:直接推送给跨服服务器拉取
    3. 子服拉本服玩家数据
        a.如果是跨服服务器请求的,优先取打包数据表中数据,有则返回,没有则继续往下走
        b.玩家在线时从地图拉取,拉取到数据后同时存入打包数据表中
        c.玩家离线时从db拉取,拉取到数据后同时存入打包数据表中
    @param msgInfo: 功能拉取玩家数据时自定义的功能信息,透传参数
    '''
    if msgInfo == None:
        msgInfo = {}
    GameWorld.DebugLog("拉取玩家打包数据: %s, %s" % (pullPlayerIDList, msgInfo))
    # pullFrom 0-跨服拉子服; >0-子服通过跨服拉子服
    if GameWorld.IsCrossServer():
        # 广播给子服拉数据
        msgInfo["pullFrom"] = 0
        dataMsg = {"pullPlayerIDList":pullPlayerIDList, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerPackData, dataMsg)
    else:
        msgInfo["pullFrom"] = GameWorld.GetServerGroupID()
        otherServerPlayerIDList = [] # 其他服玩家ID
        for playerID in pullPlayerIDList:
            if PlayerControl.GetDBPlayerAccIDByID(playerID):
                DoPullPlayerPackData(playerID, msgInfo) # 拉本服
            else:
                otherServerPlayerIDList.append(playerID)
        # 拉其他服玩家,通过跨服拉
        if otherServerPlayerIDList:
            dataMsg = {"pullPlayerIDList":otherServerPlayerIDList, "msgInfo":msgInfo}
            CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PullOtherPlayerPackData, dataMsg)
    return
def DoPullPlayerPackData(playerID, msgInfo):
    ## 子服执行拉取本服某个玩家数据,只处理属于本服的玩家
    # @param pullFrom: 0-跨服拉; >0-子服拉
    if not PlayerControl.GetDBPlayerAccIDByID(playerID):
        # 非本服玩家
        return
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        GameWorld.DebugLog("玩家不在线的调用打包db数据! playerID=%s" % (playerID), playerID)
        data = str(msgInfo)
        GameWorld.GetGameWorld().SendDBLogic(ChConfig.gstDBLogic_PlayerPackData, playerID, data, len(data))
        return
    GameWorld.DebugLog("玩家在线的发给地图打包数据! playerID=%s" % (playerID), playerID)
    # 在线的转发给地图
    QueryPlayerResult_PlayerMirror(curPlayer, "PullPlayerPackData", msgInfo)
    return
def ReuestPlayerPackDataRet(msgInfo, packMirrorID=0, packData=""):
    '''请求玩家打包数据汇总,如同时请求多个的话会逐一进行汇总整合,直到所有请求的玩家数据均有后再统一执行之后的逻辑
    '''
    requestID = msgInfo.get("requestID") # 可以是玩家ID,或者是系统自定义的请求ID
    if requestID not in PyGameData.g_requestPlayerPackDataInfo:
        GameWorld.DebugLog("不存在该[玩家打包数据]请求! requestID=%s,packMirrorID=%s,%s" % (requestID, packMirrorID, msgInfo))
        return
    recInfo = PyGameData.g_requestPlayerPackDataInfo[requestID] # 记录的汇总信息
    recMsgInfo = recInfo.get("msgInfo", {})
    packDataDict = recInfo.get("packDataDict", {})
    if msgInfo.get("requestTime") != recMsgInfo.get("requestTime"):
        # 因为请求的数据有一定的延迟,收到回调汇总后可能已经不是之前请求的了,验证不通过则不处理
        # 暂以请求时间戳作为验证依据即可,同一秒内不可重复发起请求,所以单秒内一定是唯一的
        return
    mirrorIDList = msgInfo["mirrorIDList"]
    if packMirrorID:
        if packMirrorID not in mirrorIDList:
            # 不在需要的请求ID里不处理,一般不可能,仅作为防范验证
            return
        if packMirrorID not in packDataDict:
            packDataDict[packMirrorID] = packData
    for mirrorID in mirrorIDList:
        if mirrorID not in packDataDict:
            GameWorld.DebugLog("还有目标玩家没有数据,稍等后处理! requestID=%s,mirrorID=%s,mirrorIDList=%s,packIDList=%s"
                               % (requestID, mirrorID, mirrorIDList, packDataDict.keys()))
            return
    GameWorld.DebugLog("汇总玩家打包数据完毕: requestID=%s,packMirrorID=%s, %s" % (requestID, packMirrorID, msgInfo))
    PyGameData.g_requestPlayerPackDataInfo.pop(requestID, None) # 汇总完毕,清除汇总信息
    msgType = msgInfo.get("msgType")
    # 镜像战斗
    if msgType == "MirrorBattle":
        playerID = msgInfo.get("playerID", 0)
        # 玩家发起的
        if playerID:
            playerID = msgInfo["playerID"]
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not curPlayer:
                return
            tagMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
            routeIndex = curPlayer.GetRouteServerIndex()
        else:
            tagMapID = msgInfo.get("requestMapID", 0)
            routeIndex = -1
        sendMsg = str([msgInfo, packDataDict])
        GameWorld.DebugLog("MapServer_QueryPlayer tagMapID=%s,len=%s" % (tagMapID, len(sendMsg)), playerID)
        GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, playerID, tagMapID, "PlayerMirror", sendMsg, len(sendMsg), routeIndex)
    # 其他功能可再扩展
    else:
        pass
    return
def ClientServerMsg_PlayerPackData(serverGroupID, msgData):
    ## 收到子服同步的玩家打包数据
    playerID = msgData["playerID"]
    packData = msgData["packData"]
    cacheBase = msgData.get("cacheBase", {})
    if cacheBase:
        PlayerViewCache.UpdCrossCacheBase(playerID, cacheBase)
    PyDataManager.GetDBPlayerPackDataManager().UpdPlayerPackData(playerID, packData)
    msgInfo = msgData.get("msgInfo", {})
    if not msgInfo:
        return
    pullFrom = msgInfo.get("pullFrom")
    # 跨服自身需要的
    if pullFrom == 0:
        ReuestPlayerPackDataRet(msgInfo, playerID, packData)
    # 其他子服发起的,推给目标子服
    elif pullFrom > 0:
        tagServerGroupID = pullFrom
        dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PushPlayerPackData, dataMsg, [tagServerGroupID])
    return
def ClientServerMsg_PullOtherPlayerPackData(serverGroupID, msgData):
    ## 收到子服请求拉取玩家打包数据
    msgInfo = msgData["msgInfo"]
    pullPlayerIDList = msgData["pullPlayerIDList"]
    otherServerPlayerIDList = []
    packDataDict = {}
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in pullPlayerIDList:
        packObj = packDataMgr.GetPlayerPackObj(playerID)
        # 已经有的数据先推送回去
        if packObj:
            packDataDict[playerID] = packObj.PackData
            dataMsg = {"playerID":playerID, "packData":packObj.PackData, "msgInfo":msgInfo}
            CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PushPlayerPackData, dataMsg, [serverGroupID])
        else:
            otherServerPlayerIDList.append(playerID)
    # 还没有数据的,广播给其他子服拉数据
    if otherServerPlayerIDList:
        dataMsg = {"pullPlayerIDList":otherServerPlayerIDList, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PullPlayerPackData, dataMsg)
    return
##-------------------------------------------------------------------------------------------------
def CrossServerMsg_PlayerPackDataState(msgData):
    ## 收到跨服服务器玩家打包数据状态同步
    playerID = msgData["playerID"]
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    isCross, isNeed = 1, 1
    QueryPlayerResult_PlayerMirror(curPlayer, "PackDataSyncState", [isCross, isNeed])
    return
def CrossServerMsg_PullPlayerPackData(msgData):
    ## 收到跨服服务器拉取玩家打包数据
    msgInfo = msgData["msgInfo"]
    pullPlayerIDList = msgData["pullPlayerIDList"]
    needPullPlayerIDList = []
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    for playerID in pullPlayerIDList:
        packObj = packDataMgr.GetPlayerPackObj(playerID)
        if packObj:
            # 本服有数据,直接推给跨服
            dataMsg = {"playerID":playerID, "packData":packObj.PackData, "msgInfo":msgInfo}
            CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
        else:
            needPullPlayerIDList.append(playerID)
    DoPullPlayerPackData(needPullPlayerIDList, msgInfo)
    return
def CrossServerMsg_PushPlayerPackData(msgData):
    ## 收到跨服服务器推送的玩家打包数据
    playerID = msgData["playerID"]
    packData = msgData["packData"]
    msgInfo = msgData["msgInfo"]
    ReuestPlayerPackDataRet(msgInfo, playerID, packData)
    return
def QueryPlayerResult_PlayerMirror(curPlayer, msgType, msgData=None):
    sysMsg = str([msgType, msgData])
    curPlayer.MapServer_QueryPlayerResult(0, 0, "PlayerMirror", sysMsg, len(sysMsg))
    return
def OnMGUpdatePlayerPackData(curPlayer, curPackData):
    ## 地图同步更新的玩家打包数据
    if GameWorld.IsCrossServer():
        return
    playerID = curPackData.PlayerID
    packDataSyncState = curPackData.PackDataSyncState
    packData = curPackData.PackData
    if not packDataSyncState or not packData:
        return
    msgInfo = eval(curPackData.PackMsg) if curPackData.PackMsg else {} # 打包数据附带的信息
    # 本服需要,先更新数据
    if packDataSyncState % 10:
        PyDataManager.GetDBPlayerPackDataManager().UpdPlayerPackData(playerID, packData)
    # 跨服需要,同步给跨服,由跨服服务器再进一步处理
    if packDataSyncState / 10:
        cacheBase = PlayerViewCache.GetSyncCrossCacheBase(curPlayer) if curPlayer else {}
        dataMsg = {"playerID":playerID, "packData":packData, "cacheBase":cacheBase, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
    # 本服需要的功能
    pullFrom = msgInfo.get("pullFrom")
    if pullFrom > 0 and pullFrom == GameWorld.GetServerGroupID():
        ReuestPlayerPackDataRet(msgInfo, playerID, packData)
    return
def OnDBPlayerPackData(playerID, packData, msgInfo):
    ## 收到db打包玩家数据回调
    GameWorld.DebugLog("收到db打包玩家数据回调: playerID=%s, %s" % (playerID, msgInfo))
    pullFrom = msgInfo.get("pullFrom")
    # 跨服需要,同步给跨服,由跨服服务器再进一步处理
    if pullFrom == 0 or (pullFrom > 0 and pullFrom != GameWorld.GetServerGroupID()):
        dataMsg = {"playerID":playerID, "packData":packData, "msgInfo":msgInfo}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_PlayerPackData, dataMsg)
        return
    # 本服需要,汇总结果
    ReuestPlayerPackDataRet(msgInfo, playerID, packData)
    return
def OnMGReuestPlayerPackData(msgInfo):
    ## 地图请求需要玩家打包数据信息
    #"msgType":"MirrorBattle", "msgData":msgData, "mirrorIDList":mirrorIDList, "requestTime":requestTime, "requestID":playerID, "playerID":playerID
    packDataDict = {}
    pullPlayerIDList = []
    packDataMgr = PyDataManager.GetDBPlayerPackDataManager()
    requestID = msgInfo["requestID"]
    mirrorIDList = msgInfo["mirrorIDList"]
    for mirrorID in mirrorIDList:
        packObj = packDataMgr.GetPlayerPackObj(mirrorID)
        if packObj:
            packDataDict[mirrorID] = packObj.PackData
            continue
        pullPlayerIDList.append(mirrorID)
    # 每次请求直接覆盖,由地图控制请求限制
    PyGameData.g_requestPlayerPackDataInfo[requestID] = {"msgInfo":msgInfo, "packDataDict":packDataDict}
    # 还有玩家没有缓存打包数据,需要先完全拉取后再同步给地图
    if pullPlayerIDList:
        OnPullPlayerPackData(pullPlayerIDList, msgInfo)
        return
    ReuestPlayerPackDataRet(msgInfo)
    return
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -44,6 +44,7 @@
import GameWorldOpenServerCampaign
import ShareDefine
import GameDataRecord
import PlayerPackData
import PlayerCompensation
import PlayerFB
import UpdatePlayerName
@@ -281,10 +282,6 @@
    # 查询地图NPC数量
    elif queryType == ChConfig.queryType_NPCCnt:
        __QueryMapNPCCntInfo(curPlayer, queryCallName, sendCMD, tick)
        return
    elif queryType == ChConfig.queryType_MirrorPlayer:
        data = str({"id" : curPlayer.GetPlayerID(), "mapid" : GameWorld.GetQueryPlayerMapID(curPlayer)})
        GameWorld.GetGameWorld().SendDBLogic(0, queryID, data, len(data))
        return
    else:
        GameWorld.ErrLog('unKnow queryType = %s' % (queryType))
@@ -643,6 +640,11 @@
        GameDataRecord.ChangeCoinCnt(eval(resultName))
        return
    
    #请求玩家的打包数据
    if callName == "ReuestPlayerPackData":
        PlayerPackData.OnMGReuestPlayerPackData(eval(resultName))
        return
    #跨服排位PK战斗结算
    if callName == "CrossChampionshipPKOver":
        CrossChampionship.MapServer_CrossChampionshipPKOver(eval(resultName), tick)
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -32,6 +32,7 @@
import CrossBattlefield
import CrossRealmPlayer
import PyGameDataStruct
import PlayerPackData
import IpyGameDataPY
import PyDataManager
import CrossRealmPK
@@ -75,6 +76,9 @@
        return True
    
    if GameWorldSkyTower.IsSkyTowerPassPlayer(playerID):
        return True
    if PlayerPackData.IsPackDataPlayer(playerID):
        return True
    
    #跨服榜单上的默认保留
@@ -124,6 +128,8 @@
def DelOutofTimeViewCacheData():
    ## 删除过期的查看缓存数据
    PlayerPackData.DelOutofTimePackData()
    
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
@@ -249,9 +255,6 @@
def UpdCrossCacheBase(playerID, cacheBase, isLogout=False):
    ## 更新同步跨服基础查看缓存
    #更新跨服在线状态,只要有同步即视为在线,除了指定是Logout的
    olMgr = ChPlayer.GetOnlinePlayerMgr()
    olMgr.SetOnlineState(playerID, not isLogout, cacheBase.get("ServerGroupID", 0))
    curCache = FindViewCache(playerID, True)
    if not curCache:
        return {}
@@ -286,6 +289,11 @@
#    ...         ...
#    WORD        ItemDataSize20;
#    char        ItemData20[ItemDataSize20];
#    BYTE        PackDataSyncState;    // 打包数据同步状态: 0-不同步;个位-是否同步本服;十位-是否同步跨服
#    DWORD       PackDataLen;
#    char        PackData[PackDataLen];
#    WORD        PackMsgLen;
#    char        PackMsg[PackMsgLen];
#};
def OnMGUpdatePlayerCache(routeIndex, mapID, curPackData, tick):
    playerID = curPackData.PlayerID
@@ -323,12 +331,15 @@
        setattr(curCache, "ItemDataSize%s" % classLV, itemDataSize)
        #GameWorld.DebugLog("    更新Item数据: classLV=%s,size=%s, %s" % (classLV, itemDataSize, itemData), playerID)
        
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    # 在可能删除之前执行打包数据相关逻辑
    PlayerPackData.OnMGUpdatePlayerPackData(curPlayer, curPackData)
    if isLogout:
        #不需要保存离线数据的,直接删除缓存数据
        if not IsSaveDBViewCache(curCache):
            DeleteViewCache(playerID)
            return
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if curPlayer:
            curCache.GeTuiID = curPlayer.GetGeTuiClientID()
            curCache.GeTuiIDSize = len(curCache.GeTuiID)
ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
@@ -30,6 +30,7 @@
import PlayerRecData
import GameWorldMineArea
import PyGameDataStruct
import PlayerPackData
import PlayerFuncTeam
import IpyGameDataPY
import PlayerCharm
@@ -316,6 +317,7 @@
class PyGameDataManager(object):
    def __init__(self):
        self.DBPlayerPackDataManager = PlayerPackData.DBPlayerPackDataManager()
        self.DBGameRecDataManager = GameRecData.DBGameRecDataManager()
        self.DBPyFuncTeamManager = PlayerFuncTeam.DBPyFuncTeamManager()
        self.DBPyFuncTeamMemManager = PlayerFuncTeam.DBPyFuncTeamMemManager()
@@ -352,6 +354,7 @@
    def GetSaveData(self):
        buff = ""
        buff += self.DBPlayerPackDataManager.GetSaveData()
        buff += self.DBGameRecDataManager.GetSaveData()
        buff += self.DBPyFuncTeamManager.GetSaveData()
        buff += self.DBPyFuncTeamMemManager.GetSaveData()
@@ -387,6 +390,7 @@
        return buff
    
    def LoadGameData(self, gameBuffer, pos):
        pos = self.DBPlayerPackDataManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
        pos = self.DBGameRecDataManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
        pos = self.DBPyFuncTeamManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
        pos = self.DBPyFuncTeamMemManager.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
@@ -514,6 +518,11 @@
    pyGameDataMgr = GetPyGameDataManager()
    return pyGameDataMgr.DBGameRecDataManager
def GetDBPlayerPackDataManager():
    # 玩家打包数据管理
    pyGameDataMgr = GetPyGameDataManager()
    return pyGameDataMgr.DBPlayerPackDataManager
def GetDBPyFuncTeamManager():
    # 功能队伍管理
    pyGameDataMgr = GetPyGameDataManager()
ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -160,6 +160,8 @@
g_crossActAllRechargeInfo = {} # 跨服全民充值信息,本服用 {zoneID:{playerID:总充值, ...}, ...}
g_crossYaomoBossHurtInfo = {} # 跨服妖魔boss伤害信息,本服用 {playerID:hurtTotal, ...}
g_requestPlayerPackDataInfo = {} # 请求玩家打包数据汇总信息 {requestID:{"msgInfo":msgInfo, "packDataDict":packDataDict}, ...}
g_familyTalkCache = {} #{familyID:[[time,content,extras],..]}
g_worldTalkCache = [] #[[time,name, playerID, content,extras],..]
ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
@@ -3538,3 +3538,70 @@
            )
        return output
# 玩家数据打包表 #tagDBPlayerPackData
class tagDBPlayerPackData(Structure):
    _pack_ = 1
    _fields_ = [
        ('PlayerID', ctypes.c_ulong),
        ('UpdTime', ctypes.c_ulong),
        ('PackDataSize', ctypes.c_ulong),
        ('PackData', ctypes.c_char_p),
        ('ADOResult', ctypes.c_ulong),
    ]
    def __init__(self):
        Structure.__init__(self)
        self.clear()
    def clear(self):
        self.PlayerID = 0
        self.UpdTime = 0
        self.PackDataSize = 0
        self.PackData = ''
    def readData(self, buf, pos = 0, length = 0):
        if not pos <= length:
            return -1
        if len(buf) < pos + self.getLength():
            return -1
        self.clear()
        self.PlayerID, pos = CommFunc.ReadDWORD(buf, pos)
        self.UpdTime, pos = CommFunc.ReadDWORD(buf, pos)
        self.PackDataSize, pos = CommFunc.ReadDWORD(buf, pos)
        tmp, pos = CommFunc.ReadString(buf, pos, self.PackDataSize)
        self.PackData = ctypes.c_char_p(tmp)
        return self.getLength()
    def getBuffer(self):
        buf = ''
        buf = CommFunc.WriteDWORD(buf, self.PlayerID)
        buf = CommFunc.WriteDWORD(buf, self.UpdTime)
        buf = CommFunc.WriteDWORD(buf, self.PackDataSize)
        buf = CommFunc.WriteString(buf, self.PackDataSize, self.PackData)
        return buf
    def getLength(self):
        length = 0
        length += sizeof(ctypes.c_ulong)
        length += sizeof(ctypes.c_ulong)
        length += sizeof(ctypes.c_ulong)
        length += self.PackDataSize
        return length
    def outputString(self):
        output = '''// 玩家数据打包表 #tagDBPlayerPackData:
            PlayerID = %s,
            UpdTime = %s,
            PackDataSize = %s,
            PackData = %s,
            ADOResult = %s,
            '''%(
                self.PlayerID,
                self.UpdTime,
                self.PackDataSize,
                self.PackData,
                self.ADOResult,
            )
        return output
ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -1648,6 +1648,9 @@
CrossServerMsg_Notify = "Notify"                        # 提示信息
CrossServerMsg_ChatCrossWorld = "ChatCrossWorld"        # 跨服世界聊天
CrossServerMsg_ViewPlayerCacheRet = "ViewPlayerCacheRet"# 查看跨服玩家信息结果
CrossServerMsg_PlayerPackDataState = "PlayerPackDataState"# 玩家打包数据同步状态
CrossServerMsg_PullPlayerPackData = "PullPlayerPackData"# 拉取玩家打包数据
CrossServerMsg_PushPlayerPackData = "PushPlayerPackData"# 推送玩家打包数据
CrossServerMsg_PKMatchReqRet = "PKMatchReqRet"          # 跨服PK匹配请求结果
CrossServerMsg_PKMatchResult = "PKMatchResult"          # 跨服PK匹配结果
CrossServerMsg_PKReadyOKRoomList = "PKReadyOKRoomList"  # 跨服PK已准备好的房间列表
@@ -1705,6 +1708,8 @@
ClientServerMsg_ChatCrossWorld = "ChatCrossWorld"       # 跨服世界聊天
ClientServerMsg_GMCMD = "GMCMD"                         # GM命令
ClientServerMsg_ViewPlayerCache = "ViewPlayerCache"     # 查看跨服玩家信息
ClientServerMsg_PullOtherPlayerPackData = "PullOtherPlayerPackData"   # 拉其他服玩家打包数据
ClientServerMsg_PlayerPackData = "PlayerPackData"       # 玩家打包数据同步
ClientServerMsg_PKMatch = "PKMatch"                     # 跨服PK匹配
ClientServerMsg_PKRobotOver = "PKRobotOver"             # 跨服PK机器人结算
ClientServerMsg_PKCancel = "PKCancel"                   # 跨服PK取消匹配
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1933,6 +1933,22 @@
PacketSubCMD_1=0x10
PacketCallFunc_1=OnTurnFight
;镜像战斗
[MirrorAttack]
ScriptName = Attack\MirrorAttack.py
Writer = hxp
Releaser = hxp
RegType = 0
RegisterPackCount = 2
PacketCMD_1=0xA1
PacketSubCMD_1=0x09
PacketCallFunc_1=OnSycnPlayerPackData
PacketCMD_2=0xB4
PacketSubCMD_2=0x11
PacketCallFunc_2=OnMirrorFight
;砍树
[PlayerCutTree]
ScriptName = Player\PlayerCutTree.py
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
@@ -54,6 +54,7 @@
import GameObj
import BuffSkill
import PlayerState
import MirrorAttack
import ChPyNetSendPack
import NPCHurtManager
import NetPackCommon
@@ -2911,6 +2912,18 @@
        恶意攻击自己的玩家无论什么情况下都可反击,不用切换模式
    '''
    #关系有3层,无-友好-敌人
    #镜像PK下,无视PK区域、PK模式等,仅验证双方是否同一阵营
    curBattleID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID)
    tagBattleID = tagPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID)
    if curBattleID and curBattleID == tagBattleID:
        battle = MirrorAttack.GetMirrorBattleByID(curBattleID)
        if battle.batState != ChConfig.Def_MirrorBatState_Fight:
            return ChConfig.Type_Relation_None, ChConfig.Def_PASysMessage_None
        if curPlayer.GetFaction() != tagPlayer.GetFaction():
            return ChConfig.Type_Relation_Enemy , ChConfig.Def_PASysMessage_None
        return ChConfig.Type_Relation_Friend, ChConfig.Def_PASysMessage_None
    #判断是否可释放(增/减)技能或普攻
    if CheckPlayersRelationInFB_IsNone(curPlayer, tagPlayer):
        return ChConfig.Type_Relation_None, ChConfig.Def_PASysMessage_None
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
New file
@@ -0,0 +1,841 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package MirrorAttack
#
# @todo:镜像战斗
# @author hxp
# @date 2024-10-17
# @version 1.0
#
# 详细描述: 镜像战斗,支持与玩家镜像数据战斗,以NPC为战斗实例,支持快速战斗
#
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import GameWorld
import ChPlayer
import ChConfig
import EffGetSet
import PlayerPet
import PetControl
import PyGameData
import ShareDefine
import PlayerHorse
import IPY_GameWorld
import PlayerControl
import OperControlManager
import PlayerViewCacheTube
import PassiveBuffEffMng
import ChNetSendPack
import IpyGameDataPY
import AttackCommon
import SkillCommon
import PlayerState
import SkillShell
import CommFunc
import PlayerFB
import GameMap
import FBLogic
import GameObj
import time
class MirrorBattle():
    ## 某场战斗
    def __init__(self):
        self.Clear()
        return
    def Clear(self):
        self.isSysbg = False # 是否系统后台进行战斗的,玩家无感知,仅知道结果
        self.requestID = 0 # 请求ID,一般是玩家ID或者系统自定的ID,如某一场PK的标识信息
        self.playerID = 0 # 所属玩家ID,可能为0
        self.battleID = 0 # 该场战斗的ID,一般玩家发起的为playerID,系统发起的为大于十亿的值,即 1000000000 + 该系统场次对应功能值
        self.mapID = 0 # 功能mapID,代表某一个功能
        self.funcLineID = 0
        self.batState = 0 # 状态:0-无;1-准备中;2-战斗中;3-快速结束中,4-结束
        self.mirrorIDDict = {} # 该场所有玩家镜像实例ID对应真实ID {playerID:realPlayerID, ...}
        self.realIDDict = {} # 该场所有真实玩家对应初始信息 {playerID:{k:v, ...}, ...}
        self.playerFactionDict = {} # 该场所有玩家阵营信息,真实玩家+镜像玩家 {playerID:faction, ...}
        self.playerAutoSkillInfo = {} # 玩家自动释放技能列表 {playerID:[skillTypeID, ...], ...}
        self.deadPlayerIDList = [] # 已被击杀的玩家ID列表
        self.isLogout = False # 是否下线的
        self.isQuick = False # 是否快速战斗结束的
        self.winFaction = 0 # 获胜阵营
        return
    def AddBattlePlayer(self, curPlayer, faction, posX=0, posY=0):
        playerID = curPlayer.GetPlayerID()
        realPlayerID = curPlayer.GetRealPlayerID()
        if realPlayerID:
            self.mirrorIDDict[playerID] = realPlayerID
        else:
            self.realIDDict[playerID] = {"SightLevel":curPlayer.GetSightLevel(), "Faction":curPlayer.GetFaction(), "PosX":curPlayer.GetPosX(), "PosY":curPlayer.GetPosY()}
        self.playerFactionDict[playerID] = faction
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, self.battleID)
        if posX and posY:
            curPlayer.ResetPos(posX, posY)
        curPlayer.SetCanAttack(True)
        curPlayer.SetFaction(faction)
        PlayerControl.SetPlayerSightLevel(curPlayer, self.battleID) # 视野层级默认为战场ID,每场战斗的玩家独立视野
        GameObj.SetHPFull(curPlayer) # 回满血
        SkillCommon.ResetAllSkillCD(curPlayer) # 重置技能CD
        return
    def GetPlayerAutoUseSkillList(self, curPlayer):
        playerID = curPlayer.GetPlayerID()
        if playerID in self.playerAutoSkillInfo:
            return self.playerAutoSkillInfo[playerID]
        # J.技能搭配表.xlsx 配置的技能
        job = curPlayer.GetJob()
        defaultSkillList = []
        ipyDataMgr = IpyGameDataPY.IPY_Data()
        for index in range(ipyDataMgr.GetSkillMatchCount()):
            ipyData = ipyDataMgr.GetSkillMatchByIndex(index)
            skills = ipyData.GetSkills()
            if job > len(skills):
                continue
            defaultSkillList.append(skills[job - 1])
        playerSetting = curPlayer.GetSetting()
        autoUseSkillList = CommFunc.ParseSetting_AutoSkillList(playerSetting, defaultSkillList)
        curJobCommAtkSkillIDList = IpyGameDataPY.GetFuncEvalCfg("JobFitterSkill", job)
        # 普攻放最后
        for commSkillID in curJobCommAtkSkillIDList:
            autoUseSkillList.append(commSkillID)
        GameWorld.DebugLog("加载玩家设置的自动技能: %s" % autoUseSkillList, playerID)
        self.playerAutoSkillInfo[playerID] = autoUseSkillList
        return autoUseSkillList
def AddMirrorBattle(battleID, mapID=0, funcLineID=0, requestID=0, isSysbg=False, playerID=0):
    ## 增加镜像战斗管理
    # @param battleID: 战斗ID
    battle = None
    if battleID and battleID not in PyGameData.g_mirrorBattleDict:
        battle = MirrorBattle()
        battle.battleID = battleID
        battle.mapID = mapID
        battle.funcLineID = funcLineID
        battle.requestID = requestID
        battle.isSysbg = isSysbg
        battle.playerID = playerID
        PyGameData.g_mirrorBattleDict[battleID] = battle
    return battle
def GetMirrorBattle(curPlayer):
    ## 获取玩家实例所属的战场
    return GetMirrorBattleByID(curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID))
def GetMirrorBattleByID(battleID):
    battle = None
    if battleID in PyGameData.g_mirrorBattleDict:
        battle = PyGameData.g_mirrorBattleDict[battleID]
    # 不会执行,仅为了代码编辑.时出提示
    if False:
        battle = MirrorBattle()
    return battle
def ClearMirrorBattleByPlayer(curPlayer):
    ## 清除玩家创建的镜像战斗
    ClearMirrorBattleByID(curPlayer.GetPlayerID())
    return
def ClearMirrorBattleByID(battleID):
    ## 清除镜像战斗
    battle = PyGameData.g_mirrorBattleDict.pop(battleID, None)
    if not battle:
        return
    isSysbg = battle.isSysbg
    playerMgr = GameWorld.GetPlayerManager()
    tick = GameWorld.GetGameWorld().GetTick()
    # 回收镜像玩家
    for mirrorID in battle.mirrorIDDict.keys():
        mirrorPlayer = playerMgr.FindPlayerByID(mirrorID)
        if mirrorPlayer:
            PlayerControl.DeleteMirror(mirrorPlayer, isSysbg) # 系统场延迟回收
    # 重置真实玩家
    for playerID, info in battle.realIDDict.items():
        curPlayer = playerMgr.FindPlayerByID(playerID)
        if not curPlayer:
            continue
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, 0)
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleTime, 0)
        if curPlayer.GetPlayerAction() == IPY_GameWorld.paDie or GameObj.GetHP(curPlayer) <= 0:
            ChPlayer.PlayerRebornByType(curPlayer, ChConfig.rebornType_System, tick, isAddSuperBuff=False)
        if "PosX" in info:
            curPlayer.ResetPos(info["PosX"], info["PosY"]) # 回到进去前的坐标
        curPlayer.SetFaction(0)
        PlayerControl.SetPlayerSightLevel(curPlayer, 0)
        GameObj.SetHPFull(curPlayer) # 回满血
        SkillCommon.ResetAllSkillCD(curPlayer) # 重置技能CD
        curPlayer.SetAttackTick(tick)
        ChPlayer.__Sync_ClientBuff(curPlayer)
    # 所属玩家
    playerID = battle.playerID
    curPlayer = playerMgr.FindPlayerByID(playerID)
    if curPlayer:
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleID, 0)
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleTime, 0)
    return
def CreateMirrorPlayer(battleID, mirrorPlayerID, mirrorPlayerData, posX=0, posY=0, faction=0, curPlayer=None):
    ''' 创建镜像玩家
    @param battleID: 所属的战斗ID
    @param mirrorPlayerID: 目标镜像玩家ID
    @param mirrorPlayerData: 目标镜像玩家数据
    @param curPlayer: 执行创建的玩家
    '''
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        GameWorld.ErrLog("没有该镜像战斗ID,无法创建玩家镜像! battleID=%s" % battleID)
        return
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    playerID = 0
    if curPlayer:
        playerID = curPlayer.GetPlayerID()
    # playerData为base64后的数据
    mirrorPlayer = GameWorld.GetGameWorld().CreateMirrorPlayer(mirrorPlayerData, posX, posY)
    if not mirrorPlayer:
        GameWorld.ErrLog("CreateMirrorPlayer mirrorPlayerID=%s,posX=%s,posY=%s,faction=%s"
                         % (mirrorPlayerID, posX, posY, faction), playerID)
        return
    PlayerControl.SetCustomMap(mirrorPlayer, mapID, funcLineID)
    mirrorID = mirrorPlayer.GetID()
    realPlayerID = mirrorPlayer.GetRealPlayerID()
    dataFightPower = PlayerControl.GetFightPower(mirrorPlayer)
    GameWorld.DebugLog("CreateMirrorPlayer mirrorID=%s,realPlayerID=%s,mapID=%s,funcLineID=%s,posX=%s,posY=%s,faction=%s,dataFightPower=%s,,accID=%s"
                       % (mirrorID, realPlayerID, mapID, funcLineID, posX, posY, faction, dataFightPower, mirrorPlayer.GetAccID()), playerID)
    ChPlayer.InitPlayerPack(mirrorPlayer)
    PlayerHorse.PlayerRideHorseUp(mirrorPlayer, False, False)
    #是否镜像玩家 判断 mirrorPlayer.GetRealPlayerID()是否为0
    #python自己处理,以下逻辑,可以在DoPlayerLogin函数最后 判断是镜像玩家后统一处理
    #index = mirrorPlayer.GetIndex()
    #tick = GameWorld.GetGameWorld().GetTick()
    #ChPlayer.PlayerLogin
    #PlayerEventCounter.GameServer_InitOK(index, tick)
    #ChPlayer.LoadMapOK(index, tick)
    #GameServerRefresh.GameSever_PlayerInitOK(index, tick)
    mirrorPlayer.SendToBServerServerInitOK()
    mirrorPlayer.SetMapLoadOK(True)
    #将玩家放置在这个地图上
    mirrorPlayer.InitPos(mirrorPlayer.GetPosX(), mirrorPlayer.GetPosY())
    #把玩家设置为初始化成功状态
    mirrorPlayer.SetCanAttack(True)
    mirrorPlayer.SetCanMove(True)
    mirrorPlayer.SetIsNeedProcess(True)
    mirrorPlayer.SetInitOK(True)
    mirrorPlayer.EndLoadMap()
    mirrorPlayer.SetGameServerInitOK(True)
    #刷被动、属性
    PassiveBuffEffMng.OnLoadMapGFPassive(mirrorPlayer)
    mirrorControl = PlayerControl.PlayerControl(mirrorPlayer)
    mirrorControl.ReCalcAllState()
    refreshFightPower = PlayerControl.GetFightPower(mirrorPlayer)
    if refreshFightPower != dataFightPower:
        GameWorld.ErrLog("CreateMirrorPlayer mirrorID=%s,realPlayerID=%s,dataFightPower=%s != refreshFightPower=%s"
                         % (mirrorID, realPlayerID, dataFightPower, refreshFightPower), playerID)
    battle.AddBattlePlayer(mirrorPlayer, faction, posX, posY)
    #最后设置可见,刷新视野
    mirrorPlayer.SetVisible(True)
    mirrorPlayer.RefreshView()
    #灵宠有些属性取主人玩家,所以在玩家刷完属性后处理,也必须在主人刷新视野后处理,不然出现封包顺序可能不对,导致前端无法显示灵宠
    PetControl.DoLogic_PetLoadMapOK(mirrorPlayer)
    if GameWorld.GetGameWorld().GetDebugLevel():
        DebugLogPlayerInfo(mirrorPlayer)
        if playerID == realPlayerID:
            DebugLogPlayerInfo(curPlayer)
    return mirrorPlayer
def DebugLogPlayerInfo(curPlayer):
    playerID = curPlayer.GetPlayerID()
    GameWorld.DebugLog("-------------- DebugLogPlayerInfo玩家信息 --------------", playerID)
    realPlayerID = curPlayer.GetRealPlayerID()
    posX = curPlayer.GetPosX()
    posY = curPlayer.GetPosY()
    sightLevel = curPlayer.GetSightLevel()
    GameWorld.DebugLog("realPlayerID=%s,posX=%s,posY=%s,sightLevel=%s" % (realPlayerID, posX, posY, sightLevel), playerID)
    GameWorld.DebugLog("生命=%s/%s, 护盾=%s/%s"
                       % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer),
                          PlayerControl.GetProDef(curPlayer), PlayerControl.GetMaxProDef(curPlayer)), playerID)
    # 属性
    attrInfo = ""
    for index in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
        value = EffGetSet.GetValueByEffIndex(curPlayer, index)
        if value:
            attrInfo = "%s,%s=%s" % (attrInfo, index, value)
    GameWorld.DebugLog("AttrInfo=%s" % attrInfo, playerID)
    GameWorld.DebugLog("FightPower=%s" % (PlayerControl.GetFightPower(curPlayer)), playerID)
    # 技能
    skillDict = {}
    skillManager = curPlayer.GetSkillManager()
    for i in range(0 , skillManager.GetSkillCount()):
        curSkill = skillManager.GetSkillByIndex(i)
        if not curSkill:
            continue
        funcType = curSkill.GetFuncType()
        if funcType not in skillDict:
            skillDict[funcType] = {}
        skillInfo = skillDict[funcType]
        skillInfo[curSkill.GetSkillID()] = curSkill.GetSkillName()
    for funcType, skillInfo in skillDict.items():
        skillIDList = skillInfo.keys()
        skillIDList.sort()
        GameWorld.DebugLog("Skill FuncType=%s,count=%s,%s" % (funcType, len(skillIDList), skillIDList), playerID)
    # 被动
    passiveEff = PassiveBuffEffMng.GetPassiveEffManager().GetPassiveEff(curPlayer)
    if passiveEff:
        GameWorld.DebugLog("被动效果 :%s" % passiveEff.AffectSkillDict, playerID)
        GameWorld.DebugLog("选中的被动技能效果 :%s" % passiveEff.AffectPassiveSkillSetDict, playerID)
        GameWorld.DebugLog("被动BUFF效果 :%s" % passiveEff.AffectBuffDict, playerID)
        GameWorld.DebugLog("神兽被动效果 :%s" % passiveEff.AffectDogzSkillDict, playerID)
    else:
        GameWorld.DebugLog("无技能被动效果!", playerID)
    fightPet = curPlayer.GetPetMgr().GetFightPet()
    if fightPet:
        GameWorld.DebugLog("出战宠物技能%s-%s" % PlayerPet.GetPetLearnSkill(curPlayer), playerID)
        passiveEff = PassiveBuffEffMng.GetPassiveEffManager().GetPassiveEff(fightPet)
        if passiveEff:
            GameWorld.DebugLog("宠物被动效果 :%s" % passiveEff.AffectSkillDict, playerID)
            GameWorld.DebugLog("宠物被动BUFF效果 :%s" % passiveEff.AffectBuffDict, playerID)
        else:
            GameWorld.DebugLog("无技能被动效果!", playerID)
    # 物品
    for packType in [IPY_GameWorld.rptEquip, ShareDefine.rptPet, ShareDefine.rptDogzEquip]:
        curPack = curPlayer.GetItemManager().GetPack(packType)
        itemCount = 0
        for i in range(0, curPack.GetCount()):
            item = curPack.GetAt(i)
            if not item.GetItemTypeID():
                continue
            itemCount += 1
            #GameWorld.DebugLog("packType=%s,i=%s,itemID=%s,userData=%s" % (packType, i, item.GetItemTypeID(), item.GetUserData()))
        GameWorld.DebugLog("packType:%s,count=%s/%s" % (packType, itemCount, curPack.GetCount()), playerID)
    GameWorld.DebugLog("-------------------------------------------------------", playerID)
    return
#// A1 09 同步打包玩家数据 #tagCMSycnPlayerPackData
#
#struct tagCMSycnPlayerPackData
#{
#    tagHead        Head;
#};
def OnSycnPlayerPackData(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, forcePackData=True)
    return
#// B4 11 镜像战斗 #tagCMMirrorFight
#
#struct    tagCMMirrorFight
#{
#    tagHead        Head;
#    DWORD        MapID;    // 自定义地图ID,如竞技场等
#    WORD        FuncLineID;
#    DWORD        TagPlayeID;    // 目标玩家ID,支持跨服玩家ID
#    BYTE        CmdType;    // 命令类型: 0-创建战斗;1-开始战斗;2-战斗中跳过;3-不创建战斗直接得结果
#};
def OnMirrorFight(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    mapID = clientData.MapID
    funcLineID = clientData.FuncLineID
    tagPlayeID = clientData.TagPlayeID
    cmdType = clientData.CmdType
    if not tagPlayeID:
        return
    playerID = curPlayer.GetPlayerID()
    #if tagPlayeID == playerID:
    #    GameWorld.DebugLog("不能打自己!", playerID)
    #    return
    # 创建战斗,玩家自身参与
    if cmdType == 0:
        isSysbg = False
        requestID = playerID
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        battlePlayerList.append({"playerID":playerID, "faction":1, "posX":posX, "posY":posY})
        battlePlayerList.append({"playerID":tagPlayeID, "faction":2, "posX":posX + 5, "posY":posY})
        OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg, curPlayer)
        return
    # 开始战斗
    if cmdType == 1:
        battle = GetMirrorBattle(curPlayer)
        if battle:
            OnMirrorBattleStart(battle.battleID)
        return
    # 战斗中跳过
    if cmdType == 2:
        battle = GetMirrorBattle(curPlayer)
        if battle:
            DoMirrorBattleQuick(battle.battleID)
        return
    # 不战斗直接跳过,即玩家没有参与,创建系统战斗场,之后扩展
    if cmdType == 3:
        isSysbg = True
        requestID = playerID
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        battlePlayerList.append({"playerID":playerID, "faction":1, "posX":posX, "posY":posY})
        battlePlayerList.append({"playerID":tagPlayeID, "faction":2, "posX":posX + 5, "posY":posY})
        OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg, curPlayer)
        return
    # 可不做验证,PK结束后由各个功能自行做结算验证
    return
def OnPlayerLeaveMap(curPlayer):
    ## 玩家离开地图
    if curPlayer.GetRealPlayerID():
        return
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    # 如果还在战斗中,直接快速执行战斗结果
    if battle.batState == ChConfig.Def_MirrorBatState_Fight:
        DoMirrorBattleQuick(battle.battleID, True)
    # 统一退出
    if PlayerControl.GetCustomMapID(curPlayer):
        PlayerFB.DoExitCustomScene(curPlayer)
    return
def OnRequestCreateMirrorBattle(mapID, funcLineID, requestID, battlePlayerList, isSysbg=False, curPlayer=None):
    ''' 请求创建镜像战斗,支持多对多,支持跨服,本服跨服地图中均可直接请求
    @param mapID: 功能地图ID
    @param funcLineID: 功能地图线路ID
    @param requestID: 请求ID,如果是玩家发起的,一般传入玩家ID;如果是系统发起的,由系统自行决定,比如roomID之类
    @param battlePlayerList: 战斗的玩家信息列表 [{"playerID":玩家ID, "posX":坐标x, "posY":坐标y, "faction":阵营}, ...]
    @param isSysbg: 是否后台战斗,默认否,但是系统发起的默认是
    @param curPlayer: 发起的玩家,为空时代表系统发起创建的
    '''
    playerID = 0
    if curPlayer:
        playerID = curPlayer.GetPlayerID()
        curTime = int(time.time())
        # 请求cd验证
        requestTime = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleTime)
        if requestTime and (curTime - requestTime) <= 20: # 20秒内不重复请求
            PlayerControl.NotifyCode(curPlayer, "RequestLater")
            return
        curPlayer.SetDict(ChConfig.Def_PlayerKey_MirrorBattleTime, curTime)
    else:
        isSysbg = True # 系统发起的默认后台战斗
    mirrorIDList = []
    for battleInfo in battlePlayerList:
        batPlayerID = battleInfo["playerID"]
        faction = battleInfo["faction"]
        if batPlayerID == playerID and faction == 1 and not isSysbg:
            # 自己不用,使用自身进行战斗即可
            continue
        if batPlayerID not in mirrorIDList:
            mirrorIDList.append(batPlayerID)
    # 战斗相关的数据
    msgData = {"mapID":mapID, "funcLineID":funcLineID, "battlePlayerList":battlePlayerList, "isSysbg":isSysbg}
    # 发送GameServer请求玩家打包数据
    requestTime = curTime # 请求的时间戳,每个玩家最多允许同时存在一场战斗,每次重新请求后覆盖数据
    requestMapID = GameWorld.GetGameWorld().GetRealMapID()
    sendMsg = str({"msgType":"MirrorBattle", "msgData":msgData, "mirrorIDList":mirrorIDList,
                   "requestTime":requestTime, "requestID":requestID, "requestMapID":requestMapID, "playerID":playerID})
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(0, 0, 0, "ReuestPlayerPackData", sendMsg, len(sendMsg))
    GameWorld.DebugLog("请求创建镜像战斗: %s" % sendMsg, playerID)
    return
def OnMirrorBattleInit(msgInfo, packDataDict, curPlayer=None):
    ''' 镜像战斗初始化
    @param msgInfo: OnRequestCreateMirrorBattle 发送GameServer的信息
    @param packDataDict: 返回需要的玩家打包数据 {playerID:packData, ...}
    @param curPlayer: 如果是玩家发起的则不为空
    '''
    requestID = msgInfo["requestID"]
    playerID = msgInfo.get("playerID", 0)
    msgData = msgInfo["msgData"]
    mapID = msgData.get("mapID", 0)
    funcLineID = msgData.get("funcLineID", 0)
    battlePlayerList = msgData.get("battlePlayerList", [])
    isSysbg = msgData.get("isSysbg", 0) # 系统后台战斗
    battleID = 0
    if isSysbg:
        sysBattleIDStart = 1000000000 # 系统场从十亿开始
        for num in range(sysBattleIDStart, sysBattleIDStart + 1000):
            if num not in PyGameData.g_mirrorBattleDict:
                battleID = num
                break
    elif playerID:
        battleID = playerID
    battle = AddMirrorBattle(battleID, mapID, funcLineID, requestID, isSysbg, playerID)
    if not battle:
        GameWorld.ErrLog("镜像战场ID已存在! battleID=%s,msgInfo=%s" % (battleID, msgInfo), requestID)
        return
    GameWorld.DebugLog("镜像战斗初始化: msgData=%s,packIDList=%s" % (msgData, packDataDict.keys()), battleID)
    for battleInfo in battlePlayerList:
        batPlayerID = battleInfo["playerID"]
        posX = battleInfo.get("posX", 0)
        posY = battleInfo.get("posY", 0)
        faction = battleInfo.get("faction", 0)
        if curPlayer and batPlayerID == playerID and faction == 1 and not isSysbg:
            battle.AddBattlePlayer(curPlayer, faction, posX, posY)
            continue
        packData = packDataDict.get(batPlayerID)
        if not packData:
            GameWorld.ErrLog("初始化镜像战斗时没有玩家镜像数据! batPlayerID=%s" % batPlayerID, playerID)
            continue
        CreateMirrorPlayer(battleID, batPlayerID, packData, posX, posY, faction, curPlayer)
    battle.batState = ChConfig.Def_MirrorBatState_Prepare
    if not isSysbg:
        return
    # 系统场默认直接开始、快速战斗结束
    OnMirrorBattleStart(battleID)
    DoMirrorBattleQuick(battleID)
    return
def OnMirrorBattleStart(battleID):
    ## 镜像战斗开始
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        return
    if battle.batState >= ChConfig.Def_MirrorBatState_Fight:
        return
    battle.batState = ChConfig.Def_MirrorBatState_Fight
    return
def ProcessPlayerMirrorAI(curPlayer, tick):
    ## 镜像战斗AI
    battle = GetMirrorBattle(curPlayer)
    if not battle:
        return
    playerID = curPlayer.GetPlayerID()
    if battle.batState != ChConfig.Def_MirrorBatState_Fight:
        #GameWorld.DebugLog("镜像玩家仅自由战斗状态下需要处理! battleID=%s,batState=%s" % (battle.battleID, battle.batState), playerID)
        return
    if not battle.isQuick:
        realPlayerID = curPlayer.GetRealPlayerID()
        if not realPlayerID:
            # 常规战斗下,真实玩家不处理,由玩家自行控制
            return
    if GameObj.GetHP(curPlayer) <= 0:
        #GameWorld.DebugLog("镜像玩家已被击杀", playerID)
        return
    # 攻击间隔
    if tick - curPlayer.GetPlayerAttackTick() < curPlayer.GetAtkInterval():
        GameWorld.DebugLog("攻击间隔: %s < %s" % (tick - curPlayer.GetPlayerAttackTick(), curPlayer.GetAtkInterval()), playerID)
        return
    autoUseSkillList = battle.GetPlayerAutoUseSkillList(curPlayer)
    GameWorld.DebugLog("镜像AI攻击: autoUseSkillList=%s" % (autoUseSkillList), playerID)
    if curPlayer.GetPlayerVehicle() == IPY_GameWorld.pvHorse:
        PlayerHorse.PlayerRideHorseDown(curPlayer)
    isOK = False
    actionObj = PlayerState.__GetCanAttack_ObjDetel(curPlayer, tick)
    if actionObj:
        isOK = PlayerAttack(curPlayer, actionObj, tick, autoUseSkillList)
    if not isOK:
        curFaction = curPlayer.GetFaction()
        playerMgr = GameWorld.GetMapCopyPlayerManager()
        for batPlayerID, faction in battle.playerFactionDict.items():
            if faction == curFaction:
                continue
            if actionObj and actionObj.GetID() == batPlayerID:
                continue
            tagObj = playerMgr.FindPlayerByID(batPlayerID)
            isOK = PlayerAttack(curPlayer, tagObj, tick, autoUseSkillList)
            if isOK:
                break
    if isOK:
        # 每次处理仅执行一次成功行为
        return
    return
def PlayerAttack(curPlayer, tagObj, tick, autoUseSkillList):
    ## 玩家攻击, 参考技能使用 #def UseSkillEx(index, clientData, tick):
    if not tagObj or GameObj.GetHP(tagObj) <= 0:
        return
    playerID = curPlayer.GetPlayerID()
    posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
    tagObjType, tagObjID = tagObj.GetGameObjType(), tagObj.GetID()
    tagPosX, tagPosY = tagObj.GetPosX(), tagObj.GetPosY()
    curPlayer.SetActionObj(tagObj)
    curPlayer.ClearUseSkillRec()
    curPlayer.SetAttackTargetPos(posX, posY)
    curPlayer.SetUseSkillPosX(tagPosX)
    curPlayer.SetUseSkillPosY(tagPosY)
    curPlayer.SetUseSkillTagType(tagObjType)
    curPlayer.SetUseSkillTagID(tagObjID)
    needMoveto = False # 有可释放的技能优先释放技能,没有的话再移动
    useSkillResult = False
    skillMgr = curPlayer.GetSkillManager()
    for skillTypeID in autoUseSkillList:
        curSkill = skillMgr.FindSkillBySkillTypeID(skillTypeID)
        if not curSkill:
            continue
        skillID = curSkill.GetSkillID()
        #CheckSkillCondition
        #被动技能无法使用
        if SkillCommon.isPassiveSkill(curSkill):
            continue
        #还在冷却时间内无法释放
        if SkillCommon.RefreshSkillRemainTime(curSkill, tick) != 0:
            continue
        if not AttackCommon.CheckPlayerAttackDist(curPlayer, tagObj, curSkill):
            needMoveto = True
            continue
        curPlayer.SetUseSkill(curSkill.GetSkillData())
        useSkillData = curPlayer.GetUseSkill()
        if not PlayerState.__DoClientUseSkillEx(curPlayer, useSkillData, tick):
            GameWorld.DebugLog("        技能攻击失败: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
            continue
        useSkillResult = True
        GameWorld.DebugLog("        技能攻击成功: playerID=%s,tagID=%s,skillID=%s" % (playerID, tagObjID, skillID))
        if useSkillData and useSkillData.GetSkillID() != ChConfig.Def_SkillID_Somersault:
            # 跟随玩家同频率攻击
            PetControl.PetFight(curPlayer, tick)
            PlayerState.SummonFollowAtk(curPlayer, tick)
        break
    curPlayer.ClearUseSkillRec()
    if useSkillResult:
        curPlayer.SetAttackTick(tick)
    else:
        if needMoveto:
            MoveToObj(curPlayer, tagObj, tick)
            return
    return useSkillResult
def MoveToObj(curPlayer, tagObj, tick):
    #不可移动行为状态, 服务端限制
    if not OperControlManager.IsObjCanDoAction(curPlayer,
                                               ChConfig.Def_Obj_ActState_ServerAct,
                                               IPY_GameWorld.oalMove):
        return
    destX = tagObj.GetPosX()
    destY = tagObj.GetPosY()
    # 缩小两格子用于前方一小片区域
    resultPos = GameMap.GetEmptyPlaceInArea(destX, destY, 1)
    moveDestX = resultPos.GetPosX()
    moveDestY = resultPos.GetPosY()
    if (tick - curPlayer.GetDictByKey("MoveTick")) < 1000:
        # .Move( 接口调用太快会导致移动时间不够长(不足一格)导致无法移动 或者移动过慢问题
        # SetDestPos 调用会导致反向移动偏快
        #curPlayer.SetDestPos(moveDestX, moveDestY)
        return
    curPlayer.SetDict("MoveTick", tick)
    #return curPlayer.Move(moveDestX, moveDestY)
    # 执行一次重置位置,避免快速发包导致无法移动
    curPlayer.ChangePos(moveDestX, moveDestY)
    sendPack = ChNetSendPack.tagObjMove()
    sendPack.Clear()
    sendPack.ObjID = curPlayer.GetID()
    sendPack.ObjType = IPY_GameWorld.gotNPC
    sendPack.MoveType = IPY_GameWorld.gotPlayer
    sendPack.DestPosX = moveDestX
    sendPack.DestPosY = moveDestY
    sendPack.Speed = curPlayer.GetSpeed()
    sendPack.StartPosX = curPlayer.GetPosX()
    sendPack.StartPosY = curPlayer.GetPosY()
    curPlayer.NotifyAll(sendPack.GetBuffer(), sendPack.GetLength())
    return
def DoMirrorBattleQuick(battleID, isLogout=False):
    ## 执行快速战斗
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        return
    if battle.batState > ChConfig.Def_MirrorBatState_Fight:
        return
    tick = GameWorld.GetGameWorld().GetTick()
    battle.batState = ChConfig.Def_MirrorBatState_Fight
    battle.isQuick = True
    battle.isLogout = isLogout
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    perLoopTick = 100 # 每次循环视为已过毫秒
    maxLoopCount = 60 * 1000 / perLoopTick # 循环次数上限,暂定最长PK时长60秒
    GameWorld.DebugLog("DoMirrorBattleQuick isLogout=%s,maxLoopCount=%s,tick=%s" % (isLogout, maxLoopCount, tick), battleID)
    # 屏蔽发包
    for batPlayerID in battle.realIDDict.keys():
        curPlayer = playerMgr.FindPlayerByID(batPlayerID)
        if not curPlayer:
            continue
        curPlayer.SetForbiddenSyncClientState(True)
    for loopCount in range(maxLoopCount):
        if battle.batState != ChConfig.Def_MirrorBatState_Fight:
            # 可能还没循环完毕就结束了
            break
        tick += loopCount * perLoopTick # 修改每次循环的tick
        GameWorld.DebugLog("    loopCount=%s,tick=%s" % (loopCount, tick), battleID)
        for batPlayerID in battle.playerFactionDict.keys():
            if batPlayerID in battle.deadPlayerIDList:
                continue
            curPlayer = playerMgr.FindPlayerByID(batPlayerID)
            if not curPlayer or GameObj.GetHP(curPlayer) <= 0:
                continue
            # 刷新定时处理的buff效果
            SkillShell.ProcessPersistBuff(curPlayer, tick)
            #刷新玩家Buff时长
            reFlashBuff = PlayerState.ProcessRefreshBuffState(curPlayer, tick)
            attrBuffResult, actBuffResult = PlayerState.ProcessRefreshActionBuffState(curPlayer, tick)
            playerControl = PlayerControl.PlayerControl(curPlayer)
            if actBuffResult:
                playerControl.RefreshPlayerActionState()
            #此处才是真正的刷新人物属性值,需刷属性逻辑应在此行前调用
            if not playerControl.RefreshPlayerAttrStateEx():
                if reFlashBuff or attrBuffResult:
                    playerControl.RefreshPlayerAttrByBuff()
                # 只刷BUFF情况
                playerControl.RefreshPlayerAttrByBuffEx()
            ProcessPlayerMirrorAI(curPlayer, tick)
    if battle.winFaction:
        # 已经有获胜方了,代表已经触发过结算胜负
        return
    # 暂定没击杀算输,发起方为1
    GameWorld.DebugLog("没有击败对方阵营!", battleID)
    battle.winFaction = 2
    OnMirrorAttackOver(battleID)
    return
def OnPlayerDead(curPlayer):
    battleID = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_MirrorBattleID)
    if not battleID:
        return
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        return
    playerID = curPlayer.GetPlayerID()
    curFaction = curPlayer.GetFaction()
    if playerID not in battle.deadPlayerIDList:
        battle.deadPlayerIDList.append(playerID)
    GameWorld.DebugLog("镜像战斗战场玩家被击杀! playerID=%s,curFaction=%s,deadPlayerIDList=%s"
                       % (playerID, curFaction, battle.deadPlayerIDList), battleID)
    winFaction = 0
    for batPlayerID, faction in battle.playerFactionDict.items():
        if faction != curFaction:
            winFaction = faction
            continue
        if batPlayerID not in battle.deadPlayerIDList:
            #GameWorld.DebugLog("相同阵营还有未被击杀的玩家!")
            return
    # 同阵营都被击杀了,结算胜负
    battle.winFaction = winFaction
    GameWorld.DebugLog("某一阵营已被击败! winFaction=%s" % winFaction, battleID)
    OnMirrorAttackOver(battleID)
    return
def OnMirrorAttackOver(battleID):
    battle = GetMirrorBattleByID(battleID)
    if not battle:
        return
    if battle.batState >= ChConfig.Def_MirrorBatState_Over:
        # 已经结算过
        return
    battle.batState = ChConfig.Def_MirrorBatState_Over
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    winFaction = battle.winFaction
    isQuick = battle.isQuick
    isLogout = battle.isLogout
    GameWorld.DebugLog("镜像战斗结束: mapID=%s,funcLineID=%s,winFaction=%s,isQuick=%s,isLogout=%s"
                       % (mapID, funcLineID, winFaction, isQuick, isLogout), battleID)
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    #快速战斗结束的额外处理
    if isQuick:
        for playerID in battle.realIDDict.keys():
            curPlayer = playerMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            curPlayer.SetForbiddenSyncClientState(False)
        for playerID in battle.playerFactionDict.keys():
            curPlayer = playerMgr.FindPlayerByID(playerID)
            if not curPlayer:
                continue
            # 重新通知最终血量及状态
            GameObj.SetHP(curPlayer, GameObj.GetHP(curPlayer))
            if playerID in battle.deadPlayerIDList:
                curPlayer.SetDead(curPlayer.GetDictByKey(ChConfig.Def_NPCDead_KillerID),
                                  curPlayer.GetDictByKey(ChConfig.Def_NPCDead_KillerType))
    FBLogic.OnMirrorBattleOver(battleID, mapID)
    if battle.isSysbg:
        ClearMirrorBattleByID(battleID)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1869,6 +1869,8 @@
Def_FBMapID_ArenaBattle = 31290
#情缘副本
Def_FBMapID_Love = 31300
#镜像切磋
Def_FBMapID_MirrorBattle = 100
#回合战斗自定义地图ID
Def_TFMapID_MineArea = 1 # 福地 1
@@ -1879,7 +1881,7 @@
Def_TFMapID_SendToGameServer = [Def_TFMapID_MineArea, Def_FBMapID_ArenaBattle]
#前端自定义场景地图
ClientCustomScene = [Def_FBMapID_PersonalBoss, Def_FBMapID_ArenaBattle]
ClientCustomSceneList = [Def_FBMapID_PersonalBoss, Def_FBMapID_ArenaBattle, Def_FBMapID_MirrorBattle]
#注册上传跨服服务器数据后直接进入跨服服务器的地图
RegisterEnter_CrossServerMapIDList = [Def_FBMapID_CrossPenglai, Def_FBMapID_CrossDemonLand, Def_FBMapID_CrossDemonKing, 
@@ -2011,6 +2013,7 @@
                'MineArea':[Def_TFMapID_MineArea], #福地
                'AlineInvade':[Def_TFMapID_AlineInvade], #异兽入侵
                'Adventure':[Def_TFMapID_Adventure], #冒险
                'MirrorBattle':[Def_FBMapID_MirrorBattle], #镜像切磋
                }
#特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
@@ -2630,7 +2633,7 @@
#---------------------------------------------------------------------
#请求类型(需要和GameServer中的一致)
Def_QueryType_Count = 56
Def_QueryType_Count = 55
(
queryType_sqtPlayer,                  #查询玩家
queryType_sqtFamilyWar,               #家族战
@@ -2687,7 +2690,6 @@
queryType_EnterFB,                    #进入副本
queryType_NPCInfo,                    #查询NPCInfo
queryType_NPCCnt,                     #查询NPC数量
queryType_MirrorPlayer,               #镜像玩家
) = range(0, Def_QueryType_Count)
#------------------------------------------------------------------------------ 
#---------------------------------------------------------------------
@@ -3199,6 +3201,14 @@
    Def_PlayerState_Ice, # 寒冰状态(同减速) 19
) = range(20)
#镜像战斗状态 0-无;1-准备中;2-自由战斗;3-结束
(
Def_MirrorBatState_Init, # 初始化 0
Def_MirrorBatState_Prepare, # 初始化完毕,准备阶段 1
Def_MirrorBatState_Fight, # 战斗阶段 2
Def_MirrorBatState_Over, # 战斗结束 3
) = range(4)
#---SetDict 含NPC字典KEY,不存于数据库---
Def_GameObjKey_InheritOwner = "InheritOwner"  # 类暴风雪计算时用主人属性
@@ -3273,6 +3283,8 @@
Def_PlayerKey_TransTick = 'TransTick'  # 传送tick
Def_PlayerKey_SyncVIPKillNPCLVInfo = 'SyncVIPKillNPCLVInfo'  # 击杀NPC增加VIP杀怪等级经验信息同步开关
Def_PlayerKey_RequestEnterCrossServerTick = 'RequestEnterCrossServerTick'    # 上次请求进入跨服tick
Def_PlayerKey_MirrorBattleTime = 'MirrorBattleTime'    # 最近一次请求镜像战斗时间戳
Def_PlayerKey_MirrorBattleID = 'MirrorBattleID'    # 镜像战斗ID,有值时代表处于镜像战斗中
Def_PlayerKey_FamilyArrestQueryState = 'ArrestQueryState'  # 家族悬赏任务完成查询状态
Def_PlayerKey_Frist_Lock = "Frist_Lock"  # 是否接受了任务1
@@ -3702,6 +3714,8 @@
Def_PDict_ShareGameAwardState = "ShareGameAwardState"  # 每日分享游戏领奖记录
Def_PDict_GoodGameAwardState = "GoodGameAwardState"  # 游戏好评领奖记录
Def_PDict_EquipViewCacheState = "EquipViewCacheState"  # 本次上线是否同步过装备缓存
Def_PDict_PackDataSyncState = "PackDataSyncState"  # 本次上线打包数据同步状态,按位存储是否同步 0-本服,1-跨服
Def_PDict_PackDataSyncFightPower = "PackDataSyncFightPower"  # 本次上线打包数据同步时的战力,用于对比,只对比求余亿部分即可
Def_PDict_DayOnlineTime = "OnlineTime"  # 当日在线时长
Def_PDict_OnlineStartTick = "OnlineStartTime"        # 在线计算时间
Def_PDict_LVAwardGetRecord = "LVAwardGetRecord"  # 等级奖励领取信息记录,按二进制位标示
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py
@@ -719,6 +719,11 @@
    ItemData19 = ""    #(String ItemData19)
    ItemDataSize20 = 0    #(WORD ItemDataSize20)
    ItemData20 = ""    #(String ItemData20)
    PackDataSyncState = 0    #(BYTE PackDataSyncState)// 打包数据同步状态: 0-不同步;个位-是否同步本服;十位-是否同步跨服
    PackDataLen = 0    #(DWORD PackDataLen)
    PackData = ""    #(String PackData)
    PackMsgLen = 0    #(WORD PackMsgLen)
    PackMsg = ""    #(String PackMsg)
    data = None
    def __init__(self):
@@ -778,6 +783,11 @@
        self.ItemData19,_pos = CommFunc.ReadString(_lpData, _pos,self.ItemDataSize19)
        self.ItemDataSize20,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.ItemData20,_pos = CommFunc.ReadString(_lpData, _pos,self.ItemDataSize20)
        self.PackDataSyncState,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.PackDataLen,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.PackData,_pos = CommFunc.ReadString(_lpData, _pos,self.PackDataLen)
        self.PackMsgLen,_pos = CommFunc.ReadWORD(_lpData, _pos)
        self.PackMsg,_pos = CommFunc.ReadString(_lpData, _pos,self.PackMsgLen)
        return _pos
    def Clear(self):
@@ -833,6 +843,11 @@
        self.ItemData19 = ""
        self.ItemDataSize20 = 0
        self.ItemData20 = ""
        self.PackDataSyncState = 0
        self.PackDataLen = 0
        self.PackData = ""
        self.PackMsgLen = 0
        self.PackMsg = ""
        return
    def GetLength(self):
@@ -886,6 +901,11 @@
        length += len(self.ItemData19)
        length += 2
        length += len(self.ItemData20)
        length += 1
        length += 4
        length += len(self.PackData)
        length += 2
        length += len(self.PackMsg)
        return length
@@ -940,6 +960,11 @@
        data = CommFunc.WriteString(data, self.ItemDataSize19, self.ItemData19)
        data = CommFunc.WriteWORD(data, self.ItemDataSize20)
        data = CommFunc.WriteString(data, self.ItemDataSize20, self.ItemData20)
        data = CommFunc.WriteBYTE(data, self.PackDataSyncState)
        data = CommFunc.WriteDWORD(data, self.PackDataLen)
        data = CommFunc.WriteString(data, self.PackDataLen, self.PackData)
        data = CommFunc.WriteWORD(data, self.PackMsgLen)
        data = CommFunc.WriteString(data, self.PackMsgLen, self.PackMsg)
        return data
    def OutputString(self):
@@ -992,7 +1017,12 @@
                                ItemDataSize19:%d,
                                ItemData19:%s,
                                ItemDataSize20:%d,
                                ItemData20:%s
                                ItemData20:%s,
                                PackDataSyncState:%d,
                                PackDataLen:%d,
                                PackData:%s,
                                PackMsgLen:%d,
                                PackMsg:%s
                                '''\
                                %(
                                self.Head.OutputString(),
@@ -1043,7 +1073,12 @@
                                self.ItemDataSize19,
                                self.ItemData19,
                                self.ItemDataSize20,
                                self.ItemData20
                                self.ItemData20,
                                self.PackDataSyncState,
                                self.PackDataLen,
                                self.PackData,
                                self.PackMsgLen,
                                self.PackMsg
                                )
        return DumpString
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -5887,6 +5887,54 @@
#------------------------------------------------------
# A1 09 同步打包玩家数据 #tagCMSycnPlayerPackData
class  tagCMSycnPlayerPackData(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xA1
        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 = 0xA1
        self.SubCmd = 0x09
        return
    def GetLength(self):
        return sizeof(tagCMSycnPlayerPackData)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// A1 09 同步打包玩家数据 //tagCMSycnPlayerPackData:
                                Cmd:%s,
                                SubCmd:%s
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd
                                )
        return DumpString
m_NAtagCMSycnPlayerPackData=tagCMSycnPlayerPackData()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMSycnPlayerPackData.Cmd,m_NAtagCMSycnPlayerPackData.SubCmd))] = m_NAtagCMSycnPlayerPackData
#------------------------------------------------------
#A1 03 设置是否成年 #tagCMAdult
class  tagCMAdult(Structure):
@@ -21070,6 +21118,70 @@
#------------------------------------------------------
# B4 11 镜像战斗 #tagCMMirrorFight
class  tagCMMirrorFight(Structure):
    _pack_ = 1
    _fields_ = [
                  ("Cmd", c_ubyte),
                  ("SubCmd", c_ubyte),
                  ("MapID", c_int),    # 自定义地图ID,如竞技场等
                  ("FuncLineID", c_ushort),
                  ("TagPlayeID", c_int),    # 目标玩家ID,支持跨服玩家ID
                  ("CmdType", c_ubyte),    # 命令类型: 0-创建战斗;1-开始战斗;2-战斗中跳过;3-不创建战斗直接得结果
                  ]
    def __init__(self):
        self.Clear()
        self.Cmd = 0xB4
        self.SubCmd = 0x11
        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 = 0xB4
        self.SubCmd = 0x11
        self.MapID = 0
        self.FuncLineID = 0
        self.TagPlayeID = 0
        self.CmdType = 0
        return
    def GetLength(self):
        return sizeof(tagCMMirrorFight)
    def GetBuffer(self):
        return string_at(addressof(self), self.GetLength())
    def OutputString(self):
        DumpString = '''// B4 11 镜像战斗 //tagCMMirrorFight:
                                Cmd:%s,
                                SubCmd:%s,
                                MapID:%d,
                                FuncLineID:%d,
                                TagPlayeID:%d,
                                CmdType:%d
                                '''\
                                %(
                                self.Cmd,
                                self.SubCmd,
                                self.MapID,
                                self.FuncLineID,
                                self.TagPlayeID,
                                self.CmdType
                                )
        return DumpString
m_NAtagCMMirrorFight=tagCMMirrorFight()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMMirrorFight.Cmd,m_NAtagCMMirrorFight.SubCmd))] = m_NAtagCMMirrorFight
#------------------------------------------------------
# B4 0F 回收私有专属木桩怪 #tagCMRecyclePriWoodPile
class  tagCMRecyclePriWoodPile(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py
@@ -607,3 +607,52 @@
    '''
    # 由于float会有不精确的现象出现xxx.9999999或xxx.0000000000001的问题,所以这里计算出的结果向上取整
    return int(math.ceil(round(floatRMB * 100)))
#64进制符号,前端用*号补空
symbols64 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'
def Convert10To64(number):
    """将十进制数转换为64进制"""
    if number < 0:
        number = abs(number)
    result = ''
    while number > 0:
        result = symbols64[number % 64] + result
        number //= 64
    return result or '0'
def Convert64To10(number64):
    """将64进制数转换为十进制"""
    value = 0
    for digit in number64:
        value = value * 64 + symbols64.index(digit)
    return value
def ParseSetting(playerSetting, index, cLen=1):
    """解析玩家设置存储"""
    s = playerSetting[index:index+cLen]
    s = s.replace("*", "") # 前端*代表空
    if not s:
        return 0
    return Convert64To10(s)
def ParseSetting_AutoSkillList(playerSetting, defaultSkillList):
    '''解析玩家设置  - 自动释放技能顺序列表
    @param playerSetting: 玩家设置保存串
    @param skillOrderList: 技能默认顺序列表
    '''
    playerSkillSetList = []
    SkillMatchPage = ParseSetting(playerSetting, 30, 1)
    fromIndex = 32 + SkillMatchPage * 15
    indexNum = 0
    for index in range(fromIndex, fromIndex + 15):
        skillIndex = indexNum
        v = ParseSetting(playerSetting, index, 1)
        if v:
            skillIndex = v - 1
        if skillIndex >= len(defaultSkillList):
            break
        skillID = defaultSkillList[skillIndex]
        playerSkillSetList.append(skillID)
        indexNum += 1
    return playerSkillSetList
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py
@@ -1052,7 +1052,7 @@
    for i in range(0, playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        EventResponse_OnAction(curPlayer, eventName, fileID)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py
@@ -6,6 +6,7 @@
import GameWorld
import PlayerControl
## GM命令执行入口
#  @param curPlayer 当前玩家
@@ -20,7 +21,7 @@
    if playerID != 0:
        mirrorPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if mirrorPlayer:
            mirrorPlayer.DeleteMirror()
            PlayerControl.DeleteMirror(mirrorPlayer)
        return
    ids = []
@@ -36,4 +37,6 @@
        mirrorPlayer = playerManager.FindPlayerByID(id)
        if not mirrorPlayer:
            continue
        mirrorPlayer.DeleteMirror()
        PlayerControl.DeleteMirror(mirrorPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
@@ -1,42 +1,97 @@
#!/usr/bin/python
# -*- coding: GBK -*-
##@package PlayerMirror
# 创建玩家镜像, 考虑地图人满问题,py可以做个预判,可调整地图配置的人数上限
#-------------------------------------------------------------------------------
#
##@package GM.Commands.PlayerMirror
#
# @todo:创建玩家镜像进行战斗
# @author hxp
# @date 2024-10-17
# @version 1.0
#
# 详细描述: 创建玩家镜像, 考虑地图人满问题,py可以做个预判,可调整地图配置的人数上限
#
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import GameWorld
import PlayerEventCounter
import ChPlayer
import GameServerRefresh
import MirrorAttack
import PlayerViewCacheTube
import PlayerFB
import ChConfig
## GM命令执行入口
#  @param curPlayer 当前玩家
#  @param playerList 参数列表 [玩家ID]
#  @param paramList 参数列表 [玩家ID]
#  @return None
#  @remarks 函数详细说明.
def OnExec(curPlayer, playerList):
    playerID = 0
    if len(playerList) != 0:
        playerID = playerList[0]
    if playerID != 0:
        #向GameServer请求其他玩家数据
        #开始查询
        curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_MirrorPlayer, playerID, 'PlayerMirror', '', 0)
def OnExec(curPlayer, paramList):
    if not paramList:
        GameWorld.DebugAnswer(curPlayer, "-------------------%s" % GameWorld.GetCurrentDataTimeStr())
        GameWorld.DebugAnswer(curPlayer, "创建战斗: PlayerMirror c 是否后台 [目标ID ID2 ...]")
        GameWorld.DebugAnswer(curPlayer, "开始战斗: PlayerMirror s")
        GameWorld.DebugAnswer(curPlayer, "跳过战斗: PlayerMirror q")
        GameWorld.DebugAnswer(curPlayer, "退出战斗: PlayerMirror e")
        GameWorld.DebugAnswer(curPlayer, "更新镜像: PlayerMirror 5")
        GameWorld.DebugAnswer(curPlayer, "是否后台:0-玩家自身参与战斗")
        GameWorld.DebugAnswer(curPlayer, "是否后台:1-玩家无感知,系统直接出结果")
        GameWorld.DebugAnswer(curPlayer, "目标ID:无-自己;>0-其他玩家ID支持跨服玩家ID")
        GameWorld.DebugAnswer(curPlayer, "目标ID多个时为多对多战斗")
        GameWorld.DebugAnswer(curPlayer, "多对多阵营分配均分AABBB即玩家和AA对BBB")
        return
    playerData = curPlayer.GetPackData()
    # playerData为base64后的数据
    mirrorPlayer = GameWorld.GetGameWorld().CreateMirrorPlayer(playerData, curPlayer.GetPosX(), curPlayer.GetPosY())
    
    #是否镜像玩家 判断 mirrorPlayer.GetRealPlayerID()是否为0
    if mirrorPlayer:
        GameWorld.Log("mirrorPlayer.GetRealPlayerID %s"%mirrorPlayer.GetRealPlayerID())
        index = mirrorPlayer.GetIndex()
    mapID = ChConfig.Def_FBMapID_MirrorBattle
    lineID = 0
    tick = GameWorld.GetGameWorld().GetTick()
    playerID = curPlayer.GetPlayerID()
    value1 = paramList[0]
    if value1 == "c":
        isSysbg = paramList[1] if len(paramList) > 1 else 0
        if not isSysbg:
            if not PlayerFB.DoEnterCustomScene(curPlayer, mapID, lineID, tick):
                GameWorld.DebugAnswer(curPlayer, "进入自定义PK创景失败:%s" % mapID)
                return
        mirrorIDList = paramList[2:]
        if not mirrorIDList:
            mirrorIDList.append(playerID)
        factionIDListA, factionIDListB = [playerID], []
        while mirrorIDList:
            # 后面为对手
            factionIDListB.append(mirrorIDList.pop(-1))
            # 前面为队友
            if mirrorIDList:
                factionIDListA.append(mirrorIDList.pop(0))
        posX, posY = curPlayer.GetPosX(), curPlayer.GetPosY()
        battlePlayerList = []
        for i, batPlayerID in enumerate(factionIDListA):
            battlePlayerList.append({"playerID":batPlayerID, "faction":1, "posX":posX, "posY":posY + i * 5})
        for i, batPlayerID in enumerate(factionIDListB):
            battlePlayerList.append({"playerID":batPlayerID, "faction":2, "posX":posX + 5, "posY":posY + i * 5})
        GameWorld.DebugAnswer(curPlayer, "创建镜像: %s VS %s" % (factionIDListA, factionIDListB))
        requestID = playerID
        MirrorAttack.OnRequestCreateMirrorBattle(mapID, lineID, requestID, battlePlayerList, isSysbg, curPlayer)
    elif value1 == "s":
        battle = MirrorAttack.GetMirrorBattle(curPlayer)
        if battle:
            MirrorAttack.OnMirrorBattleStart(battle.battleID)
    elif value1 == "q":
        battle = MirrorAttack.GetMirrorBattle(curPlayer)
        if battle:
            MirrorAttack.DoMirrorBattleQuick(battle.battleID)
    elif value1 == "e":
        PlayerFB.DoExitCustomScene(curPlayer)
    elif value1 == 5:
        tick = GameWorld.GetGameWorld().GetTick()
        ChPlayer.PlayerLogin(index, tick)
        PlayerEventCounter.GameServer_InitOK(index, tick)
        ChPlayer.LoadMapOK(index, tick)
        GameServerRefresh.GameSever_PlayerInitOK(index, tick)
        PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, forcePackData=True)
        GameWorld.DebugAnswer(curPlayer, "已更新最新镜像缓存!")
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -39,6 +39,7 @@
import IPY_GameWorld
import LogUI
import ReadChConfig
import IpyGameDataPY
import random
import math
import sys
@@ -590,6 +591,26 @@
    if curPlayer.GetGMLevel() in [0 , ChConfig.Def_GM_LV_God]:
        return
    
    return True
def IsMirrorPlayer(curPlayer):
    ## 是否镜像玩家
    if curPlayer.GetRealPlayerID() > 0:
        return True
    return False
def IsNormalPlayer(curPlayer):
    '''是否正常可用的常规玩家,一般用于判断是否是一个正常的玩家
    可用于封包通知判断、活动玩家判断等一切仅处理真实常规玩家的验证
    '''
    if not curPlayer or curPlayer.GetID() == 0 or curPlayer.IsEmpty():
        return False
    #if not curPlayer.GetInitOK():
    #    return False
    #if IsTJGPlayer(curPlayer):
    #    return False
    if IsMirrorPlayer(curPlayer):
        return False
    return True
#---------------------------------------------------------------------
@@ -1289,6 +1310,31 @@
    ## 服务器组ID,必须唯一,代表这台物理服务器
    return ToIntDef(ReadChConfig.GetPyMongoConfig("platform", "GroupID"), 0)
def GetMainServerID(serverID):
    ## 获取服务器ID所属主服ID
    ServerIDMainServerDict = IpyGameDataPY.GetConfigEx("ServerIDMainServerDict")
    if ServerIDMainServerDict == None:
        filePath = ChConfig.GetDBPath() + ("\\MixServerMap_%s.json" % GetPlatform())
        if not os.path.isfile(filePath):
            SendGameErrorEx("GetMainServerIDError", "file can not found. %s" % filePath)
        else:
            fileObj = open(filePath, 'rb')
            content = fileObj.read()
            fileObj.close()
            MixServerMapDict = eval(content)
            ServerIDMainServerDict = {}
            for mainServerIDStr, serverIDList in MixServerMapDict.items():
                mainServerID = int(mainServerIDStr)
                for sID in serverIDList:
                    ServerIDMainServerDict[sID] = mainServerID
            IpyGameDataPY.SetConfigEx("ServerIDMainServerDict", ServerIDMainServerDict)
            Log("加载 ServerIDMainServerDict=%s" % ServerIDMainServerDict)
    if not ServerIDMainServerDict:
        return serverID
    return ServerIDMainServerDict.get(serverID, serverID)
def GetPlatformServerNum(platform):
    # 获取服务器的平台编号
    platformNumDict = ReadChConfig.GetDBEvalChConfig("DBPlatformNum")
@@ -1334,7 +1380,7 @@
def GetPlayerServerID(curPlayer):
    accID = curPlayer.GetAccID()
    infoList = accID.split(Def_AccID_Split_Sign)
    return 0 if len(infoList) < 3 else int(infoList[-1][1:])
    return 0 if len(infoList) < 3 else ToIntDef(infoList[-1][1:])
def GetPlayerServerSID(curPlayer):
    # 返回含s的serverID
@@ -2337,6 +2383,14 @@
        SendGameError("MapServerRaiseException", errorMsg)
    return
def SendGameErrorEx(errType, msgInfo="", playerID=0):
    ErrLog("SendGameErrorEx: %s -> %s" % (errType, msgInfo), playerID)
    if GetGameWorld().GetDebugLevel():
        raise Exception("%s -> %s" % (errType, msgInfo))
    else:
        SendGameError(errType, msgInfo)
    return
def SendGameError(errType, msgInfo=""):
    ''' 向运维发送邮件,用于需要紧急处理的信息
    @param errType: 错误类型,自定义即可
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -368,6 +368,10 @@
#  @remarks 函数详细说明.
def DoFBOnKill_Player(curPlayer, defender, tick):
    do_FBLogic_ID = __GetFBLogic_MapID(GameWorld.GetMap().GetMapID())
    mapID = PlayerControl.GetCustomMapID(curPlayer)
    if not mapID:
        mapID = GameWorld.GetMap().GetMapID()
    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
    
    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "DoFBOnKill_Player"))
    
@@ -2502,4 +2506,15 @@
    
    return callFunc(curPlayer, mapID, funcLineID, hurtObj, hurtValue, factionHurtValue)
def OnMirrorBattleOver(battleID, mapID):
    ## 镜像战斗结束
    do_FBLogic_ID = __GetFBLogic_MapID(mapID)
    callFunc = GameWorld.GetExecFunc(FBProcess, "GameLogic_%s.%s" % (do_FBLogic_ID, "OnMirrorBattleOver"))
    if callFunc == None:
        return
    return callFunc(battleID)
    
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -2403,6 +2403,27 @@
            return dataMapID
    return mapID
def GetGeneralTrainMapIDList():
    ## 获取通用养成副本
    GeneralTrainMapIDList = IpyGameDataPY.GetConfigEx("GeneralTrainMapIDList")
    if not GeneralTrainMapIDList:
        GeneralTrainMapIDList = []
        ipyDataMgr = IpyGameDataPY.IPY_Data()
        for i in xrange(ipyDataMgr.GetFBGeneralTrainCount()):
            ipyData = ipyDataMgr.GetFBGeneralTrainByIndex(i)
            dMapID = ipyData.GetDataMapID()
            if dMapID not in GeneralTrainMapIDList:
                GeneralTrainMapIDList.append(dMapID)
        GeneralTrainMapIDList = IpyGameDataPY.SetConfigEx("GeneralTrainMapIDList", GeneralTrainMapIDList)
        #GameWorld.Log("加载GeneralTrainMapIDList=%s" % GeneralTrainMapIDList)
    return GeneralTrainMapIDList
def GetClientCustomScene():
    ## 获取前端自定义副本场景
    mapIDList = GetGeneralTrainMapIDList()
    return mapIDList + ChConfig.ClientCustomSceneList
## 同步进入副本时间
#  @param curPlayer 玩家
#  @param syncMapID 同步的地图,默认0为全部
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
New file
@@ -0,0 +1,73 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#-------------------------------------------------------------------------------
#
##@package GameWorldLogic.FBProcess.GameLogic_MirrorBattle
#
# @todo:镜像PK切磋
# @author hxp
# @date 2024-10-17
# @version 1.0
#
# 详细描述: 镜像PK切磋
#
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import GameWorld
import MirrorAttack
import FBCommon
import GameObj
## 是否能够通过活动查询进入
def OnEnterFBEvent(curPlayer, mapID, lineID, tick):
    return True
## 客户端进入自定义场景
def OnEnterCustomScene(curPlayer, mapID, lineID):
    return
##处理副本中杀死玩家逻辑
def DoFBOnKill_Player(atkobj, defender, tick):
    GameWorld.DebugLog("镜像切磋击杀玩家: defID=%s" % (defender.GetID()), atkobj.GetID())
    return True
def OnMirrorBattleOver(battleID):
    ## 镜像战斗结束
    battle = MirrorAttack.GetMirrorBattleByID(battleID)
    if not battle:
        return
    isLogout = battle.isLogout
    mapID = battle.mapID
    funcLineID = battle.funcLineID
    winFaction = battle.winFaction
    curPlayerID = battle.requestID # 副本所属玩家ID,该玩家不一定参与实际战斗
    curIsWin = 0
    GameWorld.DebugLog("镜像战斗结算: mapID=%s,funcLineID=%s,winFaction=%s,isLogout=%s" % (mapID, funcLineID, winFaction, isLogout), battleID)
    playerMgr = GameWorld.GetMapCopyPlayerManager()
    for playerID, faction in battle.playerFactionDict.items():
        curPlayer = playerMgr.FindPlayerByID(playerID)
        if not curPlayer:
            continue
        realPlayerID = curPlayer.GetRealPlayerID()
        isWin = (faction == winFaction)
        GameWorld.DebugLog("剩余血量: %s/%s,playerID=%s,realPlayerID=%s,faction=%s,isWin=%s"
                           % (GameObj.GetHP(curPlayer), GameObj.GetMaxHP(curPlayer), playerID, realPlayerID, faction, isWin), battleID)
        if isWin and faction == 1:
            curIsWin = 1
    if not curPlayerID:
        return
    curPlayer = playerMgr.FindPlayerByID(curPlayerID)
    if not curPlayer:
        return
    # 结算奖励,通知结果
    giveItemList = []
    overDict = {"isWin":curIsWin, FBCommon.Over_itemInfo:FBCommon.GetJsonItemList(giveItemList)}
    FBCommon.NotifyFBOver(curPlayer, mapID, funcLineID, isWin, overDict)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -86,6 +86,11 @@
                        ("list", "StarAttrValue", 0),
                        ),
                "SkillMatch":(
                        ("BYTE", "IDIndex", 1),
                        ("list", "Skills", 0),
                        ),
                "CreateRole":(
                        ("BYTE", "RoleType", 1),
                        ("list", "BaseAttrIDList", 0),
@@ -2695,6 +2700,16 @@
    def GetStarUpNeedItemList(self): return self.attrTuple[2] # 升该星所需物品 [[物品ID,个数], ...] list
    def GetStarAttrType(self): return self.attrTuple[3] # 累计总属性类型 list
    def GetStarAttrValue(self): return self.attrTuple[4] # 累计总属性值 list
# 技能搭配表
class IPY_SkillMatch():
    def __init__(self):
        self.attrTuple = None
        return
    def GetIDIndex(self): return self.attrTuple[0] # BYTE
    def GetSkills(self): return self.attrTuple[1] # 主动技能(职业1id|职业2id) list
# 创角表
class IPY_CreateRole():
@@ -6557,6 +6572,7 @@
        self.__LoadFileData("PlayerFaceStar", onlyCheck)
        self.__LoadFileData("PlayerFacePic", onlyCheck)
        self.__LoadFileData("PlayerFacePicStar", onlyCheck)
        self.__LoadFileData("SkillMatch", onlyCheck)
        self.__LoadFileData("CreateRole", onlyCheck)
        self.__LoadFileData("RolePoint", onlyCheck)
        self.__LoadFileData("LingQiAttr", onlyCheck)
@@ -7059,6 +7075,13 @@
        self.CheckLoadData("PlayerFacePicStar")
        return self.ipyPlayerFacePicStarCache[index]
    def GetSkillMatchCount(self):
        self.CheckLoadData("SkillMatch")
        return self.ipySkillMatchLen
    def GetSkillMatchByIndex(self, index):
        self.CheckLoadData("SkillMatch")
        return self.ipySkillMatchCache[index]
    def GetCreateRoleCount(self):
        self.CheckLoadData("CreateRole")
        return self.ipyCreateRoleLen
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py
@@ -161,7 +161,7 @@
#  @return 返回值无意义
#  @remarks 刷新装备对人物属性的改变
def RefreshPlayerEquipAttribute(curPlayer, classLV=0):
    GameWorld.DebugLog("Start RefreshPlayerEquipAttribute classLV=%s!!!" % classLV)
    GameWorld.DebugLog("Start RefreshPlayerEquipAttribute classLV=%s!!!" % classLV, curPlayer.GetPlayerID())
    classlvList = xrange(1, IpyGameDataPY.GetFuncCfg('EquipMaxClasslv') + 1) if classLV == 0 else [classLV]
    for rclasslv in classlvList:
        __CalcEquips_Effect(curPlayer, rclasslv)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py
@@ -41,7 +41,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActAllRecharge(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py
@@ -44,7 +44,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActCTGBillboard(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py
@@ -154,7 +154,7 @@
    for i in range(0, playerManager.GetActivePlayerCount()):
        curPlayer = playerManager.GetActivePlayerByIndex(i)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerControl.Sync_ExpRateChange(curPlayer)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py
@@ -46,18 +46,18 @@
    OSCBillboardDataLimitDict = IpyGameDataPY.GetFuncEvalCfg("OSCBillboardOpen", 1)
    campTypeS = str(campaignType)
    if campTypeS not in OSCBillboardDataLimitDict:
        GameWorld.DebugLog("不存在该开服活动类型: %s" % campaignType)
        #GameWorld.DebugLog("不存在该开服活动类型: %s" % campaignType)
        return False
    limitValue = OSCBillboardDataLimitDict[campTypeS][OSC_BillLimitValue]
    endOpenServerDay = OSCBillboardDataLimitDict[campTypeS][OSC_EndDay]
    
    openServerDay = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_ServerDay) + 1
    if openServerDay > endOpenServerDay:
        GameWorld.DebugLog("该开服活动已结束,无法上榜!campaignType=%s,openServerDay=%s > endOpenServerDay=%s" % (campaignType, openServerDay, endOpenServerDay))
        #GameWorld.DebugLog("该开服活动已结束,无法上榜!campaignType=%s,openServerDay=%s > endOpenServerDay=%s" % (campaignType, openServerDay, endOpenServerDay))
        return False
    
    if curValue != None and curValue < limitValue:
        GameWorld.DebugLog("该开服活动数值不足,无法上榜!campaignType=%s,curValue=%s < limitValue=%s" % (campaignType, curValue, limitValue))
        #GameWorld.DebugLog("该开服活动数值不足,无法上榜!campaignType=%s,curValue=%s < limitValue=%s" % (campaignType, curValue, limitValue))
        return False
    return True
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py
@@ -90,7 +90,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerBossTrialAction(curPlayer, actNum)
    return
@@ -133,7 +133,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActBossTrial(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py
@@ -93,7 +93,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerBuyCountGiftAction(curPlayer, actNum)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py
@@ -47,7 +47,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerBuyOneAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py
@@ -54,7 +54,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCollectWordsAction(curPlayer, actNum)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py
@@ -44,7 +44,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFamilyCTGAssistAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py
@@ -44,7 +44,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerGarbageSortingAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py
@@ -40,7 +40,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerGodGiftAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py
@@ -39,7 +39,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerGrowupBuyAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py
@@ -64,7 +64,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerGubaoAction(curPlayer, actNum)
    return
@@ -107,7 +107,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActGubao(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py
@@ -42,7 +42,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        SyncHorsePetFeastInfo(curPlayer, actNum)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py
@@ -65,7 +65,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerHorsePetTrainAction(curPlayer, actNum)
    return
@@ -108,7 +108,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActHorsePetTrain(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py
@@ -47,7 +47,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerLoginAwardAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py
@@ -71,7 +71,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerActLoginAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py
@@ -57,7 +57,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerManyDayRechargeAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py
@@ -49,7 +49,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerRechargePrizeAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py
@@ -39,7 +39,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerRechargeRebateGoldAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py
@@ -55,7 +55,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerSingleRechargeAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py
@@ -69,7 +69,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerActTaskAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py
@@ -57,7 +57,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerTotalRechargeAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py
@@ -69,7 +69,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerTurntableAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py
@@ -67,7 +67,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerXianXiaMJAction(curPlayer, actNum)
    return
@@ -143,7 +143,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCrossActXianXiaMJ(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py
@@ -353,7 +353,7 @@
    hadInit = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_HadInitFruitAttr)
    if hadInit:
        return
    GameWorld.DebugLog("__InitPlayerAttrFruit")
    #GameWorld.DebugLog("__InitPlayerAttrFruit")
    
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    maxCnt = ipyDataMgr.GetAttrFruitCount()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py
@@ -41,6 +41,9 @@
def UpdatePlayerBillboardOnLeaveServer(curPlayer, isAll=False):
    ##下线更新玩家排行榜
    if GameWorld.IsCrossServer():
        # 跨服服务器不用更新本服榜
        return
    #UpdateTotalRechargeBillboard(curPlayer)
    
    UpdatePlayerLVBillboard(curPlayer) # 等级榜
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py
@@ -46,7 +46,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerBossRebornAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
@@ -24,6 +24,7 @@
import PlayerTrade
import PlayerTeam
import SkillCommon
import MirrorAttack
import GameMap
import FBLogic
import GameWorldProcess
@@ -1213,6 +1214,37 @@
    
    return
#---------------------------------------------------------------------
def OnDelayDeleteMirror(curPlayer, tick):
    addTick = curPlayer.GetDictByKey("DelayDeleteMirror")
    if not addTick:
        return
    if tick - addTick < 10000: # 10s后回收
        return
    DeleteMirror(curPlayer, False)
    return True
def DeleteMirror(curPlayer, isDelay=False):
    ''' 回收镜像玩家
    '''
    if not curPlayer.GetRealPlayerID():
        return
    playerID = curPlayer.GetPlayerID()
    if isDelay:
        #由于系统场次快速战斗原因,如果立马回收可能导致某个功能出现问题
        #如被动技能效果,执行顺序的原因,如击杀后 -> 结算 -> 回收玩家 -> 触发被动技能,导致异常
        GameWorld.DebugLog("镜像玩家设置延迟回收!", playerID)
        SetPlayerSightLevel(curPlayer, playerID)
        tick = GameWorld.GetGameWorld().GetTick()
        curPlayer.SetDict("DelayDeleteMirror", tick)
        return
    GameWorld.DebugLog("镜像玩家回收!", playerID)
    #杀死所有召唤的灵
    KillPlayerSummonNPC(curPlayer)
    #召唤回出战的宠物
    PetControl.ReCallFightPet(curPlayer)
    curPlayer.DeleteMirror()
    return
##杀死玩家所有的召唤兽死亡
# @param curPlayer 玩家实例
# @return 返回值无意义
@@ -1323,6 +1355,8 @@
    UpdateOnLineTime(curPlayer, tick)
    
    PassiveBuffEffMng.OnPlayerLeaveMap(curPlayer)
    MirrorAttack.OnPlayerLeaveMap(curPlayer)
    
    #离开地图清空恶意攻击自己玩家信息
    if curPlayer.GetPlayerID() in PyGameData.g_maliciousAttackDict:
@@ -4584,7 +4618,9 @@
    #  @return 返回值无意义
    #  @remarks 刷新玩家所有状态
    def RefreshAllState(self, isSyncBuff=False, billboardFunc=None, isForce=False):
        GameWorld.DebugLog("Start RefreshAllState!!!")
        curPlayer = self.__Player
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("Start RefreshAllState!!!", playerID)
        
        self.RefreshPlayerActionState()
        self.RefreshPlayerAttrState(billboardFunc, isForce)
@@ -4596,8 +4632,9 @@
    #  @return 返回值无意义
    #  @remarks 刷新玩家所有状态
    def ReCalcAllState(self):
        GameWorld.DebugLog("Start ReCalcAllState!!!")
        curPlayer = self.__Player
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("Start ReCalcAllState!!!", playerID)
        
        for funcIndex in ChConfig.CalcAttrFuncList:
            ClearCalcAttrListValue(curPlayer, funcIndex)
@@ -4632,7 +4669,7 @@
        PlayerFace.CalcFaceAttr(curPlayer)
        PlayerFace.CalcFacePicAttr(curPlayer)
        self.RefreshAllState(isForce=True)
        GameWorld.DebugLog("End ReCalcAllState!!!")
        GameWorld.DebugLog("End ReCalcAllState!!!", playerID)
        return
    
    
@@ -4652,14 +4689,15 @@
        if curPlayer.GetDictByKey(ChConfig.Def_Player_RefreshAttrByBuff) != 1:
            return
        
        GameWorld.DebugLog("Start RefreshPlayerAttrByBuffEx!!!")
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("Start RefreshPlayerAttrByBuffEx!!!", playerID)
        beforeMaxHP = GameObj.GetMaxHP(curPlayer)
        beforeMoveSpeedValue = GetSpeedValue(curPlayer)
        #构建玩家刷新通知客户端字典, 缓存[索引, 数值]
        playerStateDict = {}
        for index in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
            playerStateDict.update({index:EffGetSet.GetValueByEffIndex(curPlayer, index)})
        #GameWorld.DebugLog("刷属性前=%s" % playerStateDict)
        #GameWorld.DebugLog("刷属性前=%s" % playerStateDict, playerID)
        #self.PrintAttr(curPlayer, "ˢ֮ǰ")
        #---------------------------开始计算-------------------------------------
        curPlayer.BeginRefreshState()
@@ -4673,7 +4711,7 @@
        self.__DoRefreshAttrAfterLogic(beforeMaxHP, beforeMoveSpeedValue, playerStateDict)
        
        curPlayer.SetDict(ChConfig.Def_Player_RefreshAttrByBuff, 0)
        GameWorld.DebugLog("End RefreshPlayerAttrByBuffEx!!!")
        GameWorld.DebugLog("End RefreshPlayerAttrByBuffEx!!!", playerID)
        return
    
    
@@ -4745,7 +4783,8 @@
        
        PlayerGubao.CalcGubaoAttr(curPlayer) # 古宝定位为对贯通所有游戏功能系统的属性玩法,所以每次都重新刷新
        
        GameWorld.DebugLog("Start RefreshPlayerAttrStateEx!!!")
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("Start RefreshPlayerAttrStateEx!!!", playerID)
        
        #beforeAtkInterval = curPlayer.GetAtkInterval()
        beforeMaxHP = GameObj.GetMaxHP(curPlayer)
@@ -4757,7 +4796,7 @@
        playerStateDict = {}
        for index in xrange(1, ChConfig.Def_Calc_AllAttrType_MAX):
            playerStateDict.update({index:EffGetSet.GetValueByEffIndex(curPlayer, index)})
        #GameWorld.DebugLog("刷属性前=%s" % playerStateDict)
        #GameWorld.DebugLog("刷属性前=%s" % playerStateDict, playerID)
        #self.PrintAttr(curPlayer, "ˢ֮ǰ")
        #---------------------------开始计算-------------------------------------
        curPlayer.BeginRefreshState()
@@ -4784,7 +4823,7 @@
            attrInfo, insidePerAttrDict = GetCalcAttrListValue(curPlayer, funcIndex)[:2]
            if attrInfo == notAttrList and not insidePerAttrDict:
                continue
            GameWorld.DebugLog("功能点属性: %s(%s), %s, 内层百分比附加: %s" % (funcIndex, ChConfig.FuncIndexName.get(funcIndex, ""), attrInfo, insidePerAttrDict))
            GameWorld.DebugLog("功能点属性: %s(%s), %s, 内层百分比附加: %s" % (funcIndex, ChConfig.FuncIndexName.get(funcIndex, ""), attrInfo, insidePerAttrDict), playerID)
            funcAttrInfoList[funcIndex] = attrInfo
            funcInsidePerAttrList[funcIndex] = insidePerAttrDict
            # 不同功能点间的数值累加,需使用支持衰减递增的计算方式
@@ -4794,7 +4833,7 @@
        #    2.2 将基础属性累加到玩家身上
        if baseAttrDict or baseAttrNolineDict:
            # 因为基础属性会影响战斗属性计算,所以先统计增加基础属性
            GameWorld.DebugLog("功能附加点: baseAttrDict=%s,baseAttrNolineDict=%s" % (baseAttrDict, baseAttrNolineDict))
            GameWorld.DebugLog("功能附加点: baseAttrDict=%s,baseAttrNolineDict=%s" % (baseAttrDict, baseAttrNolineDict), playerID)
            CalcLineEffect.ChangePlayerAttrInLineEffectList(curPlayer, baseAttrDict)
            CalcNoLineEffect.ChangePlayerAttrInNoLineEffectList(curPlayer, baseAttrNolineDict)
            
@@ -4807,11 +4846,11 @@
        funcAttrInfoList[ChConfig.Def_CalcAttrFunc_LingGenQuailty] =  lingGenQualityAttrList
        funcInsidePerAttrList[ChConfig.Def_CalcAttrFunc_LingGenQuailty] =  lingGenQualityInsidePerAttrDict
        GameWorld.DebugLog("功能点属性: %s(%s), %s, 内层百分比附加: %s" 
                           % (ChConfig.Def_CalcAttrFunc_RoleBase, ChConfig.FuncIndexName.get(ChConfig.Def_CalcAttrFunc_RoleBase, ""), roleBaseAttrInfo, roleInsidePerAttrDict))
                           % (ChConfig.Def_CalcAttrFunc_RoleBase, ChConfig.FuncIndexName.get(ChConfig.Def_CalcAttrFunc_RoleBase, ""), roleBaseAttrInfo, roleInsidePerAttrDict), playerID)
        GameWorld.DebugLog("功能点属性: %s(%s), %s, 内层百分比附加: %s" 
                           % (ChConfig.Def_CalcAttrFunc_LingGenQuailty, ChConfig.FuncIndexName.get(ChConfig.Def_CalcAttrFunc_LingGenQuailty, ""), lingGenQualityAttrList, lingGenQualityInsidePerAttrDict))
                           % (ChConfig.Def_CalcAttrFunc_LingGenQuailty, ChConfig.FuncIndexName.get(ChConfig.Def_CalcAttrFunc_LingGenQuailty, ""), lingGenQualityAttrList, lingGenQualityInsidePerAttrDict), playerID)
        
        #self.PrintAttr(curPlayer, "基础后")
        #self.PrintAttr(curPlayer, "基础后", playerID)
        
        # 3.计算战斗属性
        #    3.1 战斗属性层级交叉影响基值数值汇总
@@ -4820,7 +4859,7 @@
                                         funcAttrInfoList[ChConfig.Def_CalcAttrFunc_LingGen],
                                         funcAttrInfoList[ChConfig.Def_CalcAttrFunc_LingGenQuailty],
                                         ])
        #GameWorld.DebugLog("基础层级: %s" % baseAttrList)
        #GameWorld.DebugLog("基础层级: %s" % baseAttrList, playerID)
        
        #        功能点交叉影响非线性层对应属性基值列表
        funcAttrPerInfo = {ChConfig.TYPE_Calc_BaseAtkAddPer:[baseAttrList],
@@ -4866,7 +4905,7 @@
            if not battleNoLineAttrDict:
                continue
            addAttrDict = {}
            #GameWorld.DebugLog("交叉功能点: i=%s,%s" % (i,battleNoLineAttrDict))
            #GameWorld.DebugLog("交叉功能点: i=%s,%s" % (i,battleNoLineAttrDict), playerID)
            for noLineAttrType, addAttrPer in battleNoLineAttrDict.items():
                if noLineAttrType not in funcAttrPerInfo or noLineAttrType not in ChConfig.FuncNoLinearAttrDict:
                    continue
@@ -4893,7 +4932,7 @@
                # 如符文有个武器攻击百分比增加属性,增加的数值属于符文功能,不属于武器功能点的,只是基值使用了武器攻击     
                funcCrossAttrPerInfoDict[i] = addAttrDict # 先都统计完后再累加到对应功能属性里,不然可能会导致功能基值变更
                
        GameWorld.DebugLog("交叉影响属性: %s" % funcCrossAttrPerInfoDict)
        GameWorld.DebugLog("交叉影响属性: %s" % funcCrossAttrPerInfoDict, playerID)
        
        #    3.3 统计所有功能固定属性影响累加
        allFixAttrDict = {} # 固定属性层级总属性基值
@@ -4917,8 +4956,8 @@
                addValueExDict[fixAttrType] = addValueEx
            fixAttrPerAddExDict[funcIndex] = addValueExDict
            
        GameWorld.DebugLog("固定属性总和: %s" % allFixAttrDict)
        GameWorld.DebugLog("固定百分比附加属性: %s" % fixAttrPerAddExDict)
        GameWorld.DebugLog("固定属性总和: %s" % allFixAttrDict, playerID)
        GameWorld.DebugLog("固定百分比附加属性: %s" % fixAttrPerAddExDict, playerID)
        
        # 4. 计算属性及战力, 需在计算buff层之前计算
        curLV = curPlayer.GetLV()
@@ -4971,14 +5010,14 @@
            elif mfpType == ShareDefine.Def_MFPType_Wash:
                OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_Wash, mfpTotal)
                
        #GameWorld.DebugLog("整体层级线性属性: %s" % allAttrList)
        #GameWorld.DebugLog("整体层级线性属性: %s" % allAttrList, playerID)
        
        # 5.被动技能附加属性,不算战力
        passiveSkillAttrList = [{} for _ in range(4)]
        SkillShell.CalcPassiveAttr_Effect(curPlayer, passiveSkillAttrList)  # 属性类技能与buff同层
        for funcIndex in ChConfig.CalcAttrFuncSkillList:
            passiveSkillAttrList = AddAttrListValue([passiveSkillAttrList, funcAttrInfoList[funcIndex]])    
        GameWorld.DebugLog("无战力被动属性: %s" % passiveSkillAttrList)
        GameWorld.DebugLog("无战力被动属性: %s" % passiveSkillAttrList, playerID)
        
        skillFixAttrExDict = {}
        skillFixAddPerDict = passiveSkillAttrList[ChConfig.CalcAttr_BattleNoline]
@@ -5030,14 +5069,14 @@
        while billFuncCnt > 0 and PyGameData.g_refreshAttrBillboardFunc:
            billFuncCnt -= 1
            billboardFunc = PyGameData.g_refreshAttrBillboardFunc.pop(0)
            GameWorld.DebugLog("回调排行榜: %s" % billboardFunc)
            GameWorld.DebugLog("回调排行榜: %s" % billboardFunc, playerID)
            billboardFunc(curPlayer, isForceUpdate=True)
            
        # 暂停刷新属性标志,必须被调用
        curPlayer.SetDict(ChConfig.Def_Player_RefreshAttr, 0)
        curPlayer.SetDict(ChConfig.Def_Player_RefreshAttrByBuff, 0)
        curPlayer.SetDict(ChConfig.Def_Player_HadRefreshAttr, 1)
        GameWorld.DebugLog("End RefreshPlayerAttrStateEx!!!")
        GameWorld.DebugLog("End RefreshPlayerAttrStateEx!!!", playerID)
        return True
    
    
@@ -5072,6 +5111,7 @@
    def __RefreshBuffAttr(self):
        ## 刷新buff层属性,该层属性只会改变玩家最终属性,不会影响战力等
        curPlayer = self.__Player
        playerID = curPlayer.GetPlayerID()
        allAttrListBuffs = [{} for _ in range(4)]
        # 6.计算buff属性, buff层级的不算如战斗力
        #        算战斗力总值时该层影响的数值不统计,但刷属性时需计算
@@ -5095,7 +5135,7 @@
        
        #        刷新攻击速度
        self.__SetAtkInterval()
        GameWorld.DebugLog("Buff层属性: %s" % allAttrListBuffs)
        GameWorld.DebugLog("Buff层属性: %s" % allAttrListBuffs, playerID)
        return
    
    def __DoRefreshGMAttr(self):
@@ -5339,8 +5379,9 @@
    def SendModuleFightPowerPack(self, curPlayer, mfpDict):
        mfpDataList = []
        totalFightPower = 0
        GameWorld.DebugLog("战力功能点: %s" % ChConfig.MFPTypeAttrFuncIndexDict)
        GameWorld.DebugLog("模块战力: %s" % mfpDict)
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("战力功能点: %s" % ChConfig.MFPTypeAttrFuncIndexDict, playerID)
        GameWorld.DebugLog("模块战力: %s" % mfpDict, playerID)
        for mfpType, fightPower in mfpDict.items():
            SetMFPFightPower(curPlayer, mfpType, fightPower)
            mfpData = ChPyNetSendPack.tagMCModuleFightPower()
@@ -5373,7 +5414,7 @@
                                 ChConfig.Def_PDictType_FightPower)
            NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FightPower_HighestEx, highestFightPower / ChConfig.Def_PerPointValue,
                                 ChConfig.Def_PDictType_FightPower)
        GameWorld.DebugLog("总战力: %s, 历史最高战力: %s, beforeFightPower=%s" % (totalFightPower, highestFightPower, beforeFightPower))
        GameWorld.DebugLog("总战力: %s, 历史最高战力: %s, beforeFightPower=%s" % (totalFightPower, highestFightPower, beforeFightPower), playerID)
        PlayerBillboard.UpdatePlayerFPTotalBillboard(curPlayer)
        # 记录开服活动数据,开服活动前X天理论上不会超过20E,暂不处理
        OpenServerCampaign.UpdOpenServerCampaignRecordData(curPlayer, ShareDefine.Def_Campaign_Type_FightPower, min(totalFightPower, ChConfig.Def_UpperLimit_DWord))
@@ -5384,21 +5425,22 @@
    def __RefreshMoveSpeed(self, allAttrListBuffs):
        ## 刷新移动速度
        curPlayer = self.__Player
        playerID = curPlayer.GetPlayerID()
        
        moveSpeedFormat = IpyGameDataPY.GetFuncCfg("MoveSpeed")
        
        if PlayerTruck.GetHaveAutoTruck(curPlayer):
            speed = IpyGameDataPY.GetFuncCfg("MoveSpeed", 3)
            GameWorld.DebugLog("运镖固定速度值: speed=%s" % speed)
            GameWorld.DebugLog("运镖固定速度值: speed=%s" % speed, playerID)
        else:
            speed = GetSpeedNotBuff(curPlayer)
            GameWorld.DebugLog("功能移动速度值: speed=%s" % speed)
            GameWorld.DebugLog("功能移动速度值: speed=%s" % speed, playerID)
            
            # 骑乘状态加上骑乘附加速度
            if curPlayer.GetPlayerVehicle() == IPY_GameWorld.pvHorse:
                speedHorse = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_SpeedHorse)
                speed += speedHorse
                GameWorld.DebugLog("    骑乘状态附加值: %s, speed=%s" % (speedHorse, speed))
                GameWorld.DebugLog("    骑乘状态附加值: %s, speed=%s" % (speedHorse, speed), playerID)
            
                
            buffBattleAttr = allAttrListBuffs[ChConfig.CalcAttr_Battle]
@@ -5409,14 +5451,14 @@
            
            if buffSpeed or buffSpeedPer:
                speed = int(speed * (ShareDefine.Def_MaxRateValue + buffSpeedPer) / float(ShareDefine.Def_MaxRateValue) + buffSpeed)
                GameWorld.DebugLog("    buff影响后速度值: speed=%s,buffSpeedPer=%s,buffSpeed=%s" % (speed, buffSpeedPer, buffSpeed))
                GameWorld.DebugLog("    buff影响后速度值: speed=%s,buffSpeedPer=%s,buffSpeed=%s" % (speed, buffSpeedPer, buffSpeed), playerID)
                
            speed = max(speed, 0)   #防小于0错误
        if GetSpeedValue(curPlayer) != speed:
            SetSpeedValue(curPlayer, speed)
            moveSpeed = eval(FormulaControl.GetCompileFormula("MoveSpeed", moveSpeedFormat))
            curPlayer.SetSpeed(moveSpeed)
            GameWorld.DebugLog("公式计算后移动频率: moveSpeed=%s 毫秒/格" % moveSpeed)
            GameWorld.DebugLog("公式计算后移动频率: moveSpeed=%s 毫秒/格" % moveSpeed, playerID)
            
            fightPet = curPlayer.GetPetMgr().GetFightPet()
            #无出战宠物
@@ -5444,9 +5486,10 @@
    #  @return 返回值无意义
    #  @remarks 刷新玩家所有行为BUFF状态
    def RefreshPlayerActionState(self):
        GameWorld.DebugLog("Start RefreshPlayerActionState!!!")
        #curTime = time.clock()
        curPlayer = self.__Player
        playerID = curPlayer.GetPlayerID()
        GameWorld.DebugLog("Start RefreshPlayerActionState!!!", playerID)
        #curTime = time.clock()
        
        #先清除所有状态
        OperControlManager.ClearObjActionState(curPlayer)
@@ -5644,6 +5687,8 @@
        
        #PlayerTJG.PlayerTJGReborn(curPlayer, tick)
        GameObj.ClearPyPlayerState(curPlayer)
        MirrorAttack.OnPlayerDead(curPlayer)
        return
    
    
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py
@@ -54,7 +54,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerCostRebateAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py
@@ -125,7 +125,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.OnlineCount()):
        curPlayer = playerManager.OnlineAt(i)
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        #检查重置玩家信息
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py
@@ -39,7 +39,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerDailyGiftbagAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -207,7 +207,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnDay(curPlayer)
@@ -225,7 +225,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnDayEx(curPlayer)
@@ -275,7 +275,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnHour(curPlayer)
@@ -319,7 +319,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnWeek(curPlayer)
@@ -335,7 +335,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnWeekEx(curPlayer)
@@ -380,7 +380,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnMonth(curPlayer)
@@ -394,7 +394,7 @@
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnMonthEx(curPlayer)
@@ -439,7 +439,7 @@
    for i in range(0, playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        
        if not curPlayer or curPlayer.IsEmpty():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        PlayerOnYear(curPlayer)
@@ -1580,7 +1580,7 @@
            playerManager = GameWorld.GetPlayerManager()
            for index in xrange(playerManager.GetPlayerCount()):
                curPlayer = playerManager.GetPlayerByIndex(index)
                if curPlayer.GetID() == 0:
                if not GameWorld.IsNormalPlayer(curPlayer):
                    continue
                ChPlayer.Sync_PyServerDataTimeToClient(curPlayer)
            return
@@ -1679,7 +1679,7 @@
            playerManager = GameWorld.GetPlayerManager()
            for index in xrange(playerManager.GetPlayerCount()):
                curPlayer = playerManager.GetPlayerByIndex(index)
                if curPlayer.GetID() == 0:
                if not GameWorld.IsNormalPlayer(curPlayer):
                    continue
                PlayerWorldAverageLv.UpdatePlayerWorldAverageLv(curPlayer)
            
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
@@ -38,6 +38,7 @@
import ShareDefine
import GameFuncComm
import FBHelpBattle
import MirrorAttack
import SkillShell
import PyGameData
import PetControl
@@ -496,7 +497,7 @@
                          % (curMapID, curLineID), playerID)
            result = 0
        StartCustomSceneResult(curPlayer, mapID, lineID, result)
        return
        return result
    
    #进入副本通用检查
    if mapID:
@@ -504,7 +505,7 @@
        fbLineIpyData = FBCommon.GetFBLineIpyData(mapID, lineID)
        if PlayerControl.CheckMoveToFB(curPlayer, mapID, lineID, fbIpyData, fbLineIpyData, tick) != ShareDefine.EntFBAskRet_OK:
            StartCustomSceneResult(curPlayer, mapID, lineID, 0)
            return
            return 0
    
    PlayerControl.SetPlayerSightLevel(curPlayer, curPlayer.GetID())
        
@@ -512,6 +513,7 @@
    curPlayer.SetDict(ChConfig.Def_PlayerKey_ClientCustomSceneStepTick, tick)
    PlayerControl.SetCustomMap(curPlayer, mapID, lineID)
    NPCCommon.ClearPriWoodPile(curPlayer)
    MirrorAttack.ClearMirrorBattleByPlayer(curPlayer)
    GameWorld.Log("玩家开始自定义场景!mapID=%s,lineID=%s" % (mapID, lineID), playerID)
    if mapID:
        #PetControl.DoLogic_PetLoadMapOK(curPlayer)
@@ -523,7 +525,7 @@
        
    #通知进入状态
    StartCustomSceneResult(curPlayer, mapID, lineID, 1)
    return
    return 1
def StartCustomSceneResult(curPlayer, mapID, lineID, result):
    if result != 1:
@@ -547,6 +549,7 @@
    if mapID and FBCommon.GetCustomMapStep(curPlayer, mapID, lineID) != ChConfig.CustomMapStep_Over:
        FBCommon.SetCustomMapStep(curPlayer, mapID, lineID, ChConfig.CustomMapStep_Over)
    NPCCommon.ClearPriWoodPile(curPlayer)
    MirrorAttack.ClearMirrorBattleByPlayer(curPlayer)
    
    #默认回满血
    if GameObj.GetHP(curPlayer) > 0 and curPlayer.GetPlayerAction() != IPY_GameWorld.paDie and GameObj.GetHP(curPlayer) < GameObj.GetMaxHP(curPlayer):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py
@@ -52,7 +52,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFairyCeremonyAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py
@@ -68,7 +68,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        
        if familyID and curPlayer.GetFamilyID() != familyID:
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py
@@ -42,7 +42,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFeastLoginAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py
@@ -39,7 +39,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.OnlineCount()):
        curPlayer = playerManager.OnlineAt(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFeastRedPacketAction(curPlayer)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py
@@ -41,7 +41,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFeastTravelAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py
@@ -50,7 +50,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFeastWeekPartyAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py
@@ -44,7 +44,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFeastWishAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py
@@ -55,7 +55,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerFlashGiftbagAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py
@@ -66,7 +66,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerflashSaleAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py
@@ -123,7 +123,7 @@
        
    # 附加战力
    curPlayer.SetDict(ChConfig.Def_PlayerKey_MFPEx % ShareDefine.Def_MFPType_GodWeapon, fightPowerEx)
    GameWorld.DebugLog("神兵属性:%s" % allAttrList)
    #GameWorld.DebugLog("神兵属性:%s" % allAttrList)
    # 保存计算值
    PlayerControl.SetCalcAttrListValue(curPlayer, ChConfig.Def_CalcAttrFunc_GodWeapon, allAttrList)   
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py
@@ -58,6 +58,10 @@
    ## 挂机定时处理收益
    if GameWorld.IsCrossServer():
        return
    if curPlayer.GetRealPlayerID() != 0:
        return
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Guaji):
        return
    
@@ -89,7 +93,7 @@
    curAwardSeconds = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GuajiAwardSeconds)
    maxSeconds = GetGuajiSecondsMax(curPlayer)
    if curAwardSeconds >= maxSeconds:
        GameWorld.DebugLog("挂机收益时长已达上限: curAwardSeconds=%s >= %s" % (curAwardSeconds, maxSeconds), playerID)
        #GameWorld.DebugLog("挂机收益时长已达上限: curAwardSeconds=%s >= %s" % (curAwardSeconds, maxSeconds), playerID)
        return
    
    awardSeconds = min(maxSeconds - curAwardSeconds, awardSeconds)
@@ -100,7 +104,7 @@
    
    updAwardSeconds = curAwardSeconds + awardSeconds
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiAwardSeconds, updAwardSeconds)
    GameWorld.DebugLog("保存挂机累计收益: curAwardSeconds=%s,updAwardSeconds=%s,maxSeconds=%s" % (curAwardSeconds, updAwardSeconds, maxSeconds), playerID)
    #GameWorld.DebugLog("保存挂机累计收益: curAwardSeconds=%s,updAwardSeconds=%s,maxSeconds=%s" % (curAwardSeconds, updAwardSeconds, maxSeconds), playerID)
    
    # 经验
    exp = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GuajiExpPoint) * ChConfig.Def_PerPointValue \
@@ -109,7 +113,7 @@
    updExp = exp % ChConfig.Def_PerPointValue
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiExpPoint, updExpPoint)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiExp, updExp)
    GameWorld.DebugLog("    累计经验: %s亿%s" % (updExpPoint, updExp), playerID)
    #GameWorld.DebugLog("    累计经验: %s亿%s" % (updExpPoint, updExp), playerID)
    
    # 货币
    for moneyType, addValue in giveMoneyDict.items():
@@ -120,7 +124,7 @@
        updMoney = min(moneyValue + addValue, ChConfig.Def_UpperLimit_DWord)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiMoneyType % saveNum, moneyType)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiMoneyValue % saveNum, updMoney)
        GameWorld.DebugLog("    累计货币: moneyType=%s,updMoney=%s,saveNum=%s" % (moneyType, updMoney, saveNum), playerID)
        #GameWorld.DebugLog("    累计货币: moneyType=%s,updMoney=%s,saveNum=%s" % (moneyType, updMoney, saveNum), playerID)
        
    # 物品
    for itemID, addCount in giveItemDict.items():
@@ -131,7 +135,7 @@
        updCount = min(curCount + addCount, ChConfig.Def_UpperLimit_DWord)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiItemID % saveNum, itemID)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiItemCount % saveNum, updCount)
        GameWorld.DebugLog("    累计物品: itemID=%s,updCount=%s,saveNum=%s" % (itemID, updCount, saveNum), playerID)
        #GameWorld.DebugLog("    累计物品: itemID=%s,updCount=%s,saveNum=%s" % (itemID, updCount, saveNum), playerID)
        
    Sync_GuajiAward(curPlayer)
    return True
@@ -199,15 +203,15 @@
    lvIpyData = PlayerControl.GetPlayerLVIpyData(reLV)
    reExp = lvIpyData.GetReExp() if lvIpyData else 0
    worldLV = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
    GameWorld.DebugLog("计算挂机收益: awardSeconds=%s,useUnsecond=%s,reLV=%s,reExp=%s,worldLV=%s"
                       % (awardSeconds, useUnsecond, reLV, reExp, worldLV), playerID)
    #GameWorld.DebugLog("计算挂机收益: awardSeconds=%s,useUnsecond=%s,reLV=%s,reExp=%s,worldLV=%s"
    #                   % (awardSeconds, useUnsecond, reLV, reExp, worldLV), playerID)
    
    # 经验
    expRate = GetGuajiExpRate(curPlayer)
    secondBaseExp = int(eval(FormulaControl.GetCompileFormula("GuajiExp", IpyGameDataPY.GetFuncCfg("GuajiAward", 1))))
    secondExp = int(secondBaseExp * expRate / float(ChConfig.Def_MaxRateValue))
    addExp = awardSeconds * secondExp
    GameWorld.DebugLog("    每秒经验: %s, addExp=%s,secondBaseExp=%s,expRate=%s" % (secondExp, addExp, secondBaseExp, expRate), playerID)
    #GameWorld.DebugLog("    每秒经验: %s, addExp=%s,secondBaseExp=%s,expRate=%s" % (secondExp, addExp, secondBaseExp, expRate), playerID)
    
    # 每秒产出货币
    moneyDict = {}
@@ -216,7 +220,7 @@
        secondMoney = int(eval(FormulaControl.GetCompileFormula("GuajiMoney_%s" % moneyType, formula)))
        moneyValue = awardSeconds * secondMoney
        moneyDict[moneyType] = moneyValue
        GameWorld.DebugLog("    每秒货币: moneyType=%s,secondMoney=%s,moneyValue=%s" % (moneyType, secondMoney, moneyValue), playerID)
        #GameWorld.DebugLog("    每秒货币: moneyType=%s,secondMoney=%s,moneyValue=%s" % (moneyType, secondMoney, moneyValue), playerID)
        
    # 每x秒产出1货币
    perMoneyTimeFromulaDict = IpyGameDataPY.GetFuncEvalCfg("GuajiAward", 3, {}) # 每x秒获得1个货币公式 {货币类型:"x秒公式", ...}
@@ -227,13 +231,13 @@
        oneMoneyNeedSeconds = int(eval(FormulaControl.GetCompileFormula("GuajiMoney_%s" % moneyType, formula)))
        moneyValue = moneyAwardSeconds / oneMoneyNeedSeconds
        moneyDict[moneyType] = moneyValue
        GameWorld.DebugLog("    每X秒货币: moneyType=%s,oneMoneyNeedSeconds=%s,moneyValue=%s,moneyAwardSeconds=%s"
                           % (moneyType, oneMoneyNeedSeconds, moneyValue, moneyAwardSeconds), playerID)
        #GameWorld.DebugLog("    每X秒货币: moneyType=%s,oneMoneyNeedSeconds=%s,moneyValue=%s,moneyAwardSeconds=%s"
        #                   % (moneyType, oneMoneyNeedSeconds, moneyValue, moneyAwardSeconds), playerID)
        
        if useUnsecond:
            unSeconds = moneyAwardSeconds % oneMoneyNeedSeconds
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiMoneyUnSeconds % moneyType, unSeconds)
            GameWorld.DebugLog("        moneyType=%s,unSeconds=%s" % (moneyType, unSeconds), playerID)
            #GameWorld.DebugLog("        moneyType=%s,unSeconds=%s" % (moneyType, unSeconds), playerID)
            
    # 物品
    giveItemSecondsSet = IpyGameDataPY.GetFuncCfg("GuajiAward", 4) # 每x秒获得一次随机物品机会
@@ -243,12 +247,12 @@
        itemAwardSeconds += curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_GuajiItemUnSeconds)
            
    itemAwardTimes = itemAwardSeconds / giveItemSecondsSet # 给物品次数
    GameWorld.DebugLog("    给物品次数: %s, itemAwardSeconds=%s,giveItemSecondsSet=%s" % (itemAwardTimes, itemAwardSeconds, giveItemSecondsSet), playerID)
    #GameWorld.DebugLog("    给物品次数: %s, itemAwardSeconds=%s,giveItemSecondsSet=%s" % (itemAwardTimes, itemAwardSeconds, giveItemSecondsSet), playerID)
    
    if useUnsecond:
        unSeconds = itemAwardSeconds % giveItemSecondsSet
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_GuajiItemUnSeconds, unSeconds)
        GameWorld.DebugLog("    给物品未处理秒数=%s" % unSeconds, playerID)
        #GameWorld.DebugLog("    给物品未处理秒数=%s" % unSeconds, playerID)
        
    lvList = lvItemRateDict.keys()
    lvList.sort()
@@ -262,7 +266,7 @@
    dropCountTotal = 0
    itemDict = {}
    maxRate = itemRateList[-1][0]
    GameWorld.DebugLog("    itemRateList=%s,maxRate=%s" % (itemRateList, maxRate), playerID)
    #GameWorld.DebugLog("    itemRateList=%s,maxRate=%s" % (itemRateList, maxRate), playerID)
    if itemAwardTimes > 100: # 超过x次的,先进行批量处理
        preRate = 0
        for rateInfo in itemRateList:
@@ -277,8 +281,8 @@
            if GameWorld.CanHappen(rateEx, maxRate):
                dropCount += 1
            dropCountTotal += dropCount # 产出是是空物品也要算执行掉落次数
            GameWorld.DebugLog("    挂机物品: itemInfo=%s,curRate=%s,totalRate=%s,rateEx=%s,dropCount=%s,dropCountTotal=%s"
                               % (itemInfo, curRate, totalRate, rateEx, dropCount, dropCountTotal), playerID)
            #GameWorld.DebugLog("    挂机物品: itemInfo=%s,curRate=%s,totalRate=%s,rateEx=%s,dropCount=%s,dropCountTotal=%s"
            #                   % (itemInfo, curRate, totalRate, rateEx, dropCount, dropCountTotal), playerID)
            if not dropCount:
                continue
            
@@ -288,7 +292,7 @@
            itemDict[itemID] = itemDict.get(itemID, 0) + itemCount * dropCount
            
    awardTimesEx = itemAwardTimes - dropCountTotal
    GameWorld.DebugLog("    awardTimesEx=%s" % awardTimesEx, playerID)
    #GameWorld.DebugLog("    awardTimesEx=%s" % awardTimesEx, playerID)
    if awardTimesEx > 0:
        for _ in range(awardTimesEx):
            itemInfo = GameWorld.GetResultByRandomList(itemRateList)
@@ -307,7 +311,7 @@
    for itemID, dropCount in giveGarbageItemList.items():
        itemDict[itemID] = itemDict.get(itemID, 0) + dropCount
        
    GameWorld.DebugLog("    itemDict=%s" % (itemDict), playerID)
    #GameWorld.DebugLog("    itemDict=%s" % (itemDict), playerID)
    return addExp, moneyDict, itemDict
def GetGuajiExpRate(curPlayer):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
@@ -155,8 +155,9 @@
    if isNotify:
        curPlayer.Sync_RideHorse()
        
    playerControl = PlayerControl.PlayerControl(curPlayer)
    playerControl.RefreshPlayerAttrByBuff()
    if refreshState:
        playerControl = PlayerControl.PlayerControl(curPlayer)
        playerControl.RefreshPlayerAttrByBuff()
    return True
#=====================================================================
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py
@@ -51,7 +51,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckLuckyCloudBuyID(curPlayer)
        Sync_LuckyCloudBuyPlayerInfo(curPlayer) # 该功能收到就同步,不论有没有重置,因为可能由跨服关闭变为开启时的数据同步
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py
@@ -48,7 +48,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerLuckyTreasureAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py
@@ -52,7 +52,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerNewFairyCeremonyAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py
@@ -604,7 +604,7 @@
        
        if not attrDict:
            continue
        GameWorld.DebugLog('    刷符印属性 holeNum=%s, attrDict=%s' % (holeNum, attrDict))
        #GameWorld.DebugLog('    刷符印属性 holeNum=%s, attrDict=%s' % (holeNum, attrDict))
        for attrID, attrValue in attrDict.items():
            PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py
@@ -48,7 +48,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for index in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(index)
        if curPlayer.GetID() == 0:
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerSpringSaleAction(curPlayer, actNum)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
@@ -62,6 +62,7 @@
import PlayerYinji
import PlayerActivity
import PlayerBackup
import MirrorAttack
#---------------------------------------------------------------------
#---------------------------------------------------------------------
@@ -1165,9 +1166,16 @@
        #GameWorld.Log("玩家还未初始化成功, 不处理")
        return
    
    #延迟回收镜像玩家
    if PlayerControl.OnDelayDeleteMirror(curPlayer, tick):
        return
    #定时备档
    PlayerBackup.CheckPlayerBackup(curPlayer)
    
    #玩家镜像战斗AI
    MirrorAttack.ProcessPlayerMirrorAI(curPlayer, tick)
    #被GM封状态响应
    ProcessGMOperLogic(curPlayer, tick)
    
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py
@@ -775,7 +775,7 @@
        for attrID, attrValue in attrAwardDict.items():
            attrDict[attrID] = attrDict.get(attrID, 0) + attrValue
            
    GameWorld.DebugLog("    成就增加属性 attrDict=%s" % (attrDict))
    #GameWorld.DebugLog("    成就增加属性 attrDict=%s" % (attrDict))
    for attrID, attrValue in attrDict.items():
        PlayerControl.CalcAttrDict_Type(attrID, attrValue, allAttrList)
        
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
@@ -47,6 +47,8 @@
    ##玩家下线同步
    UpdateGameServerPlayerCache(curPlayer, tick, True)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_EquipViewCacheState, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PackDataSyncState, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PackDataSyncFightPower, 0)
    return
def ProcessCache(curPlayer, tick):
@@ -60,10 +62,31 @@
    UpdateGameServerPlayerCache(curPlayer, tick, False)
    return
def GetSyncPlayerPackData(curPlayer, force=False):
    playerID = curPlayer.GetPlayerID()
    fightPower = curPlayer.GetFightPower()
    if not force:
        syncState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PackDataSyncState)
        if not syncState:
            GameWorld.DebugLog("不需要同步打包数据", playerID)
            return ""
        # 值判断求余部分即可
        syncFightPower = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PackDataSyncFightPower)
        if syncFightPower == fightPower:
            GameWorld.DebugLog("战力不变,不需要同步打包数据! syncFightPower=%s" % syncFightPower, playerID)
            return ""
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PackDataSyncFightPower, fightPower)
    packData = curPlayer.GetPackData()
    #GameWorld.DebugLog("packData=%s %s %s" % (type(packData), len(packData), packData), playerID)
    return packData
##更新玩家当前详细信息到GameServer
#  @param curPlayer, tick
#  @return None
def UpdateGameServerPlayerCache(curPlayer, tick, IsLogouting=False):
def UpdateGameServerPlayerCache(curPlayer, tick, IsLogouting=False, forcePackData=False, packMsg=None):
    if PlayerTJG.GetIsTJG(curPlayer):
        # 脱机不处理
        return
@@ -85,6 +108,12 @@
    for classLV, itemData in itemDataDict.items():
        setattr(sendPack, "ItemData%s" % classLV, itemData)
        setattr(sendPack, "ItemDataSize%s" % classLV, len(itemData))
    # 打包数据相关
    sendPack.PackDataSyncState = 11 if forcePackData else curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PackDataSyncState)
    sendPack.PackData = GetSyncPlayerPackData(curPlayer, forcePackData)
    sendPack.PackDataLen = len(sendPack.PackData)
    sendPack.PackMsg = str(packMsg) if packMsg else "{}"
    sendPack.PackMsgLen = len(sendPack.PackMsg)
    #GameWorld.DebugLog("同步缓存: %s" % sendPack.OutputString())
    NetPackCommon.SendPyPackToGameServer(sendPack)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py
@@ -51,7 +51,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerWeekPartyAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py
@@ -90,7 +90,7 @@
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetPlayerCount()):
        curPlayer = playerManager.GetPlayerByIndex(i)
        if curPlayer == None or not curPlayer.GetInitOK():
        if not GameWorld.IsNormalPlayer(curPlayer):
            continue
        __CheckPlayerWishingWellAction(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
@@ -1,14 +1,25 @@
#!/usr/bin/python
# -*- coding: GBK -*-
#---------------------------------------------------------------------
#-------------------------------------------------------------------------------
#
#---------------------------------------------------------------------
##@package GY_Query_PlayerMirror
##@package Player.RemoteQuery.GY_Query_PlayerMirror
#
# @todo:玩家镜像打包数据
# @author hxp
# @date 2024-10-17
# @version 1.0
#
# 详细描述: 玩家镜像打包数据
#
#-------------------------------------------------------------------------------
#"""Version = 2024-10-17 15:00"""
#-------------------------------------------------------------------------------
import GameWorld
import PlayerEventCounter
import ChPlayer
import GameServerRefresh
import MirrorAttack
import PlayerViewCacheTube
import PlayerControl
import ChConfig
#---------------------------------------------------------------------
#  @param query_Type 请求类型
@@ -17,26 +28,67 @@
#  @param tick 当前时间
#  @return "True" or "False" or ""
def DoLogic(query_Type, query_ID, packCMDList, tick):
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(query_ID)
    GameWorld.DebugLog("GY_Query_PlayerMirror DoLogic", query_ID)
    msgInfo, packDataDict = packCMDList
    msgType = msgInfo["msgType"]
    # 镜像战斗
    if msgType == "MirrorBattle":
        curPlayer = None
        playerID = msgInfo.get("playerID", 0)
        if playerID:
            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not curPlayer or curPlayer.IsEmpty():
                return ''
        MirrorAttack.OnMirrorBattleInit(msgInfo, packDataDict, curPlayer)
    # 其他功能
    elif msgType == "":
        pass
    
    if not curPlayer or curPlayer.IsEmpty():
        return ''
    playerData = packCMDList['playerData']
    # playerData为base64后的数据
    mirrorPlayer = GameWorld.GetGameWorld().CreateMirrorPlayer(playerData, curPlayer.GetPosX(), curPlayer.GetPosY())
    #CreateMirrorPlayer 中会调用到 ChPlayer.PlayerLogin
    #是否镜像玩家 判断 mirrorPlayer.GetRealPlayerID()是否为0
    #python自己处理,以下逻辑,可以在DoPlayerLogin函数最后 判断是镜像玩家后统一处理
    index = mirrorPlayer.GetIndex()
    tick = GameWorld.GetGameWorld().GetTick()
    PlayerEventCounter.GameServer_InitOK(index, tick)
    ChPlayer.LoadMapOK(index, tick)
    GameServerRefresh.GameSever_PlayerInitOK(index, tick)
    return ''
def DoResult(curPlayer , callFunName , funResult , tick):
    GameWorld.DebugLog("GY_Query_PlayerMirror DoResult %s" % str(funResult), curPlayer.GetPlayerID())
    funResult = eval(funResult)
    if not funResult:
        return
    msgType = funResult[0]
    msgData = funResult[1]
    if msgType == "PackDataSyncState":
        isCross, isNeed = msgData
        __UpdPackDataSyncState(curPlayer, isCross, isNeed)
    elif msgType == "PullPlayerPackData":
        msgInfo = msgData
        __DoPullPlayerPackData(curPlayer, msgInfo, tick)
    return
def __UpdPackDataSyncState(curPlayer, isCross, isNeed):
    ## 更新打包数据同步状态,这里只更新状态即可,具体同步由定时同步处理
    syncState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_PackDataSyncState)
    if isCross:
        updSyncState = (1 if isNeed else 0) * 10 + syncState % 10
    else:
        updSyncState = syncState / 10 + (1 if isNeed else 0)
    if syncState == updSyncState:
        return
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_PackDataSyncState, syncState)
    GameWorld.DebugLog("更新打包数据同步状态: isCross=%s,isNeed=%s,syncState=%s,updSyncState=%s"
                       % (isCross, isNeed, syncState, updSyncState), curPlayer.GetPlayerID())
    return
def __DoPullPlayerPackData(curPlayer, msgInfo, tick):
    pullFrom = msgInfo.get("pullFrom")
    isCross = False
    # 0 或 非本服代表跨服需要
    if pullFrom == 0 or (pullFrom > 0 and pullFrom != GameWorld.GetServerGroupID()):
        isCross = True
    __UpdPackDataSyncState(curPlayer, isCross, 1)
    PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, forcePackData=True, packMsg=msgInfo)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -130,6 +130,7 @@
g_coupleInfo = {} # {playerID:[coupleID, coupleName], ...}
g_playerPriWoodPileNPCDict = {} # {playerID:[npcObj, ...], ...}
g_mirrorBattleDict = {} # {battleID:MirrorBattle, ...}
g_familyZhenfaInfo = {} # 仙盟阵法信息{familyID:{zhenfaType:{k:v, }, ...}, ...}
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -1648,6 +1648,9 @@
CrossServerMsg_Notify = "Notify"                        # 提示信息
CrossServerMsg_ChatCrossWorld = "ChatCrossWorld"        # 跨服世界聊天
CrossServerMsg_ViewPlayerCacheRet = "ViewPlayerCacheRet"# 查看跨服玩家信息结果
CrossServerMsg_PlayerPackDataState = "PlayerPackDataState"# 玩家打包数据同步状态
CrossServerMsg_PullPlayerPackData = "PullPlayerPackData"# 拉取玩家打包数据
CrossServerMsg_PushPlayerPackData = "PushPlayerPackData"# 推送玩家打包数据
CrossServerMsg_PKMatchReqRet = "PKMatchReqRet"          # 跨服PK匹配请求结果
CrossServerMsg_PKMatchResult = "PKMatchResult"          # 跨服PK匹配结果
CrossServerMsg_PKReadyOKRoomList = "PKReadyOKRoomList"  # 跨服PK已准备好的房间列表
@@ -1705,6 +1708,8 @@
ClientServerMsg_ChatCrossWorld = "ChatCrossWorld"       # 跨服世界聊天
ClientServerMsg_GMCMD = "GMCMD"                         # GM命令
ClientServerMsg_ViewPlayerCache = "ViewPlayerCache"     # 查看跨服玩家信息
ClientServerMsg_PullOtherPlayerPackData = "PullOtherPlayerPackData"   # 拉其他服玩家打包数据
ClientServerMsg_PlayerPackData = "PlayerPackData"       # 玩家打包数据同步
ClientServerMsg_PKMatch = "PKMatch"                     # 跨服PK匹配
ClientServerMsg_PKRobotOver = "PKRobotOver"             # 跨服PK机器人结算
ClientServerMsg_PKCancel = "PKCancel"                   # 跨服PK取消匹配
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
@@ -3864,7 +3864,7 @@
    skillAim = GetSkillFireAim(curSkill)
    affectTag = GetSkillAffectTag(curSkill)
    skillID = curSkill.GetSkillID()
    GameWorld.DebugLog("释放被动触发技能 : atkID=%s,skillID=%s(%s)" % (attacker.GetID(), skillID, curSkill.GetSkillName()))
    GameWorld.DebugLog("释放被动触发技能 : atkID=%s,skillID=%s(%s)" % (attacker.GetID(), skillID, curSkill.GetSkillName()), attacker.GetID())
    if skillAim == ChConfig.Def_UseSkillAim_None:
        if curSkill.GetSkillType() in ChConfig.Def_CanAttackSkill_List and affectTag != ChConfig.Def_UseSkillTag_Self:
@@ -3917,7 +3917,7 @@
            # 指定目标为自己
            result = DoLogic_UseSkill(attacker, attacker, curSkill, tick, isEnhanceSkill=isEnhanceSkill)
    
    GameWorld.DebugLog("触发结果-----------skillID=%s, %s" % (skillID, result))
    GameWorld.DebugLog("触发结果-----------skillID=%s, %s" % (skillID, result), attacker.GetID())
    return result