From f51835257345c50c4d3e5e0ecda7455401fb76ad Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期四, 17 十月 2024 14:51:31 +0800
Subject: [PATCH] 10263 【越南】【英文】后端支持NPC仿真实玩家战斗和快速战斗

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py                          |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py                     |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                                     |  112 ++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py                          |    3 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py                                      |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py              |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py                         |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py                        |    2 
 ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py                                                    |   39 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py                       |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py                                   |   23 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py                         |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py                        |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py                        |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py                      |   31 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py                   |    2 
 ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py                                                     |   67 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py                       |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py                         |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py                              |   42 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py                             |  841 +++++++++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py                                                   |   10 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py                                        |   49 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py                      |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py                           |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py                       |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py                              |    5 
 ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py                                                      |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py                       |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py                              |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py                            |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py                  |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                             |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py                           |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py                                       |   56 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py                            |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py                       |    6 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py                               |   39 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py                                 |    9 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py                       |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py                                |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py                                    |    2 
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py                                                          |  112 ++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py        |   98 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py                      |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py                 |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py                        |  113 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py                    |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py                       |    2 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py                                                            |   35 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py                   |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py                   |    2 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py                                               |   19 
 ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py                                                             |    9 
 PySysDB/PySysDBPY.h                                                                                                    |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py                         |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py                     |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py                      |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py                                |    2 
 ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py                                                          |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py                       |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py                            |  113 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py |   73 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py                       |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py                          |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py                     |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py                 |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py                               |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py                   |    7 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py                    |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py                   |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                                     |    5 
 ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py                                                        |    9 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py                                                |  449 ++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py                 |   13 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py               |    2 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py                                         |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py                        |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py                         |    2 
 ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py                                                           |    2 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py                                    |   22 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py                      |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py                  |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py                   |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py                       |   20 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                                        |   20 
 91 files changed, 2,424 insertions(+), 216 deletions(-)

diff --git a/PySysDB/PySysDBPY.h b/PySysDB/PySysDBPY.h
index 8c9896f..5e44e2a 100644
--- a/PySysDB/PySysDBPY.h
+++ b/PySysDB/PySysDBPY.h
@@ -67,6 +67,14 @@
 	list		StarAttrValue;	//累计总属性值
 };
 
+//技能搭配表
+
+struct	tagSkillMatch
+{
+	BYTE		_IDIndex;
+	list		Skills;	//主动技能(职业1id|职业2id)
+};
+
 //灵根表 #tagRolePoint
 
 struct	tagRolePoint
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
index 28bf1f0..a8bc41e 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -443,6 +443,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
@@ -451,7 +457,7 @@
 Def_Billboard_MaxCnt = 100
 #---------------------------------------------------------------------
 #请求类型(需要和MapServer中的一致)
-Def_QueryType_Count = 56
+Def_QueryType_Count = 55
 (
 queryType_sqtPlayer,                  #查询玩家
 queryType_sqtFamilyWar,               #家族战
@@ -508,7 +514,6 @@
 queryType_EnterFB,                    #进入副本
 queryType_NPCInfo,                    #查询NPCInfo
 queryType_NPCCnt,                     #查询NPC数量
-queryType_MirrorPlayer,               #镜像玩家
 ) = range(0, Def_QueryType_Count)
 #------------------------------------------------------------------------------ 
 #家族某行为类型保存的条数
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py
index 53677c0..6c8e35a 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChMapToGamePyPack.py
@@ -715,6 +715,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):
@@ -774,6 +779,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):
@@ -829,6 +839,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):
@@ -882,6 +897,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
 
@@ -936,6 +956,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):
@@ -988,7 +1013,12 @@
                                 ItemDataSize19:%d,
                                 ItemData19:%s,
                                 ItemDataSize20:%d,
-                                ItemData20:%s
+                                ItemData20:%s,
+                                PackDataSyncState:%d,
+                                PackDataLen:%d,
+                                PackData:%s,
+                                PackMsgLen:%d,
+                                PackMsg:%s
                                 '''\
                                 %(
                                 self.Head.OutputString(),
@@ -1039,7 +1069,12 @@
                                 self.ItemDataSize19,
                                 self.ItemData19,
                                 self.ItemDataSize20,
-                                self.ItemData20
+                                self.ItemData20,
+                                self.PackDataSyncState,
+                                self.PackDataLen,
+                                self.PackData,
+                                self.PackMsgLen,
+                                self.PackMsg
                                 )
         return DumpString
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
index 22a5b1c..4351f9c 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
+++ b/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):
@@ -20777,6 +20825,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):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
index 12c07f6..1283b15 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
+++ b/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
@@ -1243,6 +1268,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: 错误类型,自定义即可
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
index 6a5341b..eb0409a 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
+++ b/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
@@ -124,6 +125,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)
@@ -358,6 +365,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)
             
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py
index f6a3985..b58cd0b 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/QueryDBLogicResult.py
+++ b/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
\ No newline at end of file
+    return
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
index 0ce3070..bd0de0c 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -79,6 +79,7 @@
 import PlayerFuncTeam
 import PyDataManager
 import GameWorldMineArea
+import PlayerPackData
 import PlayerRecData
 import GameWorship
 import GameXiangong
@@ -298,7 +299,7 @@
         CrossYaomoBoss.OnPlayerLogin(curPlayer)
         #玩家记录
         PlayerRecData.OnPlayerLogin(curPlayer)
-        
+        PlayerPackData.OnPlayerLogin(curPlayer)
         #在线状态变更,放最后
         __OnPlayerOnlineStateChange(curPlayer, True)
         
@@ -704,11 +705,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)
         
     # 下线
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
new file mode 100644
index 0000000..ce8f7c9
--- /dev/null
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerPackData.py
@@ -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
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
index f458c71..de96e43 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
+++ b/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)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
index cde4295..9257609 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -31,6 +31,7 @@
 import CrossBattlefield
 import CrossRealmPlayer
 import PyGameDataStruct
+import PlayerPackData
 import IpyGameDataPY
 import PyDataManager
 import CrossRealmPK
@@ -74,6 +75,9 @@
         return True
     
     if GameWorldSkyTower.IsSkyTowerPassPlayer(playerID):
+        return True
+    
+    if PlayerPackData.IsPackDataPlayer(playerID):
         return True
     
     #跨服榜单上的默认保留
@@ -123,6 +127,8 @@
 
 def DelOutofTimeViewCacheData():
     ## 删除过期的查看缓存数据
+    
+    PlayerPackData.DelOutofTimePackData()
     
     pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
     playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
@@ -248,9 +254,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 {}
@@ -285,6 +288,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
@@ -321,12 +329,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)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py b/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
index 7abc70c..32db39d 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
+++ b/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()
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
index 518c228..005d6d6 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -159,6 +159,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],..]
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
index 695641c..4e65359 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
@@ -3530,3 +3530,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
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
index 14f2c78..34bb38b 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -1590,6 +1590,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已准备好的房间列表
@@ -1647,6 +1650,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取消匹配
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 2172b98..e0ada7e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -1941,6 +1941,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
+
 ;福地
 [PlayerMineArea]
 ScriptName = Player\PlayerMineArea.py
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
index 788da79..6e4731d 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/AttackLogic/AttackCommon.py
+++ b/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
@@ -2789,6 +2790,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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
new file mode 100644
index 0000000..bd53a0c
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/MirrorAttack.py
@@ -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
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 7983a9c..c0d18d0 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -1893,6 +1893,8 @@
 Def_FBMapID_ArenaBattle = 31290
 #情缘副本
 Def_FBMapID_Love = 31300
+#镜像切磋
+Def_FBMapID_MirrorBattle = 100
 
 #回合战斗自定义地图ID
 TurnFightMapIDList = (
@@ -1903,7 +1905,7 @@
 Def_TFMapID_SendToGameServer = [Def_TFMapID_MineArea]
 
 #前端自定义场景地图
-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, 
@@ -2036,6 +2038,7 @@
                 'CrossBattlefield':[Def_FBMapID_CrossBattlefield], #跨服战场
                 'CrossFamilyFlagwar':[Def_FBMapID_CrossFamilyFlagwar], #跨服仙盟夺旗战/逐鹿万界
                 'MineArea':[Def_TFMapID_MineArea], #福地
+                'MirrorBattle':[Def_FBMapID_MirrorBattle], #镜像切磋
                 }
 
 #特殊副本ID, 由系统分配, 进入时候不验证IsMapCopyFull
@@ -2647,7 +2650,7 @@
 
 #---------------------------------------------------------------------
 #请求类型(需要和GameServer中的一致)
-Def_QueryType_Count = 56
+Def_QueryType_Count = 55
 (
 queryType_sqtPlayer,                  #查询玩家
 queryType_sqtFamilyWar,               #家族战
@@ -2704,7 +2707,6 @@
 queryType_EnterFB,                    #进入副本
 queryType_NPCInfo,                    #查询NPCInfo
 queryType_NPCCnt,                     #查询NPC数量
-queryType_MirrorPlayer,               #镜像玩家
 ) = range(0, Def_QueryType_Count)
 #------------------------------------------------------------------------------ 
 #---------------------------------------------------------------------
@@ -3194,6 +3196,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"  # 类暴风雪计算时用主人属性
 
@@ -3268,6 +3278,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
@@ -3681,6 +3693,8 @@
 Def_PDict_GoodGameAwardState = "GoodGameAwardState"  # 游戏好评领奖记录
 Def_PDict_LikeGameAwardState = "LikeGameAwardState"  # 游戏点赞领奖记录
 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"  # 等级奖励领取信息记录,按二进制位标示
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py
index 53677c0..6c8e35a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChMapToGamePyPack.py
@@ -715,6 +715,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):
@@ -774,6 +779,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):
@@ -829,6 +839,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):
@@ -882,6 +897,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
 
@@ -936,6 +956,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):
@@ -988,7 +1013,12 @@
                                 ItemDataSize19:%d,
                                 ItemData19:%s,
                                 ItemDataSize20:%d,
-                                ItemData20:%s
+                                ItemData20:%s,
+                                PackDataSyncState:%d,
+                                PackDataLen:%d,
+                                PackData:%s,
+                                PackMsgLen:%d,
+                                PackMsg:%s
                                 '''\
                                 %(
                                 self.Head.OutputString(),
@@ -1039,7 +1069,12 @@
                                 self.ItemDataSize19,
                                 self.ItemData19,
                                 self.ItemDataSize20,
-                                self.ItemData20
+                                self.ItemData20,
+                                self.PackDataSyncState,
+                                self.PackDataLen,
+                                self.PackData,
+                                self.PackMsgLen,
+                                self.PackMsg
                                 )
         return DumpString
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index 22a5b1c..4351f9c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/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):
@@ -20777,6 +20825,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):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py
index 8d36902..8916fa9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CommFunc.py
@@ -609,3 +609,52 @@
     # 由于float会有不精确的现象出现xxx.9999999或xxx.0000000000001的问题,所以这里计算出的结果向上取整
     return int(math.ceil(round(floatRMB * rate)))
 
+#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
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py
index edd43ef..7e1db92 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Event/EventShell.py
@@ -1058,7 +1058,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)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py
index 8dafd9d..ad1119e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/ClearPlayerMirror.py
+++ b/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()
\ No newline at end of file
+        PlayerControl.DeleteMirror(mirrorPlayer)
+        
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
index 692a354..0e0d977 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/PlayerMirror.py
+++ b/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)
\ No newline at end of file
+        PlayerViewCacheTube.UpdateGameServerPlayerCache(curPlayer, tick, forcePackData=True)
+        GameWorld.DebugAnswer(curPlayer, "已更新最新镜像缓存!")
+        
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
index ec0ed88..db0b2f9 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
+++ b/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
@@ -588,6 +589,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
 
 #---------------------------------------------------------------------
@@ -1154,6 +1175,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")
@@ -1199,7 +1245,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
@@ -2179,6 +2225,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: 错误类型,自定义即可
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
index 849c9c4..a543acd 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBLogic.py
@@ -368,7 +368,10 @@
 #  @return None
 #  @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"))
     
@@ -2466,3 +2469,14 @@
     
     return callFunc(curPlayer, mapID, funcLineID, tagType, tagID, valueList, fightRet, awardItemList, ret)
 
+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)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
index 9cae25d..c246b0f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/FBCommon.py
@@ -2396,7 +2396,7 @@
 def GetClientCustomScene():
     ## 获取前端自定义副本场景
     mapIDList = GetGeneralTrainMapIDList()
-    return mapIDList + ChConfig.ClientCustomScene
+    return mapIDList + ChConfig.ClientCustomSceneList
 
 ## 同步进入副本时间
 #  @param curPlayer 玩家
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
new file mode 100644
index 0000000..bb16b47
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/FBProcess/GameLogic_MirrorBattle.py
@@ -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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
index af4cf0d..7a6c149 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/IpyGameDataPY.py
@@ -84,6 +84,11 @@
                         ("list", "StarAttrValue", 0),
                         ),
 
+                "SkillMatch":(
+                        ("BYTE", "IDIndex", 1),
+                        ("list", "Skills", 0),
+                        ),
+
                 "RolePoint":(
                         ("BYTE", "AttrID", 1),
                         ("dict", "AddAttrInfoPerPoint", 0),
@@ -2582,6 +2587,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_RolePoint():
@@ -6310,6 +6325,7 @@
         self.__LoadFileData("PlayerFaceStar", onlyCheck)
         self.__LoadFileData("PlayerFacePic", onlyCheck)
         self.__LoadFileData("PlayerFacePicStar", onlyCheck)
+        self.__LoadFileData("SkillMatch", onlyCheck)
         self.__LoadFileData("RolePoint", onlyCheck)
         self.__LoadFileData("LingQiAttr", onlyCheck)
         self.__LoadFileData("LingQiTrain", onlyCheck)
@@ -6807,6 +6823,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 GetRolePointCount(self):
         self.CheckLoadData("RolePoint")
         return self.ipyRolePointLen
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py
index 23f9e50..8ad3888 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ChEquip.py
+++ b/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)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py
index 6d5c5f1..43cd9fa 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActAllRecharge.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py
index 548de7f..f8f3fa2 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossActCTGBillboard.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py
index 5b3a176..94eeee2 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/GameServerRefresh.py
+++ b/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)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py
index 30a16b1..f8e8834 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/OpenServerCampaign.py
+++ b/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
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py
index 53d809a..bc6d021 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBossTrial.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py
index b5ce0a0..198b048 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyCountGift.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
         __CheckPlayerBuyCountGiftAction(curPlayer, actNum)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py
index 2d0fc50..80f5273 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActBuyOne.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py
index 4f614bd..fc1f1b0 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActCollectWords.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py
index a4520db..4cb31eb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyCTGAssist.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py
index 200dc39..6166659 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGarbageSorting.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py
index 10a1a5f..5807545 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGodGift.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py
index 5c1e738..4e47cc6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGrowupBuy.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py
index ee1d8f2..db7456f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActGubao.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py
index e0c84f5..793738c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetFeast.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py
index 40afe79..6565ef5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActHorsePetTrain.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py
index 58a4901..a7b3432 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLogin.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py
index ffa9d5b..776da9b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActLoginNew.py
@@ -43,7 +43,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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py
index 631c610..105feb3 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActManyDayRecharge.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py
index 84b283c..d51db44 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargePrize.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py
index 492f21e..907c940 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActRechargeRebateGold.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py
index 9be0a44..dfeccc1 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActSingleRecharge.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py
index 4db63b4..965e97c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTask.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
         __CheckPlayerActTaskAction(curPlayer, actNum)
     return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py
index e45af8d..9a7b033 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTotalRecharge.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py
index 109519a..db34ee5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActTurntable.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py
index 03f15b1..dc82d69 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActXianXiaMJ.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py
index 40a53d8..a460ca5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerAttrFruit.py
@@ -351,7 +351,7 @@
     hadInit = curPlayer.GetDictByKey(ChConfig.Def_PlayerKey_HadInitFruitAttr)
     if hadInit:
         return
-    GameWorld.DebugLog("__InitPlayerAttrFruit")
+    #GameWorld.DebugLog("__InitPlayerAttrFruit")
     
     ipyDataMgr = IpyGameDataPY.IPY_Data()
     maxCnt = ipyDataMgr.GetAttrFruitCount()
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py
index 04172db..982de22 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBillboard.py
+++ b/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) # 等级榜
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py
index 7add026..0bef5fa 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerBossReborn.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
index 0e70ffb..64e1b04 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerControl.py
+++ b/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
@@ -1214,6 +1215,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 返回值无意义
@@ -1324,6 +1356,8 @@
     UpdateOnLineTime(curPlayer, tick)
     
     PassiveBuffEffMng.OnPlayerLeaveMap(curPlayer)
+    
+    MirrorAttack.OnPlayerLeaveMap(curPlayer)
     
     #离开地图清空恶意攻击自己玩家信息
     if curPlayer.GetPlayerID() in PyGameData.g_maliciousAttackDict:
@@ -4611,7 +4645,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)
@@ -4623,8 +4659,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)
@@ -4659,7 +4696,7 @@
         PlayerFace.CalcFaceAttr(curPlayer)
         PlayerFace.CalcFacePicAttr(curPlayer)
         self.RefreshAllState(isForce=True)
-        GameWorld.DebugLog("End ReCalcAllState!!!")
+        GameWorld.DebugLog("End ReCalcAllState!!!", playerID)
         return
     
     
@@ -4679,14 +4716,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()
@@ -4700,7 +4738,7 @@
         self.__DoRefreshAttrAfterLogic(beforeMaxHP, beforeMoveSpeedValue, playerStateDict)
         
         curPlayer.SetDict(ChConfig.Def_Player_RefreshAttrByBuff, 0)
-        GameWorld.DebugLog("End RefreshPlayerAttrByBuffEx!!!")
+        GameWorld.DebugLog("End RefreshPlayerAttrByBuffEx!!!", playerID)
         return
     
     
@@ -4772,7 +4810,8 @@
         
         PlayerGubao.CalcGubaoAttr(curPlayer) # 古宝定位为对贯通所有游戏功能系统的属性玩法,所以每次都重新刷新
         
-        GameWorld.DebugLog("Start RefreshPlayerAttrStateEx!!!")
+        playerID = curPlayer.GetPlayerID()
+        GameWorld.DebugLog("Start RefreshPlayerAttrStateEx!!!", playerID)
         
         #beforeAtkInterval = curPlayer.GetAtkInterval()
         beforeMaxHP = GameObj.GetMaxHP(curPlayer)
@@ -4784,7 +4823,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()
@@ -4811,7 +4850,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
             # 不同功能点间的数值累加,需使用支持衰减递增的计算方式
@@ -4821,7 +4860,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)
             
@@ -4834,11 +4873,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 战斗属性层级交叉影响基值数值汇总
@@ -4847,7 +4886,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,
@@ -4881,7 +4920,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
@@ -4896,14 +4935,14 @@
                     baseValue = useAttrDict[attrType] # 只是用到目标功能的基值
                     addValue = int(baseValue * attrRate)
                     addAttrDict[attrType] = addAttrDict.get(attrType, 0) + addValue
-                    #GameWorld.DebugLog("    attrID=%s,per=%s,rate=%s,baseValue=%s,attrType=%s,addValue=%s" % (noLineAttrType, addAttrPer, attrRate, baseValue, attrType, addValue))
+                    #GameWorld.DebugLog("    attrID=%s,per=%s,rate=%s,baseValue=%s,attrType=%s,addValue=%s" % (noLineAttrType, addAttrPer, attrRate, baseValue, attrType, addValue), playerID)
                     
             if addAttrDict:
                 # 增加的数值统计到百分比属性所属功能点
                 # 如符文有个武器攻击百分比增加属性,增加的数值属于符文功能,不属于武器功能点的,只是基值使用了武器攻击     
                 funcCrossAttrPerInfoDict[i] = addAttrDict # 先都统计完后再累加到对应功能属性里,不然可能会导致功能基值变更
                 
-        GameWorld.DebugLog("交叉影响属性: %s" % funcCrossAttrPerInfoDict)
+        GameWorld.DebugLog("交叉影响属性: %s" % funcCrossAttrPerInfoDict, playerID)
         
         #    3.3 统计所有功能固定属性影响累加
         allFixAttrDict = {} # 固定属性层级总属性基值
@@ -4927,8 +4966,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()
@@ -4981,14 +5020,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]
@@ -5040,14 +5079,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
     
     
@@ -5082,6 +5121,7 @@
     def __RefreshBuffAttr(self):
         ## 刷新buff层属性,该层属性只会改变玩家最终属性,不会影响战力等
         curPlayer = self.__Player
+        playerID = curPlayer.GetPlayerID()
         allAttrListBuffs = [{} for _ in range(4)]
         # 6.计算buff属性, buff层级的不算如战斗力
         #        算战斗力总值时该层影响的数值不统计,但刷属性时需计算
@@ -5105,7 +5145,7 @@
         
         #        刷新攻击速度
         self.__SetAtkInterval()
-        GameWorld.DebugLog("Buff层属性: %s" % allAttrListBuffs)
+        GameWorld.DebugLog("Buff层属性: %s" % allAttrListBuffs, playerID)
         return
     
     def __DoRefreshGMAttr(self):
@@ -5349,8 +5389,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()
@@ -5383,7 +5424,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))
@@ -5394,21 +5435,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]
@@ -5419,14 +5461,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()
             #无出战宠物
@@ -5454,9 +5496,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)
@@ -5654,6 +5697,8 @@
         
         #PlayerTJG.PlayerTJGReborn(curPlayer, tick)
         GameObj.ClearPyPlayerState(curPlayer)
+        
+        MirrorAttack.OnPlayerDead(curPlayer)
         return
     
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py
index 1adb20b..8803cce 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCostRebate.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py
index c07cdd7..853e301 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerCrossRealmPK.py
+++ b/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
         
         #检查重置玩家信息
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py
index be153b4..6eee8d6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerDailyGiftbag.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
index b2e5745..5a9fdb2 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -205,7 +205,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)
@@ -223,7 +223,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)
@@ -273,7 +273,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)
@@ -317,7 +317,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)
@@ -333,7 +333,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)
@@ -378,7 +378,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)
@@ -392,7 +392,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)
@@ -437,7 +437,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)
@@ -1567,7 +1567,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
@@ -1666,7 +1666,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)
             
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
index 43243ff..029f675 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFB.py
+++ b/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):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py
index 3ea742c..216b53a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFairyCeremony.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py
index 0f048b1..8e6d4e1 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamilyZhenfa.py
+++ b/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:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py
index 59aa132..9839238 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastLogin.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py
index c2c56b9..c9568dc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastRedPacket.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py
index ed77e24..bb6a562 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastTravel.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py
index 68531db..3ea3a70 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWeekParty.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py
index 7f5ca59..6fc74f1 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFeastWish.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py
index b6ef8ab..b151c43 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashGiftbag.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py
index e3f8dba..26e88e0 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFlashSale.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py
index e940ba7..f48e16f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGodWeapon.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py
index 366d49c..c0e21fd 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerGuaji.py
+++ b/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):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
index db0b806..fb2a260 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
+++ b/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
 
 #=====================================================================
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py
index c586014..4ee1242 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyCloudBuy.py
+++ b/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) # 该功能收到就同步,不论有没有重置,因为可能由跨服关闭变为开启时的数据同步
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py
index e296c80..4418f33 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerLuckyTreasure.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py
index f3b0744..4b7caed 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerNewFairyCeremony.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py
index 284eb5e..7c3e0a8 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerRune.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py
index 4ebdcfd..8cb6885 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSpringSale.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
index 9a993dc..8c89276 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
+++ b/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)
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py
index 0f5602f..5a9250e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerSuccess.py
+++ b/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)
         
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
index 6223829..468be9f 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
@@ -46,6 +46,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):
@@ -59,10 +61,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
@@ -84,6 +107,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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py
index 2358e1b..1e1d99a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWeekParty.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py
index b78494b..599b016 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerWishingWell.py
+++ b/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
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
index 5375ace..7ff3583 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_PlayerMirror.py
+++ b/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
+
 
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
index cfe09d4..f0a87cb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -127,6 +127,7 @@
 g_coupleInfo = {} # {playerID:[coupleID, coupleName], ...}
 
 g_playerPriWoodPileNPCDict = {} # {playerID:[npcObj, ...], ...}
+g_mirrorBattleDict = {} # {battleID:MirrorBattle, ...}
 
 g_familyZhenfaInfo = {} # 仙盟阵法信息{familyID:{zhenfaType:{k:v, }, ...}, ...}
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index 14f2c78..34bb38b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -1590,6 +1590,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已准备好的房间列表
@@ -1647,6 +1650,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取消匹配
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
index 932035a..4002fcb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Skill/SkillShell.py
@@ -3815,7 +3815,7 @@
     
     skillAim = GetSkillFireAim(curSkill)
     affectTag = GetSkillAffectTag(curSkill)
-    GameWorld.DebugLog("释放被动触发技能 : %s- %s"%(skillAim, curSkill.GetSkillID()))
+    GameWorld.DebugLog("释放被动触发技能 : %s- %s %s"%(skillAim, curSkill.GetSkillID(), curSkill.GetSkillName()), attacker.GetID())
 
     if skillAim == ChConfig.Def_UseSkillAim_None:
         if curSkill.GetSkillType() in ChConfig.Def_CanAttackSkill_List and affectTag != ChConfig.Def_UseSkillTag_Self:
@@ -3868,7 +3868,7 @@
             # 指定目标为自己
             result = DoLogic_UseSkill(attacker, attacker, curSkill, tick, isEnhanceSkill=isEnhanceSkill)
     
-    GameWorld.DebugLog("触发结果-----------%s"%result)
+    GameWorld.DebugLog("触发结果-----------%s"%result, attacker.GetID())
     return result
 
 

--
Gitblit v1.8.0