From b3c6d36f5ae851f09a3c2e73494ff19956b06fb0 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期五, 11 四月 2025 16:56:14 +0800
Subject: [PATCH] 10367 【越南】【英语】【BT】【砍树】仙盟攻城战-服务端(跨服仙盟支持;后台查询区服仙盟支持;活动时间流程支持;攻城战主活动功能完整流程:公示、分组、战备、战斗、榜单、竞猜、领奖等;)

---
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyGCZ.py                              | 2902 ++++++++++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py                                              |   30 
 ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py                                              |  161 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                           |  251 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py                       | 1824 +++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py                                                |  251 +
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py                                            | 1824 +++++++++++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerDBGSEvent.py                                     |    4 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py                                                  |   92 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyGCZ.py             |  525 +++
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py                                     |   32 
 ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py                                                   |    1 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py                          |  150 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                       |    5 
 ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/GMT_GetFamilyByServerID.py                        |   83 
 ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py                                                |   21 
 ServerPython/CoreServerGroup/GameServer/PyNetPack.ini                                                        |   11 
 ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py                                           |   16 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py                    |   45 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                           |   21 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py                                         |   11 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py               |   13 
 ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py                                            |    7 
 ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py                                    |    4 
 ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py                                              |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py                    |    3 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py                               |   29 
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py                                        |  406 ++
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py                            |    9 
 ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py                                                 |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                                   |   20 
 ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CreateFamily.py                                   |  146 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/FamilyGCZ.py                 |   75 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py                              |   41 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FamilyGCZ.py |   47 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py             |    4 
 ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameRecData.py                                 |   35 
 PySysDB/PySysDBG.h                                                                                           |   64 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                              |   16 
 39 files changed, 9,077 insertions(+), 119 deletions(-)

diff --git a/PySysDB/PySysDBG.h b/PySysDB/PySysDBG.h
index 6f49190..a1339f2 100644
--- a/PySysDB/PySysDBG.h
+++ b/PySysDB/PySysDBG.h
@@ -792,6 +792,70 @@
 	dict		ScoreAwardEx;	//达标积分额外奖励 {积分:[[物品ID,个数,是否拍品], ...], ...}
 };
 
+
+//活动时间流程表
+
+struct tagActTimeFlow
+{
+	DWORD		_FlowID;
+	BYTE		StartDay;	//开始天
+	BYTE		StartHour;	//开始时
+	BYTE		StartMinute;	//开始分
+	BYTE		EndDay;	//结束天
+	BYTE		EndHour;	//结束时
+	BYTE		EndMinute;	//结束分
+	WORD		StateValue;	//状态值
+};
+
+//活动榜单奖励模版表
+
+struct tagActBillboardAwardTemp
+{
+	DWORD		_TemplateID;	//模板编号
+	BYTE		Rank;	//名次
+	DWORD		NeedValue;	//上榜所需值
+	dict		ValueAwardEx;	//达标值额外奖励 {值:[[物品ID,个数,是否拍品], ...], ...}
+	list		AwardItemList;	//奖励物品列表[[物品ID,个数,是否拍品], ...]
+	list		LeaderAwardItemList;	//仙盟榜盟主奖励物品信息列表[[物品ID,个数,是否拍品], ...]
+	list		EliteAwardItemList;	//仙盟榜精英奖励物品信息列表[[物品ID,个数,是否拍品], ...]
+};
+
+//活动竞猜表
+
+struct tagActGuess
+{
+	DWORD		_TemplateID;	//模板编号
+	DWORD		AwardID;	//奖励ID
+	list		RightRankList;	//猜对名次列表
+	list		AwardItemList;	//对应奖励列表 [[物品ID,个数,是否拍品], ...]
+};
+
+//仙盟攻城战活动表
+
+struct tagCrossActFamilyGCZ
+{
+	DWORD		_CfgID;	//配置ID
+	char		ActGroupName;	//活动组名(同组活动的名字需相同)
+	BYTE		ZoneID;		//组内分组编号
+	list		ServerIDRangeList;	//活动的服务器ID范围列表 [[serverIDA, serverIDB], ...]
+	char		StartDate;	//开启日期
+	char		EndDate;	//结束日期
+	BYTE		JoinFamilyCnt;	//参与仙盟数
+	WORD		ActFlowID;	//活动流程ID,对应H.活动时间流程表
+	WORD		GuessTemplateID;	//竞猜奖励模版,对应H.活动竞猜表
+	WORD		PersonalTemplateID;	//个人伤害排行奖励模版,对应H.活动榜单奖励模版表
+	WORD		FamilyTemplateID;	//仙盟积分排行奖励模版,对应H.活动榜单奖励模版表
+};
+
+//仙盟攻城战大本营等级表
+
+struct tagCrossActFamilyGCZCampLV
+{
+	DWORD		_CampLV;	//大本营等级
+	DWORD		LVUPNeedExp;	//升下一级所需经验
+	DWORD		AddHPPer;	//生命累计总加成百分比
+};
+
 //仙匣秘境活动时间表
 
 struct tagActXianXiaMJ
diff --git a/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini b/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
index 6e9965f..2f79136 100644
--- a/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
+++ b/ServerPython/CoreServerGroup/GameServer/PyNetPack.ini
@@ -566,6 +566,17 @@
 PacketSubCMD_1=0x07
 PacketCallFunc_1=OnVoiceChat
 
+[CrossFamilyGCZ]
+ScriptName = GameWorldLogic\CrossFamilyGCZ.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 1
+
+PacketCMD_1=0xC0
+PacketSubCMD_1=0x26
+PacketCallFunc_1=OnFamilyGCZQuery
+
 [GameWorship]
 ScriptName = GameWorldLogic\GameWorship.py
 Writer = hxp
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
index 2b69f4f..390086e 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChConfig.py
@@ -381,6 +381,7 @@
 Def_WorldKey_CrossPKZoneSeasonState = "CrossPKZoneSeasonState_%s" #跨服PK赛区赛季状态,跨服服务器控制,参数(zoneID)0-未开启,1-开启中,2-已结束
 Def_WorldKey_CrossPKBillboardSyncTick = "CrossPKBillboardSyncTick_%s_%s" #跨服PK本赛季排行榜同步tick,参数(zoneID, seasonID)
 Def_WorldKey_CrossSetPlayerAttrTick = "CrossSetPlayerAttrTick" #同步设置跨服玩家属性值tick
+Def_WorldKey_SyncFamilyAllToCross = "SyncFamilyAllToCross" #本次启动是否已经同步过所有仙盟到跨服服务器
 #---------------------------------------------------------------------
 #个人时间间隔
 TYPE_Player_Tick_Requestkey = 0                       # 玩家请求处理间隔
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
index 2163321..dec4787 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetPack.py
@@ -5089,6 +5089,70 @@
 
 
 #------------------------------------------------------
+# C0 26 仙盟攻城战查询 #tagCGFamilyGCZQuery
+
+class  tagCGFamilyGCZQuery(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("QueryType", c_ubyte),    #查询类型:1-成员捐献值;2-进入城池场景;3-退出城池场景;4-进入城池;5-退出城池;6-战报;7-分组仙盟成员伤害;
+                  ("BatType", c_ubyte),    #指定战场类型,需要发送的查询类型: 2、4
+                  ("GroupNum", c_ubyte),    #指定分组编号,需要发送的查询类型: 2、4
+                  ("FamilyID", c_int),    #指定仙盟ID或城池ID,查自己盟的可不发,需要发的类型:2、4、6、7
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC0
+        self.SubCmd = 0x26
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC0
+        self.SubCmd = 0x26
+        self.QueryType = 0
+        self.BatType = 0
+        self.GroupNum = 0
+        self.FamilyID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCGFamilyGCZQuery)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 26 仙盟攻城战查询 //tagCGFamilyGCZQuery:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                QueryType:%d,
+                                BatType:%d,
+                                GroupNum:%d,
+                                FamilyID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.QueryType,
+                                self.BatType,
+                                self.GroupNum,
+                                self.FamilyID
+                                )
+        return DumpString
+
+
+m_NAtagCGFamilyGCZQuery=tagCGFamilyGCZQuery()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGFamilyGCZQuery.Cmd,m_NAtagCGFamilyGCZQuery.SubCmd))] = m_NAtagCGFamilyGCZQuery
+
+
+#------------------------------------------------------
 # C0 03 强制退出跨服状态 #tagCGForceQuitCrossState
 
 class  tagCGForceQuitCrossState(Structure):
@@ -5245,8 +5309,8 @@
                   ("Cmd", c_ubyte),
                   ("SubCmd", c_ubyte),
                   ("Type", c_ubyte),    #榜单类型
-                  ("GroupValue1", c_ubyte),    # 分组值1
-                  ("GroupValue2", c_ubyte),    # 分组值2,与分组值1组合归为同组榜单数据
+                  ("GroupValue1", c_int),    # 分组值1
+                  ("GroupValue2", c_int),    # 分组值2,与分组值1组合归为同组榜单数据
                   ("StartIndex", c_int),    #查看的起始名次索引, 默认0
                   ("WatchCnt", c_ubyte),    #查看条数,默认20,最大不超过100
                   ("WatchID", c_int),    #查看指定ID名次前后,如玩家ID、家族ID等
@@ -23599,6 +23663,189 @@
 
 
 #------------------------------------------------------
+# C1 25 仙盟攻城战攻击 #tagCMFamilyGCZAtk
+
+class  tagCMFamilyGCZAtk(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("AtkType", c_ubyte),    #攻击类型: 1-普通单攻;2-技能单攻;3-技能群攻;
+                  ("TagCityID", c_int),    #目标城池ID,一般是仙盟ID或者特殊城池ID如修罗城城池,普攻单攻需指定目标,群攻技能发0
+                  ("TagGuardID", c_int),    #目标守卫ID,一般是玩家ID或者特殊守卫ID如修罗城守卫,普攻单攻需指定目标,技能攻击发0
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x25
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x25
+        self.AtkType = 0
+        self.TagCityID = 0
+        self.TagGuardID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMFamilyGCZAtk)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 25 仙盟攻城战攻击 //tagCMFamilyGCZAtk:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                AtkType:%d,
+                                TagCityID:%d,
+                                TagGuardID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.AtkType,
+                                self.TagCityID,
+                                self.TagGuardID
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZAtk=tagCMFamilyGCZAtk()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZAtk.Cmd,m_NAtagCMFamilyGCZAtk.SubCmd))] = m_NAtagCMFamilyGCZAtk
+
+
+#------------------------------------------------------
+# C1 24 仙盟攻城战捐献 #tagCMFamilyGCZContribution
+
+class  tagCMFamilyGCZContribution(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ContributionType", c_ubyte),    #捐献类型: 0-低级;1-高级
+                  ("UseCount", c_int),    #物品捐献时使用个数
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x24
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x24
+        self.ContributionType = 0
+        self.UseCount = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMFamilyGCZContribution)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 24 仙盟攻城战捐献 //tagCMFamilyGCZContribution:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ContributionType:%d,
+                                UseCount:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ContributionType,
+                                self.UseCount
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZContribution=tagCMFamilyGCZContribution()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZContribution.Cmd,m_NAtagCMFamilyGCZContribution.SubCmd))] = m_NAtagCMFamilyGCZContribution
+
+
+#------------------------------------------------------
+# C1 26 仙盟攻城战竞猜 #tagCMFamilyGCZGuess
+
+class  tagCMFamilyGCZGuess(Structure):
+    Head = tagHead()
+    SelectCnt = 0    #(BYTE SelectCnt)
+    SelectFamilyIDList = list()    #(vector<DWORD> SelectFamilyIDList)// 竞猜选择的仙盟ID排名顺序
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC1
+        self.Head.SubCmd = 0x26
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.SelectCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.SelectCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.SelectFamilyIDList.append(value)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC1
+        self.Head.SubCmd = 0x26
+        self.SelectCnt = 0
+        self.SelectFamilyIDList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 4 * self.SelectCnt
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.SelectCnt)
+        for i in range(self.SelectCnt):
+            data = CommFunc.WriteDWORD(data, self.SelectFamilyIDList[i])
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                SelectCnt:%d,
+                                SelectFamilyIDList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.SelectCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZGuess=tagCMFamilyGCZGuess()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZGuess.Head.Cmd,m_NAtagCMFamilyGCZGuess.Head.SubCmd))] = m_NAtagCMFamilyGCZGuess
+
+
+#------------------------------------------------------
 # C1 10 幸运云购购买 #tagCMLuckyCloudBuy
 
 class  tagCMLuckyCloudBuy(Structure):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
index 9e7d65d..58ed11c 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
@@ -17027,8 +17027,8 @@
 class  tagGCCrossBillboardInfo(Structure):
     Head = tagHead()
     Type = 0    #(BYTE Type)//榜单类型
-    GroupValue1 = 0    #(BYTE GroupValue1)// 分组值1
-    GroupValue2 = 0    #(BYTE GroupValue2)// 分组值2,与分组值1组合归为同组榜单数据
+    GroupValue1 = 0    #(DWORD GroupValue1)// 分组值1
+    GroupValue2 = 0    #(DWORD GroupValue2)// 分组值2,与分组值1组合归为同组榜单数据
     WatchID = 0    #(DWORD WatchID)//查看指定ID名次前后,如玩家ID、家族ID等
     BillboardCount = 0    #(BYTE BillboardCount)
     CrossBillboardDataList = list()    #(vector<tagGCCrossBillboardData> CrossBillboardDataList)
@@ -17044,8 +17044,8 @@
         self.Clear()
         _pos = self.Head.ReadData(_lpData, _pos)
         self.Type,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.GroupValue1,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.GroupValue2,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupValue1,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.GroupValue2,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.WatchID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.BillboardCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         for i in range(self.BillboardCount):
@@ -17071,8 +17071,8 @@
         length = 0
         length += self.Head.GetLength()
         length += 1
-        length += 1
-        length += 1
+        length += 4
+        length += 4
         length += 4
         length += 1
         for i in range(self.BillboardCount):
@@ -17084,8 +17084,8 @@
         data = ''
         data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
         data = CommFunc.WriteBYTE(data, self.Type)
-        data = CommFunc.WriteBYTE(data, self.GroupValue1)
-        data = CommFunc.WriteBYTE(data, self.GroupValue2)
+        data = CommFunc.WriteDWORD(data, self.GroupValue1)
+        data = CommFunc.WriteDWORD(data, self.GroupValue2)
         data = CommFunc.WriteDWORD(data, self.WatchID)
         data = CommFunc.WriteBYTE(data, self.BillboardCount)
         for i in range(self.BillboardCount):
@@ -18518,6 +18518,1750 @@
 
 m_NAtagGCCrossZoneInfo=tagGCCrossZoneInfo()
 ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCCrossZoneInfo.Cmd,m_NAtagGCCrossZoneInfo.SubCmd))] = m_NAtagGCCrossZoneInfo
+
+
+#------------------------------------------------------
+# C0 24 仙盟攻城战活动信息 #tagGCFamilyGCZActInfo
+
+class  tagGCFamilyGCZActFamily(Structure):
+    FamilyID = 0    #(DWORD FamilyID)
+    Name = ""    #(char Name[33])//参与仙盟名字
+    LV = 0    #(BYTE LV)//仙盟等级
+    ServerID = 0    #(DWORD ServerID)//仙盟所属区服ID
+    EmblemID = 0    #(DWORD EmblemID)//徽章ID
+    FightPower = 0    #(DWORD FightPower)//仙盟总战力,求余亿部分
+    FightPowerEx = 0    #(DWORD FightPowerEx)//仙盟总战力,整除亿部分
+    LeaderID = 0    #(DWORD LeaderID)//盟主ID
+    LeaderName = ""    #(char LeaderName[33])//盟主名
+    LeaderFace = 0    #(DWORD LeaderFace)
+    LeaderFacePic = 0    #(DWORD LeaderFacePic)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.LV,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ServerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.EmblemID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderName,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.LeaderFace,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderFacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.FamilyID = 0
+        self.Name = ""
+        self.LV = 0
+        self.ServerID = 0
+        self.EmblemID = 0
+        self.FightPower = 0
+        self.FightPowerEx = 0
+        self.LeaderID = 0
+        self.LeaderName = ""
+        self.LeaderFace = 0
+        self.LeaderFacePic = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteBYTE(data, self.LV)
+        data = CommFunc.WriteDWORD(data, self.ServerID)
+        data = CommFunc.WriteDWORD(data, self.EmblemID)
+        data = CommFunc.WriteDWORD(data, self.FightPower)
+        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
+        data = CommFunc.WriteDWORD(data, self.LeaderID)
+        data = CommFunc.WriteString(data, 33, self.LeaderName)
+        data = CommFunc.WriteDWORD(data, self.LeaderFace)
+        data = CommFunc.WriteDWORD(data, self.LeaderFacePic)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                FamilyID:%d,
+                                Name:%s,
+                                LV:%d,
+                                ServerID:%d,
+                                EmblemID:%d,
+                                FightPower:%d,
+                                FightPowerEx:%d,
+                                LeaderID:%d,
+                                LeaderName:%s,
+                                LeaderFace:%d,
+                                LeaderFacePic:%d
+                                '''\
+                                %(
+                                self.FamilyID,
+                                self.Name,
+                                self.LV,
+                                self.ServerID,
+                                self.EmblemID,
+                                self.FightPower,
+                                self.FightPowerEx,
+                                self.LeaderID,
+                                self.LeaderName,
+                                self.LeaderFace,
+                                self.LeaderFacePic
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZActInfo(Structure):
+    Head = tagHead()
+    ServerInfoLen = 0    #(BYTE ServerInfoLen)
+    ServerIDRangeInfo = ""    #(String ServerIDRangeInfo)//开放该活动的服务器ID范围列表,json格式 [[IDA, IDB], ...], [] 为全服
+    ZoneID = 0    #(BYTE ZoneID)// 活动分区ID,公示期为0
+    ActID = 0    #(DWORD ActID)// 活动ID,代表某一次活动的唯一ID,前端如果有活动相关的本地记录可以通过验证此ID变更进行重置
+    StartDate = ""    #(char StartDate[10])// 开始日期 y-m-d
+    EndtDate = ""    #(char EndtDate[10])// 结束日期 y-m-d
+    JoinFamilyCnt = 0    #(BYTE JoinFamilyCnt)// 参与仙盟数
+    ActFlowID = 0    #(WORD ActFlowID)// 活动流程ID,对应H.活动时间流程表中的编号,前端根据跨服时间自行计算当前所处流程状态
+    GuessTemplateID = 0    #(WORD GuessTemplateID)// 竞猜奖励模版,对应H.活动竞猜表,前端自行读表展示
+    PersonalTemplateID = 0    #(WORD PersonalTemplateID)// 个人伤害排行奖励模版,对应H.活动榜单奖励模版表,前端自行读表展示
+    FamilyTemplateID = 0    #(WORD FamilyTemplateID)// 仙盟积分排行奖励模版,对应H.活动榜单奖励模版表,前端自行读表展示
+    StateError = 0    #(BYTE StateError)// 活动流程状态是否异常,如果不为0代表活动已异常,前端自行决定是不显示活动还是活动页面做提示
+    FamilyCount = 0    #(BYTE FamilyCount)
+    ActFamilyList = list()    #(vector<tagGCFamilyGCZActFamily> ActFamilyList)//本分区参与的仙盟名单
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x24
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.ServerInfoLen,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ServerIDRangeInfo,_pos = CommFunc.ReadString(_lpData, _pos,self.ServerInfoLen)
+        self.ZoneID,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ActID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.StartDate,_pos = CommFunc.ReadString(_lpData, _pos,10)
+        self.EndtDate,_pos = CommFunc.ReadString(_lpData, _pos,10)
+        self.JoinFamilyCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ActFlowID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.GuessTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.PersonalTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.FamilyTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.StateError,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.FamilyCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyCount):
+            temActFamilyList = tagGCFamilyGCZActFamily()
+            _pos = temActFamilyList.ReadData(_lpData, _pos)
+            self.ActFamilyList.append(temActFamilyList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x24
+        self.ServerInfoLen = 0
+        self.ServerIDRangeInfo = ""
+        self.ZoneID = 0
+        self.ActID = 0
+        self.StartDate = ""
+        self.EndtDate = ""
+        self.JoinFamilyCnt = 0
+        self.ActFlowID = 0
+        self.GuessTemplateID = 0
+        self.PersonalTemplateID = 0
+        self.FamilyTemplateID = 0
+        self.StateError = 0
+        self.FamilyCount = 0
+        self.ActFamilyList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += len(self.ServerIDRangeInfo)
+        length += 1
+        length += 4
+        length += 10
+        length += 10
+        length += 1
+        length += 2
+        length += 2
+        length += 2
+        length += 2
+        length += 1
+        length += 1
+        for i in range(self.FamilyCount):
+            length += self.ActFamilyList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.ServerInfoLen)
+        data = CommFunc.WriteString(data, self.ServerInfoLen, self.ServerIDRangeInfo)
+        data = CommFunc.WriteBYTE(data, self.ZoneID)
+        data = CommFunc.WriteDWORD(data, self.ActID)
+        data = CommFunc.WriteString(data, 10, self.StartDate)
+        data = CommFunc.WriteString(data, 10, self.EndtDate)
+        data = CommFunc.WriteBYTE(data, self.JoinFamilyCnt)
+        data = CommFunc.WriteWORD(data, self.ActFlowID)
+        data = CommFunc.WriteWORD(data, self.GuessTemplateID)
+        data = CommFunc.WriteWORD(data, self.PersonalTemplateID)
+        data = CommFunc.WriteWORD(data, self.FamilyTemplateID)
+        data = CommFunc.WriteBYTE(data, self.StateError)
+        data = CommFunc.WriteBYTE(data, self.FamilyCount)
+        for i in range(self.FamilyCount):
+            data = CommFunc.WriteString(data, self.ActFamilyList[i].GetLength(), self.ActFamilyList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                ServerInfoLen:%d,
+                                ServerIDRangeInfo:%s,
+                                ZoneID:%d,
+                                ActID:%d,
+                                StartDate:%s,
+                                EndtDate:%s,
+                                JoinFamilyCnt:%d,
+                                ActFlowID:%d,
+                                GuessTemplateID:%d,
+                                PersonalTemplateID:%d,
+                                FamilyTemplateID:%d,
+                                StateError:%d,
+                                FamilyCount:%d,
+                                ActFamilyList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.ServerInfoLen,
+                                self.ServerIDRangeInfo,
+                                self.ZoneID,
+                                self.ActID,
+                                self.StartDate,
+                                self.EndtDate,
+                                self.JoinFamilyCnt,
+                                self.ActFlowID,
+                                self.GuessTemplateID,
+                                self.PersonalTemplateID,
+                                self.FamilyTemplateID,
+                                self.StateError,
+                                self.FamilyCount,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZActInfo=tagGCFamilyGCZActInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZActInfo.Head.Cmd,m_NAtagGCFamilyGCZActInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZActInfo
+
+
+#------------------------------------------------------
+# C0 29 仙盟攻城战城池战斗信息 #tagGCFamilyGCZBatCityInfo
+
+class  tagGCFamilyGCZBatCityInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("CityID", c_int),    #城池ID
+                  ("CityLV", c_ubyte),    #城池等级
+                  ("FamilyID", c_int),    #所属仙盟ID,可能为0
+                  ("GuardID", c_int),    #当前防守成员ID,为0时表示没有防守成员了,城池已被击毁
+                  ("HP", c_int),    #防守成员剩余生命,求余亿部分,剩余生命为0时代表该防守成员被击败
+                  ("HPEx", c_int),    #防守成员剩余生命,整除亿部分
+                  ("HPMax", c_int),    #防守成员最大生命,求余亿部分
+                  ("HPMaxEx", c_int),    #防守成员最大生命,整除亿部分
+                  ("AtkPlayerID", c_int),    #发起攻击的玩家ID,可能为0,判断是否自己的ID进行相应的攻击表现
+                  ("AtkRet", c_ubyte),    #攻击结果,仅在攻击玩家ID不为0时有效,0--成功;1-目标已被击杀;2-城池已被摧毁;3-其他
+                  ("KillCnt", c_ubyte),    #本次攻击击杀防守人员数
+                  ("HurtValue", c_int),    #伤害飘血,求余亿部分
+                  ("HurtValueEx", c_int),    #伤害飘血,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC0
+        self.SubCmd = 0x29
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC0
+        self.SubCmd = 0x29
+        self.CityID = 0
+        self.CityLV = 0
+        self.FamilyID = 0
+        self.GuardID = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.AtkPlayerID = 0
+        self.AtkRet = 0
+        self.KillCnt = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatCityInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 29 仙盟攻城战城池战斗信息 //tagGCFamilyGCZBatCityInfo:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                CityID:%d,
+                                CityLV:%d,
+                                FamilyID:%d,
+                                GuardID:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                AtkPlayerID:%d,
+                                AtkRet:%d,
+                                KillCnt:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.CityID,
+                                self.CityLV,
+                                self.FamilyID,
+                                self.GuardID,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.AtkPlayerID,
+                                self.AtkRet,
+                                self.KillCnt,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatCityInfo=tagGCFamilyGCZBatCityInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatCityInfo.Cmd,m_NAtagGCFamilyGCZBatCityInfo.SubCmd))] = m_NAtagGCFamilyGCZBatCityInfo
+
+
+#------------------------------------------------------
+# C0 25 仙盟攻城战轮次分组信息 #tagGCFamilyGCZBatGroupInfo
+
+class  tagGCFamilyGCZBatGroup(Structure):
+    GroupNum = 0    #(BYTE GroupNum)// 分组编号,从1开始,对应A
+    FamilyIDCnt = 0    #(BYTE FamilyIDCnt)
+    FamilyIDList = list()    #(vector<DWORD> FamilyIDList)// 仙盟ID列表,前端可以通过判断参与的仙盟ID是否在某个分组里面验证有没有战场参赛资格
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.GroupNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.FamilyIDCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyIDCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.FamilyIDList.append(value)
+        return _pos
+
+    def Clear(self):
+        self.GroupNum = 0
+        self.FamilyIDCnt = 0
+        self.FamilyIDList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 1
+        length += 4 * self.FamilyIDCnt
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.GroupNum)
+        data = CommFunc.WriteBYTE(data, self.FamilyIDCnt)
+        for i in range(self.FamilyIDCnt):
+            data = CommFunc.WriteDWORD(data, self.FamilyIDList[i])
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                GroupNum:%d,
+                                FamilyIDCnt:%d,
+                                FamilyIDList:%s
+                                '''\
+                                %(
+                                self.GroupNum,
+                                self.FamilyIDCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBat(Structure):
+    BatType = 0    #(BYTE BatType)// 战场类型 1-初级;2-中级;3-高级;
+    GroupCnt = 0    #(BYTE GroupCnt)// 分组数
+    GroupList = list()    #(vector<tagGCFamilyGCZBatGroup> GroupList)// 分组列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.BatType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.GroupCnt):
+            temGroupList = tagGCFamilyGCZBatGroup()
+            _pos = temGroupList.ReadData(_lpData, _pos)
+            self.GroupList.append(temGroupList)
+        return _pos
+
+    def Clear(self):
+        self.BatType = 0
+        self.GroupCnt = 0
+        self.GroupList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 1
+        for i in range(self.GroupCnt):
+            length += self.GroupList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.BatType)
+        data = CommFunc.WriteBYTE(data, self.GroupCnt)
+        for i in range(self.GroupCnt):
+            data = CommFunc.WriteString(data, self.GroupList[i].GetLength(), self.GroupList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                BatType:%d,
+                                GroupCnt:%d,
+                                GroupList:%s
+                                '''\
+                                %(
+                                self.BatType,
+                                self.GroupCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatGroupInfo(Structure):
+    Head = tagHead()
+    RoundNum = 0    #(BYTE RoundNum)// 第x轮的分组,从1开始,没有在对应轮次战场分组里的视为没有该轮次的参赛资格
+    BatTypeCnt = 0    #(BYTE BatTypeCnt)// 战场类型数
+    BatList = list()    #(vector<tagGCFamilyGCZBat> BatList)//战场列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x25
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.RoundNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.BatTypeCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.BatTypeCnt):
+            temBatList = tagGCFamilyGCZBat()
+            _pos = temBatList.ReadData(_lpData, _pos)
+            self.BatList.append(temBatList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x25
+        self.RoundNum = 0
+        self.BatTypeCnt = 0
+        self.BatList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 1
+        for i in range(self.BatTypeCnt):
+            length += self.BatList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.RoundNum)
+        data = CommFunc.WriteBYTE(data, self.BatTypeCnt)
+        for i in range(self.BatTypeCnt):
+            data = CommFunc.WriteString(data, self.BatList[i].GetLength(), self.BatList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                RoundNum:%d,
+                                BatTypeCnt:%d,
+                                BatList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.RoundNum,
+                                self.BatTypeCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatGroupInfo=tagGCFamilyGCZBatGroupInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatGroupInfo.Head.Cmd,m_NAtagGCFamilyGCZBatGroupInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZBatGroupInfo
+
+
+#------------------------------------------------------
+# C0 30 仙盟攻城战战斗战报 #tagGCFamilyGCZBatReport
+
+class  tagGCFamilyGCZBatRepHurt(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("TagFamilyID", c_int),    #目标仙盟ID
+                  ("HurtValue", c_int),    #输出伤害,求余亿部分
+                  ("HurtValueEx", c_int),    #输出伤害,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.TagFamilyID = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatRepHurt)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 30 仙盟攻城战战斗战报 //tagGCFamilyGCZBatReport:
+                                TagFamilyID:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.TagFamilyID,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatReport(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)// 战报仙盟ID
+    DefRepCnt = 0    #(BYTE DefRepCnt)
+    DefRepList = list()    #(vector<tagGCFamilyGCZBatRepHurt> DefRepList)// 防守战报
+    AtkRepCnt = 0    #(BYTE AtkRepCnt)
+    AtkRepList = list()    #(vector<tagGCFamilyGCZBatRepHurt> AtkRepList)// 进攻战报
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x30
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.DefRepCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.DefRepCnt):
+            temDefRepList = tagGCFamilyGCZBatRepHurt()
+            _pos = temDefRepList.ReadData(_lpData, _pos)
+            self.DefRepList.append(temDefRepList)
+        self.AtkRepCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.AtkRepCnt):
+            temAtkRepList = tagGCFamilyGCZBatRepHurt()
+            _pos = temAtkRepList.ReadData(_lpData, _pos)
+            self.AtkRepList.append(temAtkRepList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x30
+        self.FamilyID = 0
+        self.DefRepCnt = 0
+        self.DefRepList = list()
+        self.AtkRepCnt = 0
+        self.AtkRepList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        for i in range(self.DefRepCnt):
+            length += self.DefRepList[i].GetLength()
+        length += 1
+        for i in range(self.AtkRepCnt):
+            length += self.AtkRepList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteBYTE(data, self.DefRepCnt)
+        for i in range(self.DefRepCnt):
+            data = CommFunc.WriteString(data, self.DefRepList[i].GetLength(), self.DefRepList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.AtkRepCnt)
+        for i in range(self.AtkRepCnt):
+            data = CommFunc.WriteString(data, self.AtkRepList[i].GetLength(), self.AtkRepList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                DefRepCnt:%d,
+                                DefRepList:%s,
+                                AtkRepCnt:%d,
+                                AtkRepList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.DefRepCnt,
+                                "...",
+                                self.AtkRepCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatReport=tagGCFamilyGCZBatReport()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatReport.Head.Cmd,m_NAtagGCFamilyGCZBatReport.Head.SubCmd))] = m_NAtagGCFamilyGCZBatReport
+
+
+#------------------------------------------------------
+# C0 28 仙盟攻城战城池场景信息 #tagGCFamilyGCZBatSceneInfo
+
+class  tagGCFamilyGCZBatScenePlayer(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])
+    Face = 0    #(DWORD Face)
+    FacePic = 0    #(DWORD FacePic)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.Face,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.Face = 0
+        self.FacePic = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteDWORD(data, self.Face)
+        data = CommFunc.WriteDWORD(data, self.FacePic)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                Face:%d,
+                                FacePic:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.Face,
+                                self.FacePic
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneHurt(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("CityID", c_int),    #被攻击的城池ID
+                  ("HurtValue", c_int),    #伤害飘血,求余亿部分
+                  ("HurtValueEx", c_int),    #伤害飘血,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.CityID = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatSceneHurt)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 28 仙盟攻城战城池场景信息 //tagGCFamilyGCZBatSceneInfo:
+                                CityID:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.CityID,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneCity(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("CityID", c_int),    #城池ID
+                  ("CityLV", c_ubyte),    #城池等级
+                  ("FamilyID", c_int),    #所属仙盟ID,可能为0
+                  ("Rank", c_ubyte),    #当前名次,从1开始
+                  ("HP", c_int),    #剩余生命,求余亿部分,剩余生命为0时代表被摧毁
+                  ("HPEx", c_int),    #剩余生命,整除亿部分
+                  ("HPMax", c_int),    #最大生命,求余亿部分
+                  ("HPMaxEx", c_int),    #最大生命,整除亿部分
+                  ("LastAtkedTime", c_int),    #最后一次被攻击时间戳,可能为0
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.CityID = 0
+        self.CityLV = 0
+        self.FamilyID = 0
+        self.Rank = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.LastAtkedTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatSceneCity)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 28 仙盟攻城战城池场景信息 //tagGCFamilyGCZBatSceneInfo:
+                                CityID:%d,
+                                CityLV:%d,
+                                FamilyID:%d,
+                                Rank:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                LastAtkedTime:%d
+                                '''\
+                                %(
+                                self.CityID,
+                                self.CityLV,
+                                self.FamilyID,
+                                self.Rank,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.LastAtkedTime
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneInfo(Structure):
+    Head = tagHead()
+    BatType = 0    #(BYTE BatType)// 战场类型 1-初级;2-中级;3-高级;
+    GroupNum = 0    #(BYTE GroupNum)// 分组编号,从1开始,对应A
+    TopFamilyID = 0    #(DWORD TopFamilyID)// 伤害第一仙盟ID
+    TopLeaderID = 0    #(DWORD TopLeaderID)// 伤害第一盟主ID
+    TopPlayerID = 0    #(DWORD TopPlayerID)// 伤害第一玩家ID
+    TopPlayerFamilyID = 0    #(DWORD TopPlayerFamilyID)// 伤害第一玩家仙盟ID
+    AtkPlayerID = 0    #(DWORD AtkPlayerID)// 发起攻击的玩家,可能为0,仅技能攻击时通知,普攻时仅通知血量等信息
+    AtkType = 0    #(BYTE AtkType)// 攻击类型,有发起攻击的玩家时有效
+    KillCnt = 0    #(BYTE KillCnt)// 本次攻击累计击杀数,有发起攻击的玩家时有效
+    HurtCnt = 0    #(BYTE HurtCnt)
+    HurtList = list()    #(vector<tagGCFamilyGCZBatSceneHurt> HurtList)//本次攻击伤血信息,有发起攻击的玩家时有效
+    CityCnt = 0    #(BYTE CityCnt)
+    CityList = list()    #(vector<tagGCFamilyGCZBatSceneCity> CityList)// 城池信息,仅通知变化的城池
+    PlayerCnt = 0    #(BYTE PlayerCnt)
+    PlayerInfoList = list()    #(vector<tagGCFamilyGCZBatScenePlayer> PlayerInfoList)// 场景展示所需要用到的玩家信息,如第一玩家、使用技能玩家
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x28
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.BatType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.TopFamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopLeaderID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopPlayerFamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.AtkPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.AtkType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.KillCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.HurtCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.HurtCnt):
+            temHurtList = tagGCFamilyGCZBatSceneHurt()
+            _pos = temHurtList.ReadData(_lpData, _pos)
+            self.HurtList.append(temHurtList)
+        self.CityCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.CityCnt):
+            temCityList = tagGCFamilyGCZBatSceneCity()
+            _pos = temCityList.ReadData(_lpData, _pos)
+            self.CityList.append(temCityList)
+        self.PlayerCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.PlayerCnt):
+            temPlayerInfoList = tagGCFamilyGCZBatScenePlayer()
+            _pos = temPlayerInfoList.ReadData(_lpData, _pos)
+            self.PlayerInfoList.append(temPlayerInfoList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x28
+        self.BatType = 0
+        self.GroupNum = 0
+        self.TopFamilyID = 0
+        self.TopLeaderID = 0
+        self.TopPlayerID = 0
+        self.TopPlayerFamilyID = 0
+        self.AtkPlayerID = 0
+        self.AtkType = 0
+        self.KillCnt = 0
+        self.HurtCnt = 0
+        self.HurtList = list()
+        self.CityCnt = 0
+        self.CityList = list()
+        self.PlayerCnt = 0
+        self.PlayerInfoList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 1
+        length += 1
+        length += 1
+        for i in range(self.HurtCnt):
+            length += self.HurtList[i].GetLength()
+        length += 1
+        for i in range(self.CityCnt):
+            length += self.CityList[i].GetLength()
+        length += 1
+        for i in range(self.PlayerCnt):
+            length += self.PlayerInfoList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.BatType)
+        data = CommFunc.WriteBYTE(data, self.GroupNum)
+        data = CommFunc.WriteDWORD(data, self.TopFamilyID)
+        data = CommFunc.WriteDWORD(data, self.TopLeaderID)
+        data = CommFunc.WriteDWORD(data, self.TopPlayerID)
+        data = CommFunc.WriteDWORD(data, self.TopPlayerFamilyID)
+        data = CommFunc.WriteDWORD(data, self.AtkPlayerID)
+        data = CommFunc.WriteBYTE(data, self.AtkType)
+        data = CommFunc.WriteBYTE(data, self.KillCnt)
+        data = CommFunc.WriteBYTE(data, self.HurtCnt)
+        for i in range(self.HurtCnt):
+            data = CommFunc.WriteString(data, self.HurtList[i].GetLength(), self.HurtList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.CityCnt)
+        for i in range(self.CityCnt):
+            data = CommFunc.WriteString(data, self.CityList[i].GetLength(), self.CityList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.PlayerCnt)
+        for i in range(self.PlayerCnt):
+            data = CommFunc.WriteString(data, self.PlayerInfoList[i].GetLength(), self.PlayerInfoList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                BatType:%d,
+                                GroupNum:%d,
+                                TopFamilyID:%d,
+                                TopLeaderID:%d,
+                                TopPlayerID:%d,
+                                TopPlayerFamilyID:%d,
+                                AtkPlayerID:%d,
+                                AtkType:%d,
+                                KillCnt:%d,
+                                HurtCnt:%d,
+                                HurtList:%s,
+                                CityCnt:%d,
+                                CityList:%s,
+                                PlayerCnt:%d,
+                                PlayerInfoList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.BatType,
+                                self.GroupNum,
+                                self.TopFamilyID,
+                                self.TopLeaderID,
+                                self.TopPlayerID,
+                                self.TopPlayerFamilyID,
+                                self.AtkPlayerID,
+                                self.AtkType,
+                                self.KillCnt,
+                                self.HurtCnt,
+                                "...",
+                                self.CityCnt,
+                                "...",
+                                self.PlayerCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatSceneInfo=tagGCFamilyGCZBatSceneInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatSceneInfo.Head.Cmd,m_NAtagGCFamilyGCZBatSceneInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZBatSceneInfo
+
+
+#------------------------------------------------------
+# C0 26 仙盟攻城战大本营信息 #tagGCFamilyGCZCampInfo
+
+class  tagGCFamilyGCZCampMem(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])//参与玩家名字
+    FamilyLV = 0    #(BYTE FamilyLV)//参与时的家族职位
+    Face = 0    #(DWORD Face)//基本脸型
+    FacePic = 0    #(DWORD FacePic)//头像框
+    FightPower = 0    #(DWORD FightPower)//战力,求余亿部分
+    FightPowerEx = 0    #(DWORD FightPowerEx)//战力,整除亿部分
+    HP = 0    #(DWORD HP)//剩余生命,求余亿部分
+    HPEx = 0    #(DWORD HPEx)//剩余生命,整除亿部分
+    HPMax = 0    #(DWORD HPMax)//最大生命,求余亿部分
+    HPMaxEx = 0    #(DWORD HPMaxEx)//最大生命,整除亿部分
+    TotalHurt = 0    #(DWORD TotalHurt)//活动总伤害,求余亿部分,如果不在榜上则读该值
+    TotalHurtEx = 0    #(DWORD TotalHurtEx)//活动总伤害,整除亿部分
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.FamilyLV,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Face,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMax,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMaxEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TotalHurt,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TotalHurtEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.FamilyLV = 0
+        self.Face = 0
+        self.FacePic = 0
+        self.FightPower = 0
+        self.FightPowerEx = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.TotalHurt = 0
+        self.TotalHurtEx = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteBYTE(data, self.FamilyLV)
+        data = CommFunc.WriteDWORD(data, self.Face)
+        data = CommFunc.WriteDWORD(data, self.FacePic)
+        data = CommFunc.WriteDWORD(data, self.FightPower)
+        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
+        data = CommFunc.WriteDWORD(data, self.HP)
+        data = CommFunc.WriteDWORD(data, self.HPEx)
+        data = CommFunc.WriteDWORD(data, self.HPMax)
+        data = CommFunc.WriteDWORD(data, self.HPMaxEx)
+        data = CommFunc.WriteDWORD(data, self.TotalHurt)
+        data = CommFunc.WriteDWORD(data, self.TotalHurtEx)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                FamilyLV:%d,
+                                Face:%d,
+                                FacePic:%d,
+                                FightPower:%d,
+                                FightPowerEx:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                TotalHurt:%d,
+                                TotalHurtEx:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.FamilyLV,
+                                self.Face,
+                                self.FacePic,
+                                self.FightPower,
+                                self.FightPowerEx,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.TotalHurt,
+                                self.TotalHurtEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZCampInfo(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)//所在活动仙盟ID,可能不是玩家当前的仙盟ID,活动以该ID为准
+    Score = 0    #(WORD Score)//活动总积分,如果不在榜上则读该值
+    CampLV = 0    #(WORD CampLV)//大本营当前等级
+    CampExp = 0    #(DWORD CampExp)//大本营当前经验
+    CityLV = 0    #(WORD CityLV)//城池属性等级,开战后可能与当前大本营等级不一样
+    HPBase = 0    #(DWORD HPBase)//基础生命,求余亿部分
+    HPBaseEx = 0    #(DWORD HPBaseEx)//基础生命,整除亿部分
+    HPMax = 0    #(DWORD HPMax)//总生命,求余亿部分
+    HPMaxEx = 0    #(DWORD HPMaxEx)//总大生命,整除亿部分
+    HP = 0    #(DWORD HP)//剩余生命,求余亿部分
+    HPEx = 0    #(DWORD HPEx)//剩余生命,整除亿部分
+    DefMemCnt = 0    #(BYTE DefMemCnt)
+    DefMemList = list()    #(vector<tagGCFamilyGCZCampMem> DefMemList)//防守成员列表,有同步则差异更新,没有在防守成员里的视为没有参与资格
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x26
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Score,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.CampLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.CampExp,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.CityLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.HPBase,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPBaseEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMax,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMaxEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.DefMemCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.DefMemCnt):
+            temDefMemList = tagGCFamilyGCZCampMem()
+            _pos = temDefMemList.ReadData(_lpData, _pos)
+            self.DefMemList.append(temDefMemList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x26
+        self.FamilyID = 0
+        self.Score = 0
+        self.CampLV = 0
+        self.CampExp = 0
+        self.CityLV = 0
+        self.HPBase = 0
+        self.HPBaseEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.DefMemCnt = 0
+        self.DefMemList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 2
+        length += 2
+        length += 4
+        length += 2
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 1
+        for i in range(self.DefMemCnt):
+            length += self.DefMemList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteWORD(data, self.Score)
+        data = CommFunc.WriteWORD(data, self.CampLV)
+        data = CommFunc.WriteDWORD(data, self.CampExp)
+        data = CommFunc.WriteWORD(data, self.CityLV)
+        data = CommFunc.WriteDWORD(data, self.HPBase)
+        data = CommFunc.WriteDWORD(data, self.HPBaseEx)
+        data = CommFunc.WriteDWORD(data, self.HPMax)
+        data = CommFunc.WriteDWORD(data, self.HPMaxEx)
+        data = CommFunc.WriteDWORD(data, self.HP)
+        data = CommFunc.WriteDWORD(data, self.HPEx)
+        data = CommFunc.WriteBYTE(data, self.DefMemCnt)
+        for i in range(self.DefMemCnt):
+            data = CommFunc.WriteString(data, self.DefMemList[i].GetLength(), self.DefMemList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                Score:%d,
+                                CampLV:%d,
+                                CampExp:%d,
+                                CityLV:%d,
+                                HPBase:%d,
+                                HPBaseEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                DefMemCnt:%d,
+                                DefMemList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.Score,
+                                self.CampLV,
+                                self.CampExp,
+                                self.CityLV,
+                                self.HPBase,
+                                self.HPBaseEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.HP,
+                                self.HPEx,
+                                self.DefMemCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZCampInfo=tagGCFamilyGCZCampInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZCampInfo.Head.Cmd,m_NAtagGCFamilyGCZCampInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZCampInfo
+
+
+#------------------------------------------------------
+# C0 27 仙盟攻城战成员贡献值信息 #tagGCFamilyGCZContributionInfo
+
+class  tagGCFamilyGCZContribution(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("PlayerID", c_int),    
+                  ("ContributionValue", c_int),    #贡献值
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.PlayerID = 0
+        self.ContributionValue = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZContribution)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 27 仙盟攻城战成员贡献值信息 //tagGCFamilyGCZContributionInfo:
+                                PlayerID:%d,
+                                ContributionValue:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.ContributionValue
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZContributionInfo(Structure):
+    Head = tagHead()
+    ContriCnt = 0    #(BYTE ContriCnt)
+    ContriList = list()    #(vector<tagGCFamilyGCZContribution> ContriList)//成员贡献值列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x27
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.ContriCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.ContriCnt):
+            temContriList = tagGCFamilyGCZContribution()
+            _pos = temContriList.ReadData(_lpData, _pos)
+            self.ContriList.append(temContriList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x27
+        self.ContriCnt = 0
+        self.ContriList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.ContriCnt):
+            length += self.ContriList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.ContriCnt)
+        for i in range(self.ContriCnt):
+            data = CommFunc.WriteString(data, self.ContriList[i].GetLength(), self.ContriList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                ContriCnt:%d,
+                                ContriList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.ContriCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZContributionInfo=tagGCFamilyGCZContributionInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZContributionInfo.Head.Cmd,m_NAtagGCFamilyGCZContributionInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZContributionInfo
+
+
+#------------------------------------------------------
+# C0 31 仙盟攻城战分组仙盟成员伤害明细 #tagGCFamilyGCZGroupFamilyMemHurtInfo
+
+class  tagGCFamilyGCZGroupFamilyMemHurt(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])//参与玩家名字
+    HurtValue = 0    #(DWORD HurtValue)//伤害,求余亿部分
+    HurtValueEx = 0    #(DWORD HurtValueEx)//伤害,整除亿部分
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.HurtValue,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HurtValueEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteDWORD(data, self.HurtValue)
+        data = CommFunc.WriteDWORD(data, self.HurtValueEx)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGroupFamilyMemHurtInfo(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)//查看的目标仙盟ID
+    HurtMemCnt = 0    #(BYTE HurtMemCnt)
+    HurtMemList = list()    #(vector<tagGCFamilyGCZGroupFamilyMemHurt> HurtMemList)//成员伤害明细列表,只算城池被摧毁前的伤害,未排序,前端自己排序
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x31
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HurtMemCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.HurtMemCnt):
+            temHurtMemList = tagGCFamilyGCZGroupFamilyMemHurt()
+            _pos = temHurtMemList.ReadData(_lpData, _pos)
+            self.HurtMemList.append(temHurtMemList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x31
+        self.FamilyID = 0
+        self.HurtMemCnt = 0
+        self.HurtMemList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        for i in range(self.HurtMemCnt):
+            length += self.HurtMemList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteBYTE(data, self.HurtMemCnt)
+        for i in range(self.HurtMemCnt):
+            data = CommFunc.WriteString(data, self.HurtMemList[i].GetLength(), self.HurtMemList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                HurtMemCnt:%d,
+                                HurtMemList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.HurtMemCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo=tagGCFamilyGCZGroupFamilyMemHurtInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo.Head.Cmd,m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo
+
+
+#------------------------------------------------------
+# C0 32 仙盟攻城战竞猜名单信息 #tagGCFamilyGCZGuessInfo
+
+class  tagGCFamilyGCZGuessFamily(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("FamilyID", c_int),    #备选仙盟ID
+                  ("GuessValue", c_ushort),    #竞猜热度值,玩家每次选择则加1,重复选择也算
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.FamilyID = 0
+        self.GuessValue = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZGuessFamily)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 32 仙盟攻城战竞猜名单信息 //tagGCFamilyGCZGuessInfo:
+                                FamilyID:%d,
+                                GuessValue:%d
+                                '''\
+                                %(
+                                self.FamilyID,
+                                self.GuessValue
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGuessRight(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("AwardID", c_ubyte),    #奖励ID
+                  ("RightPlayerCnt", c_ushort),    #猜中玩家个数
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.AwardID = 0
+        self.RightPlayerCnt = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZGuessRight)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 32 仙盟攻城战竞猜名单信息 //tagGCFamilyGCZGuessInfo:
+                                AwardID:%d,
+                                RightPlayerCnt:%d
+                                '''\
+                                %(
+                                self.AwardID,
+                                self.RightPlayerCnt
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGuessInfo(Structure):
+    Head = tagHead()
+    PlayerID = 0    #(DWORD PlayerID)//竞猜玩家ID,有玩家修改竞猜时会附带该信息,如果是自己ID,则更新自己的竞猜选项,否则无视
+    SelectCnt = 0    #(BYTE SelectCnt)
+    SelectFamilyIDList = list()    #(vector<DWORD> SelectFamilyIDList)//竞猜玩家选择的仙盟ID顺序
+    FinalCnt = 0    #(BYTE FinalCnt)
+    FinalFamilyIDList = list()    #(vector<DWORD> FinalFamilyIDList)//最终排名顺序,仅活动排名出来后才有值
+    RightCnt = 0    #(BYTE RightCnt)
+    RightInfoList = list()    #(vector<tagGCFamilyGCZGuessRight> RightInfoList)//猜中个数明细列表
+    FamilyCnt = 0    #(BYTE FamilyCnt)
+    GuessFamilyList = list()    #(vector<tagGCFamilyGCZGuessFamily> GuessFamilyList)//备选仙盟名单列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x32
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.SelectCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.SelectCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.SelectFamilyIDList.append(value)
+        self.FinalCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FinalCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.FinalFamilyIDList.append(value)
+        self.RightCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.RightCnt):
+            temRightInfoList = tagGCFamilyGCZGuessRight()
+            _pos = temRightInfoList.ReadData(_lpData, _pos)
+            self.RightInfoList.append(temRightInfoList)
+        self.FamilyCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyCnt):
+            temGuessFamilyList = tagGCFamilyGCZGuessFamily()
+            _pos = temGuessFamilyList.ReadData(_lpData, _pos)
+            self.GuessFamilyList.append(temGuessFamilyList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x32
+        self.PlayerID = 0
+        self.SelectCnt = 0
+        self.SelectFamilyIDList = list()
+        self.FinalCnt = 0
+        self.FinalFamilyIDList = list()
+        self.RightCnt = 0
+        self.RightInfoList = list()
+        self.FamilyCnt = 0
+        self.GuessFamilyList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        length += 4 * self.SelectCnt
+        length += 1
+        length += 4 * self.FinalCnt
+        length += 1
+        for i in range(self.RightCnt):
+            length += self.RightInfoList[i].GetLength()
+        length += 1
+        for i in range(self.FamilyCnt):
+            length += self.GuessFamilyList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteBYTE(data, self.SelectCnt)
+        for i in range(self.SelectCnt):
+            data = CommFunc.WriteDWORD(data, self.SelectFamilyIDList[i])
+        data = CommFunc.WriteBYTE(data, self.FinalCnt)
+        for i in range(self.FinalCnt):
+            data = CommFunc.WriteDWORD(data, self.FinalFamilyIDList[i])
+        data = CommFunc.WriteBYTE(data, self.RightCnt)
+        for i in range(self.RightCnt):
+            data = CommFunc.WriteString(data, self.RightInfoList[i].GetLength(), self.RightInfoList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.FamilyCnt)
+        for i in range(self.FamilyCnt):
+            data = CommFunc.WriteString(data, self.GuessFamilyList[i].GetLength(), self.GuessFamilyList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                PlayerID:%d,
+                                SelectCnt:%d,
+                                SelectFamilyIDList:%s,
+                                FinalCnt:%d,
+                                FinalFamilyIDList:%s,
+                                RightCnt:%d,
+                                RightInfoList:%s,
+                                FamilyCnt:%d,
+                                GuessFamilyList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.PlayerID,
+                                self.SelectCnt,
+                                "...",
+                                self.FinalCnt,
+                                "...",
+                                self.RightCnt,
+                                "...",
+                                self.FamilyCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZGuessInfo=tagGCFamilyGCZGuessInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZGuessInfo.Head.Cmd,m_NAtagGCFamilyGCZGuessInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZGuessInfo
 
 
 #------------------------------------------------------
@@ -57675,6 +59419,70 @@
 
 
 #------------------------------------------------------
+# C1 10 仙盟攻城战玩家信息 #tagMCFamilyGCZPlayerInfo
+
+class  tagMCFamilyGCZPlayerInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ContributionCnt", c_ubyte),    # 轮次低级捐献已捐献次数,轮次变更时会重置
+                  ("Energy", c_ubyte),    # 当前可用免费体力
+                  ("EnergyTime", c_int),    # 上次恢复免费体力时间戳,为0时不再恢复
+                  ("AwardState", c_int),    # 活动奖励领取状态,按二进制位判断是否已领取,0-竞猜奖励;1-个人排行奖励;2-仙盟排名奖励;
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x10
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x10
+        self.ContributionCnt = 0
+        self.Energy = 0
+        self.EnergyTime = 0
+        self.AwardState = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCFamilyGCZPlayerInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 10 仙盟攻城战玩家信息 //tagMCFamilyGCZPlayerInfo:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ContributionCnt:%d,
+                                Energy:%d,
+                                EnergyTime:%d,
+                                AwardState:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ContributionCnt,
+                                self.Energy,
+                                self.EnergyTime,
+                                self.AwardState
+                                )
+        return DumpString
+
+
+m_NAtagMCFamilyGCZPlayerInfo=tagMCFamilyGCZPlayerInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCFamilyGCZPlayerInfo.Cmd,m_NAtagMCFamilyGCZPlayerInfo.SubCmd))] = m_NAtagMCFamilyGCZPlayerInfo
+
+
+#------------------------------------------------------
 # C1 08 幸运云购玩家信息 #tagMCLuckyCloudBuyPlayerInfo
 
 class  tagMCLuckyCloudBuyPlayerInfo(Structure):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CreateFamily.py b/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CreateFamily.py
index 0d572b6..5382fb2 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CreateFamily.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/CreateFamily.py
@@ -18,7 +18,10 @@
 import ShareDefine
 import PlayerFamily
 import GameWorldFamilyWar
+import IPY_GameServer
+import random
 
+FackFamilyIDStart = 1000000000
 
 ## 执行逻辑
 #  @param curPlayer 当前玩家
@@ -27,11 +30,17 @@
 def OnExec(curPlayer, gmList):
     
     if not gmList:
-        GameWorld.DebugAnswer(curPlayer, "CreatFamily 创建假仙盟个数 可选参数(仙盟等级 是否设置联赛名次)")
-        GameWorld.DebugAnswer(curPlayer, "删除创建的假仙盟: CreatFamily 0")
+        GameWorld.DebugAnswer(curPlayer, "创建跨服假仙盟: CreatFamily c 个数 [总战力 ServerID 等级 成员数]")
+        GameWorld.DebugAnswer(curPlayer, "删除跨服假仙盟: CreatFamily c 0")
+        GameWorld.DebugAnswer(curPlayer, "创建本服假仙盟: CreatFamily 个数 [等级 设置联赛名次]")
+        GameWorld.DebugAnswer(curPlayer, "删除本服假仙盟: CreatFamily 0")
         return
     
-    creatCount = gmList[0]
+    value1 = gmList[0]
+    if value1 == "c":
+        return
+    
+    creatCount = value1
     if creatCount <= 0:
         delFamilyList = []
         familyManager = GameWorld.GetFamilyManager()
@@ -94,4 +103,135 @@
     PlayerFamily.DoFamilySort()
     return
 
+def OnGetMergeParam(curPlayer):
+    return []
+
+def OnMergeServerExec(gmList, tick):
+    ## 跨服执行命令
+    if not gmList:
+        return
+    
+    value1 = gmList[0]
+    if value1 != "c":
+        return
+    
+    value2 = gmList[1] if len(gmList) > 1 else 1 # 默认1个
+    if not value2:
+        __delMergeServerFackFamily()
+        return
+    
+    __createMergeServerFackFamily(gmList)
+    return
+
+def __delMergeServerFackFamily():
+    
+    delCnt = 0
+    familyManager = GameWorld.GetFamilyManager()
+    for i in range(familyManager.GetCount())[::-1]:
+        family = familyManager.GetAt(i)
+        if not family:
+            continue
+        if family.GetID() >= FackFamilyIDStart:
+            familyManager.DelFamily(family.GetID())
+            delCnt += 1
+            
+    GameWorld.DebugAnswer(None, "成功删除跨服假仙盟: %s,剩余:%s" % (delCnt, familyManager.GetCount()))
+    return
+
+def __createMergeServerFackFamily(gmList):
+    ## c 个数 [总战力 ServerID 等级 成员数]
+    
+    creatCount = gmList[1] if len(gmList) > 1 else 1 # 默认1个
+    familyFightPower = gmList[2] if len(gmList) > 2 else 10000000
+    serverID = gmList[3] if len(gmList) > 3 else random.randint(1, 200)
+    familyLV = gmList[4] if len(gmList) > 4 else 1 # 默认1级
+    memberCnt = gmList[5] if len(gmList) > 5 else random.randint(1, 10)
+    
+    FakeFamilyName = "假仙盟".decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding())
+    FakeMemberName = "假成员".decode(ShareDefine.Def_Game_Character_Encoding).encode(GameWorld.GetCharacterEncoding())
+    
+    fackFamilyID = FackFamilyIDStart # 跨服假仙盟ID从10亿开始,因为子服的仙盟ID分配是从1开始
+    fackFamilyIDMax = fackFamilyID + 10000 # 限制最大假盟数
+    fackMemID = 1 # 假成员 1~9999
+    fackMemIDMax = 9999
+    fackMemIDList = []
+    createOKCnt = 0
+    familyManager = GameWorld.GetFamilyManager()
+    for _ in range(min(creatCount, 100)):
+        
+        fackFamily = familyManager.FindFamily(fackFamilyID)
+        while fackFamily and fackFamilyID < fackFamilyIDMax:
+            for m in xrange(fackFamily.GetCount()):
+                member = fackFamily.GetAt(m)
+                memID = member.GetPlayerID()
+                if memID and memID not in fackMemIDList:
+                    fackMemIDList.append(memID)
+            fackFamilyID += 1
+            fackFamily = familyManager.FindFamily(fackFamilyID)
+            
+        if fackFamily:
+            break
+        
+        fackFamilyName = "%s%s" % (FakeFamilyName, fackFamilyID)
+        fackFamily = familyManager.AddFamilyEx(fackFamilyName, fackFamilyID)
+        fackFamilyID += 1
+        if not fackFamily:
+            continue
+        
+        fackFamily.SetLV(familyLV)
+        PlayerFamily.SetFamilyTotalFightPower(fackFamily, familyFightPower)
+        PlayerFamily.SetFamilyEmblemID(fackFamily, 1)
+        fackFamily.SetServerID(serverID)
+        
+        GameWorld.DebugLog("创建假仙盟: fackFamilyID=%s,serverID=%s,familyFightPower=%s,memberCnt=%s" 
+                           % (fackFamilyID, serverID, familyFightPower, memberCnt))
+        for _ in range(memberCnt):
+            
+            while fackMemID in fackMemIDList and fackMemID < fackMemIDMax:
+                fackMemID += 1
+                
+            if fackMemID in fackMemIDList:
+                break
+            fackMemName = "%s%s" % (FakeMemberName, fackMemID)
+            
+            member = fackFamily.AddMemberEx(fackMemID)
+            if not member:
+                continue
+            
+            member.SetName(fackMemName)
+            member.SetLV(random.randint(100, 200))
+            member.SetJob(random.randint(1, 2))
+            member.SetOfficialRank(random.randint(1, 20))
+            
+            if not fackFamily.GetLeaderID():
+                member.SetFamilyLV(IPY_GameServer.fmlLeader)
+                fackFamily.SetLeaderID(fackMemID)
+                fackFamily.SetLeaderName(fackMemName)
+                fackFamily.SetLeaderOfficialRank(member.GetOfficialRank())
+                
+            fackMemID += 1
+            
+        # 根据成功创建的成员平均分配总战力
+        memFightPowerTotal = familyFightPower
+        memCnt = fackFamily.GetCount()
+        avgValue = memFightPowerTotal / memCnt
+        for m in range(memCnt):
+            member = fackFamily.GetAt(m)
+            memID = member.GetPlayerID()
+            fmLV = member.GetFamilyLV()
+            if m == memCnt - 1:
+                memFightPower = memFightPowerTotal
+            else:
+                memFightPower = avgValue + memCnt / 2 - m
+                memFightPowerTotal -= memFightPower
+            PlayerFamily.SetMemberFightPower(member, memFightPower)
+            GameWorld.DebugLog("    仙盟成员: memID=%s,memFightPower=%s,fmLV=%s" % (memID, memFightPower, fmLV))
+            
+        createOKCnt += 1
+        familyFightPower += 10000
+        
+    GameWorld.DebugAnswer(None, "成功创建跨服假仙盟: %s,总:%s" % (creatCount, familyManager.GetCount()))
+    return
+
+
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/GMT_GetFamilyByServerID.py b/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/GMT_GetFamilyByServerID.py
new file mode 100644
index 0000000..7aebf12
--- /dev/null
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GM/Commands/GMT_GetFamilyByServerID.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.GMT_GetFamilyByServerID
+#
+# @todo:查询仙盟
+# @author hxp
+# @date 2025-04-09
+# @version 1.0
+#
+# 详细描述: 查询仙盟 - 根据所属区服ID条件
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-04-09 16:00"""
+#-------------------------------------------------------------------------------
+
+import GMCommon
+import GameWorld
+import PlayerFamily
+import ChPlayer
+
+def OnExec(orderId, gmCmdDict):
+    serverIDList = eval(gmCmdDict.get("serverIDList", '[]')) 
+    queryCnt = GameWorld.ToIntDef(gmCmdDict.get("queryCnt", '0')  , 100)
+    GameWorld.DebugLog("GMT_GetFamilyByServerID queryCnt=%s, %s, %s" % (queryCnt, serverIDList, gmCmdDict))
+    
+    isCrossServer = GameWorld.IsCrossServer()
+    familyMgr = GameWorld.GetFamilyManager()
+    if isCrossServer:
+        sortFamilyIDList, totalCnt = PlayerFamily.SortCrossFamily(serverIDList, queryCnt)
+    else:
+        sortFamilyIDList = PlayerFamily.GetSortFamilyIDList()
+        totalCnt = len(sortFamilyIDList)
+        
+    retCnt = 0
+    familyList = []
+    for familyID in sortFamilyIDList:
+        if isCrossServer:
+            curFamily = familyID
+            familyID = curFamily.GetID()
+        else:
+            curFamily = familyMgr.FindFamily(familyID)
+        if not curFamily:
+            continue
+        familyList.append(GetQueryFamilyInfo(curFamily, isCrossServer))
+        retCnt += 1
+        if retCnt >= queryCnt:
+            break
+        
+    #执行成功
+    retMsg = {"familyList":familyList, "totalCnt":totalCnt}
+    GMCommon.GMCommandResult(orderId, gmCmdDict, GMCommon.Def_Success, retMsg)        
+    return
+
+def GetQueryFamilyInfo(curFamily, isCrossServer):
+    if curFamily == None:
+        return {}
+    
+    onlineCnt = 0
+    memberCnt = curFamily.GetCount()
+    onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+    for index in range(memberCnt):
+        curMember = curFamily.GetAt(index)
+        playerID = curMember.GetPlayerID()
+        if onlineMgr.IsOnline(playerID):
+            onlineCnt += 1
+            
+    familyInfo = {
+                  'ID':curFamily.GetID(),
+                  'Name':curFamily.GetName(),
+                  'FightPower':PlayerFamily.GetFamilyTotalFightPower(curFamily), # 总战力
+                  'LeaderID':curFamily.GetLeaderID(),
+                  'LeaderName':curFamily.GetLeaderName(),
+                  'LV':curFamily.GetLV(),
+                  'MemberCnt':memberCnt,
+                  'OnLineCnt':onlineCnt,
+                  'ServerID':curFamily.GetServerID(),
+                  }
+    
+    return familyInfo
+    
+    
\ No newline at end of file
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
index d7ad0ea..31bad23 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py
@@ -1223,6 +1223,98 @@
     # 找不到的默认取最后一名
     return orderDict[orderList[-1]] if isDefaultLast else defaultValue
 
+def GetActBillboardTempAward(playerID, billID, billRank, awardTemplateID, billValue=None, fmLV=None):
+    '''获取玩家活动榜单领奖奖励
+    @param playerID: 领奖玩家ID
+    @param billID: 上榜ID,不一定是玩家ID,比如仙盟ID
+    @param billRank: 上榜名次
+    @param awardTemplateID: 活动奖励模版ID
+    @param billValue: 榜单上榜值,None时不处理
+    @param fmLV: 活动时的仙盟成员等级,None时不处理
+    '''
+    playerAwardItemList = []
+    if not billRank:
+        return playerAwardItemList
+    ipyDataList = IpyGameDataPY.GetIpyGameDataList("ActBillboardAwardTemp", awardTemplateID)
+    if not ipyDataList:
+        ErrLog("活动榜单奖励模版找不到模版! billID=%s,billRank=%s,awardTemplateID=%s,billValue=%s,fmLV=%s" 
+               % (billID, billRank, awardTemplateID, billValue, fmLV), playerID)
+        return playerAwardItemList
+    
+    for ipyData in ipyDataList:
+        rank = ipyData.GetRank()
+        if billRank > rank:
+            continue
+        needValue = ipyData.GetNeedValue()
+        if needValue and billValue != None and billValue < needValue:
+            continue
+        
+        awardItemList = ipyData.GetAwardItemList()
+        leaderAwardItemList = ipyData.GetLeaderAwardItemList()
+        eliteAwardItemList = ipyData.GetEliteAwardItemList()
+        
+        if fmLV != None and fmLV == IPY_GameServer.fmlLeader and leaderAwardItemList:
+            playerAwardItemList += leaderAwardItemList
+        elif fmLV != None and fmLV > IPY_GameServer.fmlMember and eliteAwardItemList:
+            playerAwardItemList += eliteAwardItemList
+        else:
+            playerAwardItemList += awardItemList
+            
+        valueAwardEx = ipyData.GetValueAwardEx()
+        if valueAwardEx and billValue != None:
+            valueAwardExList = valueAwardEx.keys()
+            valueAwardExList.sort()
+            awardItemExList = []
+            for valueEx in valueAwardExList:
+                if billValue < valueEx:
+                    break
+                awardItemExList = valueAwardEx[valueEx] # 取最大满足条件的一档
+            playerAwardItemList += awardItemExList
+            
+        return playerAwardItemList
+    
+    ErrLog("活动榜单奖励模版找不到奖励! billID=%s,billRank=%s,awardTemplateID=%s,billValue=%s,fmLV=%s" 
+           % (billID, billRank, awardTemplateID, billValue, fmLV), playerID)
+    return playerAwardItemList
+
+def GetActGuessRightRankAwardIDDict(guessTemplateID):
+    ## 获取活动竞猜默认猜中名次对应奖励ID信息
+    # @return: {(名次, ...):awardID, ...} 用于 StatActGuessRet 统计竞猜结果
+    rightRankAwardIDDict = {}
+    guessIpyDataList = IpyGameDataPY.GetIpyGameDataList("ActGuess", guessTemplateID)
+    if guessIpyDataList:
+        for ipyData in guessIpyDataList:
+            awardID = ipyData.GetAwardID()
+            rightRankList = ipyData.GetRightRankList()
+            rightRankAwardIDDict[tuple(rightRankList)] = awardID
+    return rightRankAwardIDDict
+
+def StatActGuessRet(playerID, playerGuessIDList, finalRankIDList, rightRankAwardIDDict, statGuessRetDict, actName=""):
+    '''统计活动竞猜结果
+    @param playerID: 参与竞猜的玩家ID
+    @param playerGuessIDList: 玩家竞猜选择的ID顺序列表
+    @param finalRankIDList: 活动最终排名顺序列表
+    @param rightRankAwardIDDict: GetActGuessRightRankAwardIDDict 返回值
+    @param statGuessRetDict: 统计结果 {awardID:[猜中的玩家ID, ...], ...}
+    @param actName: 活动名称,可选参数,输出日志用
+    '''
+    rightRankList = [] # 玩家猜中的名次列表
+    for index, finalID in enumerate(finalRankIDList):
+        rank = index + 1
+        guessID = playerGuessIDList[index] if len(playerGuessIDList) > index else 0
+        if guessID == finalID:
+            rightRankList.append(rank)
+            
+    rightRankTuple = tuple(rightRankList)
+    awardID = rightRankAwardIDDict.get(rightRankTuple, 0)
+    if awardID not in statGuessRetDict:
+        statGuessRetDict[awardID] = []
+    rightPlayerIDList = statGuessRetDict[awardID]
+    rightPlayerIDList.append(playerID)
+    DebugLog("    %s统计玩家竞猜结果: playerID=%s,rightRankTuple=%s,awardID=%s,%s" 
+             % (actName, playerID, rightRankTuple, awardID, len(rightPlayerIDList)))
+    return
+
 ##概率相关, 这个事件是否能够出现
 # @param rate 基础几率
 # @param maxRate 最大几率
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py
index 0d1c52f..7772342 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossActionControl.py
@@ -27,6 +27,8 @@
 import PlayerActGubao
 import PlayerActHorsePetTrain
 import PlayerActLianqi
+import PlayerDBGSEvent
+import CrossFamilyGCZ
 import CrossRealmMsg
 import PyGameData
 import ChConfig
@@ -137,6 +139,37 @@
         if zoneID != ipyZoneID:
             return
     return actInfo
+
+def GetCrossActInfoByZoneID(actName, zoneID):
+    ## 获取分区ID对应的跨服活动信息
+    crossActInfoDict = GetCrossActInfoDict()
+    if actName not in crossActInfoDict:
+        return
+    curActInfoDict = crossActInfoDict[actName]
+    for actInfo in curActInfoDict.values():
+        ipyDataDict = actInfo.get(ShareDefine.ActKey_IpyDataInfo, {})
+        if not ipyDataDict:
+            return
+        ipyZoneID = ipyDataDict.get("ZoneID", 0)
+        if zoneID == ipyZoneID:
+            return actInfo
+    return
+
+def GetCrossActZoneIDList(actName):
+    ## 获取跨服活动当前所有分区列表
+    crossActInfoDict = GetCrossActInfoDict()
+    if actName not in crossActInfoDict:
+        return []
+    zoneIDList = []
+    curActInfoDict = crossActInfoDict[actName]
+    for actInfo in curActInfoDict.values():
+        ipyDataDict = actInfo.get(ShareDefine.ActKey_IpyDataInfo, {})
+        if not ipyDataDict:
+            return
+        zoneID = ipyDataDict.get("ZoneID", 0)
+        if zoneID and zoneID not in zoneIDList:
+            zoneIDList.append(zoneID)
+    return zoneIDList
 
 def GetCrossActInfoDict():
     if PyGameData.g_crossActInfoDict == None:
@@ -530,6 +563,7 @@
     actChangeList = []
     actStateChangeList = []
     sysnCrossActInfoDict = {}
+    flowStateErrorResetList = []
     for actName in ShareDefine.CrossActNameList:
         if actName not in actTimeInfoDict or actName not in crossActInfoDict or actName not in actCfgIDInfoDict:
             continue
@@ -549,6 +583,8 @@
             cfgID = ipyData.GetCfgID()
             groupName = ipyData.GetActGroupName()
             zoneID = ipyData.GetZoneID()
+            actFlowID = ipyData.GetActFlowID() if hasattr(ipyData, "GetActFlowID") else 0 # 活动流程ID
+            actStartDataTime = None
             
             if cfgID not in crossActInfoDict[actName]:
                 crossActInfoDict[actName][cfgID] = {}
@@ -560,6 +596,7 @@
                     endDateTime = endList[dIndex]
                     if startDateTime <= curDateTime < endDateTime:
                         state = dIndex + 1 # 也是代表第几个时间段
+                        actStartDataTime = startDateTime
                         break
                 if endList:
                     isEnd = (curDateTime >= endList[-1])
@@ -575,18 +612,11 @@
                 else:
                     stateJoin = ShareDefine.ActStateJoin_Start if state else ShareDefine.ActStateJoin_None
                     
+            actID = actInfoDict.get(ShareDefine.ActKey_ID, 0)
+            templateID = actInfoDict.get(ShareDefine.ActKey_TemplateID, 0)
             serverIDRangeList = actInfoDict.get(ShareDefine.ActKey_ServerIDRangeList)
-            # 全服广播提示信息
-            if curDateTime in notifyDict:
-                if serverIDRangeList != None:
-                    notifyKey, paramList = notifyDict[curDateTime]
-                    country = 0
-                    serverGroupIDList = []
-                    crossNotifyList = []
-                    crossNotifyList.append([ShareDefine.CrossNotify_CrossAct, [country, notifyKey, paramList], serverIDRangeList])
-                    PlayerControl.CrossNotifyEx(serverGroupIDList, crossNotifyList)
-                
             dbInfo = actInfoDict.get(ShareDefine.ActKey_DBInfo, {})
+            
             dbState = dbInfo.get(ShareDefine.ActKey_State, 0)
             dbStateJoin = dbInfo.get(ShareDefine.ActKey_StateJoin, 0)
             dbCfgID = dbInfo.get(ShareDefine.ActKey_CfgID, 0)
@@ -598,8 +628,33 @@
             if dbCfgID == cfgID and dbServerIDRangeList != serverIDRangeList:
                 forceReset = True
                 
-            actID = actInfoDict.get(ShareDefine.ActKey_ID, 0)
-            templateID = actInfoDict.get(ShareDefine.ActKey_TemplateID, 0)
+            errStateActID = 0
+            if state and actFlowID: # 有活动流程ID的,实际状态按取活动流程对应状态
+                errStateActID = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ActFlowStateError % (actName, zoneID))
+                # 已经是不同活动了,强制重置异常状态标记,防止影响新活动
+                if errStateActID and (errStateActID != actID or not dbState):
+                    GameWorld.Log("按流程走的活动流程上次异常,新活动重置状态! %s,zoneID=%s,actID=%s,errStateActID=%s,dbState=%s" % (actName, zoneID, actID, errStateActID, dbState))
+                    PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ActFlowStateError % (actName, zoneID), 0)
+                    errStateActID = 0
+                    
+                flowStateIndex, flowState, flowStatePre = GetActTimeFlowState(actFlowID, actStartDataTime, curDateTime)
+                # 非第一个流程状态变更的时候,要验证流程是否正确,如果异常,则流程照走,标记异常,具体异常的处理由各活动自行处理
+                if flowStateIndex > 0 and flowState != dbState and flowStatePre != dbState and not errStateActID:
+                    GameWorld.ErrLog("按流程走的活动流程已异常! %s,zoneID=%s,actID=%s,flowStatePre=%s,dbState=%s" % (actName, zoneID, actID, flowStatePre, dbState))
+                    PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ActFlowStateError % (actName, zoneID), actID)
+                    errStateActID = actID
+                state = flowState
+                
+            # 全服广播提示信息
+            if curDateTime in notifyDict and not flowState and not errStateActID:
+                if serverIDRangeList != None:
+                    notifyKey, paramList = notifyDict[curDateTime]
+                    country = 0
+                    serverGroupIDList = []
+                    crossNotifyList = []
+                    crossNotifyList.append([ShareDefine.CrossNotify_CrossAct, [country, notifyKey, paramList], serverIDRangeList])
+                    PlayerControl.CrossNotifyEx(serverGroupIDList, crossNotifyList)
+                    
             if not isReload and dbState == state and dbStateJoin == stateJoin and dbActID == actID and not forceReset:
                 #已经是这个状态了
                 continue
@@ -612,6 +667,9 @@
             dbInfo = {ShareDefine.ActKey_ID:actID, ShareDefine.ActKey_State:state, ShareDefine.ActKey_TemplateID:templateID, 
                       ShareDefine.ActKey_CfgID:cfgID, ShareDefine.ActKey_ServerIDRangeList:serverIDRangeList, 
                       ShareDefine.ActKey_StateJoin:stateJoin}
+            if errStateActID:
+                actInfoDict[ShareDefine.ActKey_StateError] = errStateActID
+                
             actInfoDict[ShareDefine.ActKey_DBInfo] = dbInfo
             #GameWorld.Log("    活动状态同步信息: actName=%s,cfgID=%s,groupName=%s,zoneID=%s,actInfoDict=%s" % (actName, cfgID, groupName, zoneID, actInfoDict))
             if actName not in sysnCrossActInfoDict:
@@ -622,6 +680,9 @@
                 GameWorld.Log("    活动ID变更: actName=%s,cfgID=%s,groupName=%s,zoneID=%s,dbActID=%s -> actID=%s,forceReset=%s,dbTemplateID=%s" 
                               % (actName, cfgID, groupName, zoneID, dbActID, actID, forceReset, dbTemplateID))
                 
+                if errStateActID:
+                    flowStateErrorResetList.append((actName, zoneID))
+                    
                 if actName == ShareDefine.CrossActName_CTGBillboard:
                     CrossActCTGBillboard.OnActIDChange(cfgID, dbTemplateID, state)
                     
@@ -640,18 +701,21 @@
                 elif actName == ShareDefine.CrossActName_Lianqi:
                     PlayerActLianqi.OnCrossActIDChange(cfgID, zoneID, ipyData, state)
                     
+                elif actName == ShareDefine.CrossActName_FamilyGCZ:
+                    CrossFamilyGCZ.OnCrossActIDChange(cfgID, zoneID, ipyData, state)
+                    
                 else:
                     actChangeList.append([actName, ipyData, state, cfgID, groupName, zoneID, dbActID, actID, forceReset, dbTemplateID])
                     
                 # 活动ID变更强制视为状态变更,防止维护前后状态一样,但其实活动ID已经不同的情况会导致无法触发状态变更
                 actIDChange = True
-                actStateChangeList.append([actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, dbTemplateID])
+                actStateChangeList.append([actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, actID, dbTemplateID])
                 
             elif dbState != state:
                 actIDChange = False
                 GameWorld.Log("    活动状态变更: actName=%s,cfgID=%s,groupName=%s,zoneID=%s,dbState=%s -> state=%s,actIDChange=%s,dbTemplateID=%s" 
                               % (actName, cfgID, groupName, zoneID, dbState, state, actIDChange, dbTemplateID))
-                actStateChangeList.append([actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, dbTemplateID])
+                actStateChangeList.append([actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, actID, dbTemplateID])
                 
             # 活动中刷新,每次都需要刷新的逻辑,包含重读配置等
             if state:
@@ -713,15 +777,67 @@
             CrossActAllRecharge.OnActIDChange(ipyData, state)
             
     for changeInfo in actStateChangeList:
-        actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, dbTemplateID = changeInfo
+        actName, ipyData, dbState, state, cfgID, groupName, zoneID, actIDChange, actID, dbTemplateID = changeInfo
         GameWorld.Log("    活动状态变更: actName=%s,cfgID=%s,groupName=%s,zoneID=%s,dbState=%s -> state=%s,actIDChange=%s,dbTemplateID=%s" 
                       % (actName, cfgID, groupName, zoneID, dbState, state, actIDChange, dbTemplateID))
         
         if actName == ShareDefine.CrossActName_LuckyCloudBuy:
             import CrossLuckyCloudBuy
             CrossLuckyCloudBuy.OnLuckyCloudBuyStateChange(ipyData, actIDChange, state)
+        elif actName == ShareDefine.CrossActName_FamilyGCZ:
+            CrossFamilyGCZ.OnCrossActStateChange(ipyData, actID, dbState, state)
             
+    for actName, zoneID in flowStateErrorResetList:
+        GameWorld.Log("活动结束重置按流程走的活动流程异常状态! %s,zoneID=%s" % (actName, zoneID))
+        PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ActFlowStateError % (actName, zoneID), 0)
+        
     return
+
+def IsActFlowStateError(actName, zoneID):
+    ## 流程状态是否已异常
+    errStateActID = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ActFlowStateError % (actName, zoneID))
+    if errStateActID:
+        GameWorld.ErrLog("活动流程状态已异常! %s,zoneID=%s,errStateActID=%s" % (actName, zoneID, errStateActID))
+        return True
+    return False
+
+def GetActTimeFlowState(actFlowID, actStartDataTime, curDateTime):
+    ## 获取活动流程ID对应当前状态
+    ipyDataList = IpyGameDataPY.GetIpyGameDataList("ActTimeFlow", actFlowID)
+    if not ipyDataList:
+        return 0
+    
+    flowStatePre = 0
+    flowState = 0
+    flowStateIndex = -1
+    for index, timeIpyData in enumerate(ipyDataList):
+        #dataID = timeIpyData.GetID()
+        startDay, startHour, startMinute = timeIpyData.GetStartDay(), timeIpyData.GetStartHour(), timeIpyData.GetStartMinute()
+        endDay, endHour, endMinute = timeIpyData.GetEndDay(), timeIpyData.GetEndHour(), timeIpyData.GetEndMinute()
+        
+        startSeconds = ((startDay - 1) * 24 + startHour) * 3600 + startMinute * 60
+        endSeconds = ((endDay - 1) * 24 + endHour) * 3600 + endMinute * 60
+        startDateTime = actStartDataTime + datetime.timedelta(seconds=startSeconds)
+        endDateTime = actStartDataTime + datetime.timedelta(seconds=endSeconds)
+        if curDateTime < startDateTime or curDateTime > endDateTime:
+            flowStatePre = timeIpyData.GetStateValue()
+            continue
+        flowState = timeIpyData.GetStateValue()
+        flowStateIndex = index
+        #notifyInfoDict = timeIpyData.GetNotifyInfo()
+        #if not stateError and notifyInfoDict:
+        #    diffDateTime = curDateTime - startDateTime
+        #    diffMinute = (diffDateTime.days * 24 * 3600 + diffDateTime.seconds) / 60 # 当前时间与开始时间相差分钟数
+        #    GameWorld.DebugLog("    广播判断: curDateTime=%s,startDateTime=%s,diffDays=%s,diffSeconds=%s,diffMinute=%s" 
+        #                       % (curDateTime, startDateTime, diffDateTime.days, diffDateTime.seconds, diffMinute))
+        #    if diffMinute in notifyInfoDict:
+        #        notifyKey, paramList = notifyInfoDict[diffMinute]
+        #        PlayerControl.WorldNotifyCross(serverGroupIDList, 0, notifyKey, paramList)
+        break
+    
+    if not flowState:
+        flowStatePre = 0
+    return flowStateIndex, flowState, flowStatePre
 
 def Sync_CrossActInfoToClientServer(serverGroupID=0):
     ''' 同步跨服运营活动信息到子服务器
@@ -761,6 +877,10 @@
             
         GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossActInfo % actName, syncActInfoDict)
         
+        # 以下需要等活动信息同步给地图后才处理
+        if actName == ShareDefine.CrossActName_FamilyGCZ:
+            CrossFamilyGCZ.ClientServer_CrossActInfo()
+                
     # 删除非活动中的
     for actName, actInfoDict in PyGameData.g_crossActInfoDict.items():
         for cfgID, actInfo in actInfoDict.items():
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py
index 93bbf87..d3fec6f 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossBillboard.py
@@ -19,6 +19,7 @@
 import ShareDefine
 import IpyGameDataPY
 import CrossRealmMsg
+import PlayerViewCache
 import PyGameDataStruct
 import ChPyNetSendPack
 import DataRecordPack
@@ -66,6 +67,27 @@
         for key in self.__billboardDict.keys():
             if key[0] == billboardType:
                 self.__billboardDict.pop(key)
+        return
+    
+    def ClearBillboard(self, billboardType, groupValue1=None, groupValue2=None):
+        '''清除某个类型的榜单所有数据,可指定过滤groupValue,有流向记录
+        @param groupValue1: 不为None时验证 groupValue1 是否相同
+        @param groupValue2: 不为None时验证 groupValue2 是否相同
+        如果groupValue1 groupValue2 都传入None,相当于清空 billboardType 类型的所有榜单数据
+        '''
+        clearList = []
+        for bType, gValue1, gValue2 in self.__billboardDict.keys():
+            if bType != billboardType:
+                continue
+            if groupValue1 != None and groupValue1 != gValue1:
+                continue
+            if groupValue2 != None and groupValue2 != gValue2:
+                continue
+            key = (bType, gValue1, gValue2)
+            clearList.append(key)
+            
+        for bType, gValue1, gValue2 in clearList:
+            self.GetCrossBillboard(bType, gValue1, gValue2).ClearData()
         return
     
     # 保存数据 存数据库和realtimebackup
@@ -450,8 +472,8 @@
 #{
 #    tagHead        Head;
 #    BYTE        Type;        //榜单类型
-#    BYTE        GroupValue1;    // 分组值1
-#    BYTE        GroupValue2;    // 分组值2,与分组值1组合归为同组榜单数据
+#    DWORD        GroupValue1;    // 分组值1
+#    DWORD        GroupValue2;    // 分组值2,与分组值1组合归为同组榜单数据
 #    DWORD        StartIndex;    //查看的起始名次索引, 默认0
 #    BYTE        WatchCnt;    //查看条数,默认20,最大不超过100
 #    DWORD        WatchID;        //查看指定ID名次前后,如玩家ID、家族ID等
@@ -771,6 +793,21 @@
                       id2=id2, autoSort=autoSort, value3=value3, value4=value4, value5=value5)
     return
 
+def UpdCrossBillboardPlayer(bType, playerID, groupValue1, cmpValue, cmpValue2=0, value1=0, value2=0, groupValue2=0, autoSort=True, **kwargs):
+    ## 通用的更新跨服玩家榜,GameServer直接调用
+    playerInfo = PlayerViewCache.GetShotCacheDict(playerID, "AccID", "ServerID", "Face", "FacePic")
+    name1 = playerInfo.get("Name", "")
+    name2 = playerInfo.get("AccID", "")
+    type2 = playerInfo.get("Job", 1)
+    if not value1:
+        value1 = playerInfo.get("RealmLV", 1)
+    value3 = playerInfo.get("Face", 0)
+    value4 = playerInfo.get("FacePic", 0)
+    value5 = playerInfo.get("ServerID", 0)
+    UpdCrossBillboard(bType, groupValue1, playerID, name1, name2, type2, value1, value2, cmpValue, cmpValue2, 
+                      autoSort=autoSort, value3=value3, value4=value4, value5=value5)
+    return
+
 def UpdCrossBillboard(billboardType, groupValue1, dataID, name1, name2, type2, value1, value2, cmpValue,
                       cmpValue2=0, cmpValue3=0, groupValue2=0, id2=0, autoSort=True, noSortAndSync=False,
                       **kwargs):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyGCZ.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyGCZ.py
new file mode 100644
index 0000000..0b61fe1
--- /dev/null
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossFamilyGCZ.py
@@ -0,0 +1,2902 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package CrossFamilyGCZ
+#
+# @todo:仙盟攻城战
+# @author hxp
+# @date 2025-04-09
+# @version 1.0
+#
+# 详细描述: 仙盟攻城战
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-04-09 16:00"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import ShareDefine
+import PlayerFamily
+import IpyGameDataPY
+import CrossRealmMsg
+import CrossBillboard
+import PlayerViewCache
+import CrossActionControl
+import PlayerCompensation
+import ChPyNetSendPack
+import NetPackCommon
+import PyDataManager
+import PlayerControl
+import PyGameData
+import ChConfig
+import ChPlayer
+
+import random
+import copy
+import time
+
+
+## 1-公示期;99-领奖期;轮次状态=轮次*10+轮次阶段;轮次阶段:1-分组战备;2-战斗;3-休战结算
+FamilyGCZState_Publicity = 1 # 公示期
+FamilyGCZState_Award = 99 # 结束领奖期
+
+# 轮次状态信息
+FamilyGCZRoundState_Group = 1 # 分组+战备
+FamilyGCZRoundState_Fight = 2 # 战斗
+FamilyGCZRoundState_Over = 3 # 休战结算
+
+# 战场类型
+BatTypeList = (
+BatType_Junior, # 初级 1
+BatType_Middle, # 中级 2
+BatType_High, # 高级 3
+) = range(1, 1 + 3)
+
+# 攻击类型
+(
+AtkType_Normal, # 普通单攻 1
+AtkType_SkillSingle, # 技能单攻 2
+AtkType_SkillArea, # 技能群攻 3
+) = range(1, 1 + 3)
+
+# 攻击结果
+(
+AtkRet_OK, # 成功 0
+AtkRet_TagDead, # 目标已被击杀 1
+AtkRet_TagBroken, # 城池已被摧毁 3
+AtkRet_Other, # 其他 3
+) = range(4)
+
+# 修罗城池ID,每个分组都允许有各自的修罗城,ID可以一样
+XiuluoCityID = 2000000000
+
+# 奖励类型
+(
+AwwardType_Guess, # 竞猜
+AwwardType_PlayerHurt, # 个人伤害排行
+AwwardType_FamilyScore, # 仙盟积分排行
+) = range(3)
+
+
+class FamilyGCZFighter(object):
+    ## 城池战斗防守人员 - 玩家、守卫战斗类
+    
+    def __init__(self):    
+        self.fighterID = 0
+        self.hp = 0 # 当前血量
+        self.hpMax = 0 # 最大血量
+        self.hpBase = 0 # 基础血量,不算加成
+        self.fightPower = 0 # 战斗力
+        return
+    
+    def IsDead(self): return self.hp <= 0
+    
+    def RefreshFighterAttr(self, hpFightPowerMulti, addHPPer):
+        self.hpBase = self.fightPower * hpFightPowerMulti
+        self.hpMax = int(self.hpBase * (100 + addHPPer) / 100.0)
+        self.hp = self.hpMax # 只要刷属性就回满血,战斗阶段会锁住不允许刷属性
+        return
+    
+class FamilyGCZCityWall():
+    ## 城池
+    
+    def __init__(self, zoneID=0, cityID=0, cityLV=0, familyID=0):
+        self.zoneID = zoneID
+        self.batType = 0
+        self.groupNum = 0
+        self.cityID = cityID # 城池ID
+        self.cityLV = cityLV # 城池等级,一般为锁定时的大本营等级,或指定的等级,战斗属性加成由该等级决定
+        self.familyID = familyID # 所属仙盟ID,可能无归属,如修罗城
+        self.guardMemIDList = [] # 防守战斗人员ID列表,被击杀不变
+        self.fighterIDList = [] # 防守战斗人员ID列表,按成员战力升序排,NPC守卫 + 成员,被击杀移除
+        
+        self.guardNPCCount = 0 # 守卫NPC数,这里指的是非玩家的系统守卫
+        self.guardNPC = FamilyGCZFighter() # 当前NPC守卫,目前仅修罗成有用
+                
+        self.attrLock = 0 # 是否锁定属性
+        self.hp = 0 # 当前血量,所有防守战斗人员hp 之和
+        self.hpMax = 0 # 最大血量,所有防守战斗人员hp hpMax 之和
+        self.hpBase = 0 # 基础血量,不算加成,所有防守战斗人员hp hpBase 之和
+        self.fightPowerTotal = 0 # 总战力
+        
+        self.atkedTime = 0 # 最后一次被攻击时间戳
+        self.atkReportDict = {} # 攻击战报 {tagFamilyID:totalHurt, ...}
+        self.defReportDict = {} # 防守战报 {tagFamilyID:totalHurt, ...}
+        self.fighterHurtDict = {} # 战斗成员贡献伤害,被击毁后不会再累加 {playerID:hurtValue, ...}
+        self.inCityPlayerIDList = [] # 在战斗城池里的玩家ID列表
+        return
+    
+    def ResetDefense(self, cityLV, guardMemIDList=None, guardNPCCount=0):
+        ## 城池重置防御
+        self.attrLock = 0
+        self.cityLV = cityLV
+        self.guardMemIDList = []
+        self.guardNPCCount = guardNPCCount
+        if self.guardNPCCount:
+            self.guardMemIDList += range(1, 1 + self.guardNPCCount)
+        if guardMemIDList:
+            self.guardMemIDList += guardMemIDList
+        self.atkedTime = 0
+        self.atkReportDict = {}
+        self.defReportDict = {}
+        self.fighterHurtDict = {}
+        self.inCityPlayerIDList = []
+        self.RefreshCityAttr()
+        return
+    
+    def OnFightStart(self):
+        ## 战斗开始 - 锁定
+        self.RefreshCityAttr()
+        self.attrLock = 1 # 刷完属性后锁住
+        return
+    
+    def IsBroken(self):
+        ## 是否被击毁
+        if self.hp <= 0 or not self.fighterIDList:
+            return True
+        return False
+    
+    def GetCurGuardID(self):
+        ## 当前守卫ID
+        if not self.fighterIDList:
+            return 0
+        return self.fighterIDList[0]
+    
+    def NextGuard(self):
+        ## 换下一个守卫
+        if not self.fighterIDList:
+            return 0
+        self.fighterIDList.pop(0)
+        if not self.fighterIDList:
+            return 0
+        nextGuardID = self.fighterIDList
+        if self.IsGuardNPC(nextGuardID):
+            self.guardNPC.hp = self.guardNPC.hpMax
+            GameWorld.DebugLog("    切换下一个NPC守卫: %s, %s/%s" % (nextGuardID, self.guardNPC.hp, self.guardNPC.hpMax))
+        else:
+            GameWorld.DebugLog("    切换下一个防守人员: %s" % nextGuardID)
+        return nextGuardID
+    
+    def IsGuardNPC(self, fighterID):
+        ## 是否系统NPC守卫,加上系统NPC数验证,兼容内网假仙盟成员测试
+        return self.guardNPCCount > 0 and fighterID < 10000
+    
+    def RefreshCityAttr(self, cityLV=None):
+        # @return: 是否成功刷新属性,战斗锁定时无法刷属性
+        if self.attrLock:
+            GameWorld.ErrLog("仙盟攻城战已经锁定战斗属性不能刷新! cityID=%s,familyID=%s" % (self.cityID, self.familyID))
+            return
+        if cityLV != None:
+            self.cityLV = cityLV
+        cityID = self.cityID
+        cityLV = self.cityLV
+        addHPPer = 0
+        campIpyData = IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", cityLV)
+        if campIpyData:
+            addHPPer = campIpyData.GetAddHPPer()
+            
+        hpFightPowerMulti = IpyGameDataPY.GetFuncCfg("FamilyGCZCity", 1) # 成员生命是战力的x倍
+        GameWorld.DebugLog("仙盟攻城战刷新城池属性: cityID=%s,cityLV=%s,addHPPer=%s,hpFightPowerMulti=%s" % (cityID, cityLV, addHPPer, hpFightPowerMulti))
+                
+        zone = GetFamilyGCZMgr().GetActZone(self.zoneID)
+        
+        self.hpBase = 0
+        self.hpMax = 0
+        self.fightPowerTotal = 0
+        sortFighterIDList = []
+        for fighterID in self.guardMemIDList:
+            if self.IsGuardNPC(fighterID):
+                fighter = self.guardNPC
+                fighter.fightPower = IpyGameDataPY.GetFuncCfg("FamilyGCZXiuluo", 1)
+            else:
+                fighter = zone.GetZoneJoinMember(fighterID)
+                
+            fighter.RefreshFighterAttr(hpFightPowerMulti, addHPPer)
+            
+            self.hpBase += fighter.hpBase
+            self.hpMax += fighter.hpMax
+            self.fightPowerTotal += fighter.fightPower
+            
+            sortFighterIDList.append([fighter.fightPower, fighterID])
+            GameWorld.DebugLog("    成员属性: cityID=%s,fighterID=%s,fightPower=%s,hpBase=%s,hpMax=%s,cityHPMax=%s" 
+                               % (cityID, fighterID, fighter.fightPower, fighter.hpBase, fighter.hpMax, self.hpMax))
+            
+        sortFighterIDList.sort() # 按战力升序
+        self.fighterIDList = [f[1] for f in sortFighterIDList]
+        
+        self.hp = self.hpMax # 只要刷属性就回满血,战斗阶段会锁住不允许刷属性
+        
+        GameWorld.DebugLog("    城池属性: cityID=%s,cityLV=%s,fightPowerTotal=%s,hpBase=%s,hpMax=%s" % (cityID, cityLV, self.fightPowerTotal, self.hpBase, self.hpMax))
+        GameWorld.DebugLog("        防守人员战力ID排序=%s" % sortFighterIDList)
+        GameWorld.DebugLog("        防守人员ID排序顺序=%s" % self.fighterIDList)
+        return True
+    
+    def SetPlayerInCity(self, playerID, serverGroupID):
+        if playerID in self.inCityPlayerIDList:
+            return
+        self.inCityPlayerIDList.append(playerID)
+        Sync_FamilyGCZBatCityInfo(self.zoneID, self.batType, self.groupNum, self.cityID, toPlayerServerDict={playerID:serverGroupID})
+        return
+    
+    def SetPlayerOutCity(self, playerID):
+        if playerID in self.inCityPlayerIDList:
+            self.inCityPlayerIDList.remove(playerID)
+        return
+    
+class FamilyGCZMember(FamilyGCZFighter):
+    ## 参与的成员
+    
+    def __init__(self, zoneID=0, familyID=0, playerID=0):
+        super(FamilyGCZMember, self).__init__()
+        self.zoneID = zoneID
+        self.batType = 0
+        self.groupNum = 0
+        self.playerID = playerID
+        self.familyID = familyID # 参与时的仙盟ID
+        self.fighterID = playerID
+        self.fmLV = 0 # 参与时的仙盟职位等级
+        self.playerName = ""
+        self.job = 0
+        self.realmLV = 0
+        self.face = 0
+        self.facePic = 0
+        self.lv = 0
+        self.totalHurtValue = 0 # 活动总伤害,可能无法上榜,记录用
+        self.awardState = 0 # 活动发奖同步记录,与子服的玩家个人记录对应,该记录仅用于补发奖励用
+        return
+    
+class FamilyGCZFamily():
+    ## 参与的仙盟
+    
+    def __init__(self, zoneID=0, familyID=0):
+        self.zoneID = zoneID
+        self.familyID = familyID
+        self.batType = 0
+        self.groupNum = 0
+        self.campLV = 1 # 大本营等级
+        self.campExp = 0
+        self.score = 0 # 活动总积分
+        
+        self.familyName = ""
+        self.lv = 0
+        self.leaderID = 0
+        self.leaderName = ""
+        self.emblemID = 0
+        self.serverID = 0
+        self.fightPowerTotal = 0
+        
+        self.joinMemberIDList = [] # 参与的成员ID列表 [playerID, ...]
+        self.memAddCampExpInfo = {} # 成员贡献大本营经验信息 {playerID:addExp, ...}
+        self.cityWall = FamilyGCZCityWall(zoneID, familyID, self.campLV, familyID) # 每个盟自带一个城池
+        return
+    
+    def OnRoundFamilyReset(self):
+        ## 轮次重置处理
+        self.batType = 0
+        self.groupNum = 0
+        self.cityWall.ResetDefense(self.campLV, self.joinMemberIDList)
+        self.fightPowerTotal = self.cityWall.fightPowerTotal
+        Sync_FamilyGCZCampInfo(self.zoneID, self.familyID)
+        return
+    
+    def OnRoundFamilyFightStart(self):
+        ## 轮次开战
+        self.cityWall.OnFightStart()
+        self.fightPowerTotal = self.cityWall.fightPowerTotal
+        Sync_FamilyGCZCampInfo(self.zoneID, self.familyID)
+        return
+    
+    def RefreshFamilyAttr(self, isNotify=True):
+        ## 刷新仙盟战斗属性
+        isOK = self.cityWall.RefreshCityAttr(self.campLV)
+        self.fightPowerTotal = self.cityWall.fightPowerTotal
+        if isNotify:
+            Sync_FamilyGCZCampInfo(self.zoneID, self.familyID)
+        return isOK
+    
+    def AddCampExp(self, playerID, addCampExp, isNotify=True):
+        ## 大本营加经验
+        # @return: 是否升级
+        if playerID not in self.joinMemberIDList:
+            # 非本盟锁定成员不能加经验
+            GameWorld.ErrLog("非仙盟攻城战锁定成员无法加大本营经验! familyID=%s,playerID=%s not in %s" % (self.familyID, playerID, self.joinMemberIDList))
+            return
+        
+        campLV = self.campLV
+        campExp = self.campExp
+        updCampExp = min(campExp + addCampExp, ChConfig.Def_UpperLimit_DWord)
+        updCampLV = campLV
+        
+        # 满级了也不限制捐献,让玩家可以继续消耗多余的道具
+        campIpyData = IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", campLV)
+        if campIpyData and campIpyData.GetLVUPNeedExp():
+            lvupNeedExp = campIpyData.GetLVUPNeedExp()
+            if updCampExp >= lvupNeedExp and IpyGameDataPY.GetIpyGameDataNotLog("CrossActFamilyGCZCampLV", campLV + 1):
+                updCampExp -= lvupNeedExp
+                updCampLV = campLV + 1
+        
+        self.campLV = updCampLV
+        self.campExp = updCampExp
+        self.memAddCampExpInfo[playerID] = self.memAddCampExpInfo.get(playerID, 0) + addCampExp
+        
+        GameWorld.DebugLog("增加大本营经验: familyID=%s,playerID=%s,addCampExp=%s,campLV-Exp=%s-%s,updLV-Exp=%s-%s,playerAddExpTotal=%s" 
+                           % (self.familyID, playerID, addCampExp, campLV, campExp, updCampLV, updCampExp, self.memAddCampExpInfo[playerID]))
+        
+        # 升级了
+        defMemIDList = [] # 加经验默认不通知防守成员属性
+        isLVUP = False
+        if campLV != updCampLV:
+            isLVUP = True
+            GameWorld.DebugLog("大本营升级了! familyID=%s,campLV=%s to %s" % (self.familyID, campLV, updCampLV))
+            if self.RefreshFamilyAttr(False):
+                defMemIDList = None # 升级后成功刷成员属性后,额外附带所有防守成员信息,战斗阶段属性是锁定的
+                
+        if isNotify:
+            Sync_FamilyGCZCampInfo(self.zoneID, self.familyID, defMemIDList=defMemIDList)
+            
+        return isLVUP
+    
+    def GetOLMemServerGroupIDDict(self):
+        ## 获取本盟在线参与成员信息
+        olDict = {}
+        onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+        for playerID in self.joinMemberIDList:
+            serverGroupID = onlineMgr.GetOLPlayerServerGroupID(playerID)
+            if serverGroupID:
+                olDict[playerID] = serverGroupID
+        return olDict
+    
+class FamilyGCZBatGroup():
+    ## 战斗分组
+    
+    def __init__(self, zoneID, batType, groupNum):
+        self.zoneID = zoneID
+        self.batType = batType
+        self.groupNum = groupNum
+        
+        # 城池并不一定归属于某个仙盟所以由分组自行管理
+        self.cityWallDict = {} # 城池对象信息 {cityID:FamilyGCZCityWall, ...}
+        self.__cityIDList = [] # 城池ID列表,按添加顺序的列表
+        self.__topHurtPlayerID = 0 # 本组伤害第一玩家ID
+        self.__topHurtPlayerValue = 0 # 本组伤害第一玩家伤害
+        self.inBatScenePlayerIDList = [] # 在战斗场景中的玩家ID列表 [playerID, ...]
+        return
+    
+    def GetTopHurtPlayerIDValue(self):
+        ## 获取本组有效伤害第一玩家,仅算城池未被摧毁前的累计伤害
+        if self.__topHurtPlayerID and self.__topHurtPlayerValue:
+            return self.__topHurtPlayerID, self.__topHurtPlayerValue
+        
+        for cityID in self.cityWallDict.keys():
+            cityWall = self.GetCityWall(cityID)
+            if not cityWall:
+                continue
+            for playerID, hurtTotal in cityWall.fighterHurtDict.items():
+                if hurtTotal > self.__topHurtPlayerValue:
+                    self.__topHurtPlayerID = playerID
+                    
+        return self.__topHurtPlayerID, self.__topHurtPlayerValue
+    def UpdTopHurtPlayerIDValue(self, topPlayerID, topHurtValue):
+        self.__topHurtPlayerID, self.__topHurtPlayerValue = topPlayerID, topHurtValue
+        return
+    
+    def GetCityIDList(self):
+        if not self.__cityIDList:
+            self.__cityIDList = self.cityWallDict.keys()
+        return self.__cityIDList
+    
+    def GetCityWall(self, cityID):
+        ## 获取某个城池,可能为None
+        cityWall = None
+        if cityID in self.cityWallDict:
+            cityWall = self.cityWallDict[cityID]
+        elif False: # 不可能执行,提示代码用
+            cityWall = FamilyGCZCityWall()
+        return cityWall
+    
+    def GetGroupFamilyIDList(self):
+        familyIDList = []
+        for cityID in self.GetCityIDList():
+            city = self.GetCityWall(cityID)
+            if not city:
+                continue
+            familyID = city.familyID
+            if familyID not in familyIDList:
+                familyIDList.append(familyID)
+        return familyIDList
+    
+    def AddGroupFamily(self, familyID):
+        ## 将仙盟分配到该分组        
+        zone = GetFamilyGCZMgr().GetActZone(self.zoneID)
+        joinFamily = zone.GetZoneJoinFamily(familyID)
+        if not joinFamily:
+            return
+        joinFamily.batType = self.batType
+        joinFamily.groupNum = self.groupNum
+        for playerID in joinFamily.joinMemberIDList:
+            joinMember = zone.GetZoneJoinMember(playerID)
+            if not joinMember:
+                continue
+            joinMember.batType = self.batType
+            joinMember.groupNum = self.groupNum
+            
+        joinFamily.cityWall.batType = self.batType
+        joinFamily.cityWall.groupNum = self.groupNum
+        cityID = joinFamily.cityWall.cityID
+        self.cityWallDict[cityID] = joinFamily.cityWall
+        if cityID not in self.__cityIDList:
+            self.__cityIDList.append(cityID)
+        return joinFamily
+    
+    def CreateCityWall(self, cityID, cityLV, guardMemIDList=None, guardNPCCount=0, isAttrLock=False):
+        '''创建自定义城池,如修罗城
+        @param cityID: 自定义城池ID
+        @param cityLV: 城池等级
+        @param guardMemIDList: 布防玩家ID列表
+        @param guardNPCCount: 布防系统NPC数量
+                        城池总属性: (布防的玩家 + 布防系统NPC)属性总和
+        '''
+        guardNPCCount = min(guardNPCCount, 100)
+        newCity = FamilyGCZCityWall(self.zoneID, cityID, cityLV)
+        newCity.batType = self.batType
+        newCity.groupNum = self.groupNum
+        newCity.ResetDefense(cityLV, guardNPCCount=guardNPCCount)
+        if isAttrLock:
+            newCity.attrLock = 1
+        self.cityWallDict[cityID] = newCity
+        if cityID not in self.__cityIDList:
+            self.__cityIDList.append(cityID)
+        return newCity
+    
+    def SetPlayerInScene(self, playerID, serverGroupID):
+        if playerID in self.inBatScenePlayerIDList:
+            return
+        self.inBatScenePlayerIDList.append(playerID)
+        Sync_FamilyGCZBatSceneInfo(self.zoneID, self.batType, self.groupNum, toPlayerServerDict={playerID:serverGroupID})
+        return
+    
+    def SetPlayerOutScene(self, playerID):
+        if playerID in self.inBatScenePlayerIDList:
+            self.inBatScenePlayerIDList.remove(playerID)
+        return
+    
+class FamilyGCZZone():
+    ## 攻城战分区
+    
+    def __init__(self, zoneID):
+        self.zoneID = zoneID
+        self.joinFamilyCnt = 0
+        self.lockFamilyIDList = [] # 锁定的仙盟ID列表
+        self.joinFamilyDict = {} # 参与仙盟对象信息 {familyID:FamilyGCZFamily, ...}
+        self.joinMemberDict = {} # 参与玩家对象信息 {playerID:FamilyGCZMember, ...}
+        self.roundGroupDict = {} # 当前轮次分组信息 {batType:{groupNum:FamilyGCZBatGroup, ...}, ...}
+        self.familyGuessDict = {} # 仙盟竞猜热度信息 {familyID:value, ...}
+        self.playerGuessDict = {} # 玩家竞猜记录 {playerID:[familyID, ...], ...}
+        
+        self.statGuessRetDict = None # 统计竞猜结果 {awardID:[猜中玩家ID, ...], ...}
+        self.guessFinalFamilyIDList = [] # 竞猜最终排名仙盟ID列表
+        self.inBatScenePlayerIDDict = {} # 在战斗场景中的玩家ID {(batType, groupNum):[playerID, ...], ...}
+        self.inBatCityPlayerIDDict = {} # 在战斗城池中的玩家ID  {(batType, groupNum, cityID):[playerID, ...], ...}
+        return
+    
+    def OnRoundReset(self):
+        ## 分区轮次重置
+        self.roundGroupDict = {}
+        self.inBatScenePlayerIDDict = {}
+        self.inBatCityPlayerIDDict = {}
+        for familyID in self.lockFamilyIDList:
+            joinFamily = self.GetZoneJoinFamily(familyID)
+            if not joinFamily:
+                continue
+            joinFamily.OnRoundFamilyReset()
+        return
+    
+    def OnRoundFightStart(self):
+        ## 分区轮次开始
+        for familyID in self.lockFamilyIDList:
+            joinFamily = self.GetZoneJoinFamily(familyID)
+            if not joinFamily:
+                continue
+            joinFamily.OnRoundFamilyFightStart()
+        return
+    
+    def AddBatGroup(self, batType, groupNum):
+        if batType not in self.roundGroupDict:
+            self.roundGroupDict[batType] = {}
+        groupNumDict = self.roundGroupDict[batType]
+        batGroup = FamilyGCZBatGroup(self.zoneID, batType, groupNum)
+        groupNumDict[groupNum] = batGroup
+        return batGroup
+    
+    def GetBatGroup(self, batType, groupNum):
+        ## 获取某个分组,可能为None
+        if batType not in self.roundGroupDict:
+            self.roundGroupDict[batType] = {}
+        groupNumDict = self.roundGroupDict[batType]
+        batGroup = groupNumDict.get(groupNum, None)
+        if batGroup == None and False: # 不可能执行,提示代码用
+            batGroup = FamilyGCZBatGroup()
+        return batGroup
+    
+    ## 获取本分区参与的仙盟ID列表,按锁定顺序的
+    def GetZoneLockFamilyIDList(self): return self.lockFamilyIDList
+    def GetZoneJoinFamily(self, familyID):
+        ## 获取本分区参与的仙盟,可能为None
+        joinFamily = None
+        if familyID in self.joinFamilyDict:
+            joinFamily = self.joinFamilyDict[familyID]
+        elif False: # 不可能执行,提示代码用
+            joinFamily = FamilyGCZFamily()
+        return joinFamily
+    
+    def AddZoneJoinFamily(self, familyID):
+        ## 添加参与仙盟
+        joinFamily = None
+        if familyID in self.joinFamilyDict:
+            joinFamily = self.joinFamilyDict[familyID]
+        else:
+            joinFamily = FamilyGCZFamily(self.zoneID, familyID)
+            self.joinFamilyDict[familyID] = joinFamily
+        if familyID not in self.lockFamilyIDList:
+            self.lockFamilyIDList.append(familyID)
+        GetFamilyGCZMgr().SetFamilyIDZoneID(familyID, self.zoneID)
+        return joinFamily
+    
+    def GetZoneJoinMember(self, playerID):
+        ## 获取本分区参与的玩家,可能为None
+        joinMember = None
+        if playerID in self.joinMemberDict:
+            joinMember = self.joinMemberDict[playerID]
+        elif False: # 不可能执行,提示代码用
+            joinMember = FamilyGCZMember()
+        return joinMember
+    
+    def AddZoneJoinMember(self, playerID, familyID):
+        ## 添加参与成员
+        joinMember = None
+        if playerID in self.joinMemberDict:
+            joinMember = self.joinMemberDict[playerID]
+        else:
+            joinMember = FamilyGCZMember(self.zoneID, familyID, playerID)
+            self.joinMemberDict[playerID] = joinMember
+        GetFamilyGCZMgr().SetPlayerIDZoneID(playerID, self.zoneID)
+        return joinMember
+    
+    def GetOLServerGroupIDDict(self):
+        ## 获取本分区在线参与成员信息
+        olDict = {}
+        for joinFamily in self.joinFamilyDict.values():
+            olDict.update(joinFamily.GetOLMemServerGroupIDDict())
+        return olDict
+    
+    def SetPlayerInBatScene(self, playerID, batType=0, groupNum=0, serverGroupID=0):
+        ## 设置玩家在某个战斗场景,参数默认0为退出
+        for bType, groupDict in self.roundGroupDict.items():
+            for gNum in groupDict.keys():
+                batGroup = self.GetBatGroup(bType, gNum)
+                if not batGroup:
+                    continue
+                if batType != bType or groupNum != gNum:
+                    batGroup.SetPlayerOutScene(playerID)
+                else:
+                    batGroup.SetPlayerInScene(playerID, serverGroupID)
+        return
+    
+    def SetPlayerInBatCity(self, playerID, batType=0, groupNum=0, cityID=0, serverGroupID=0):
+        ## 设置玩家在某个战斗城池,参数默认0为退出
+        for bType, groupDict in self.roundGroupDict.items():
+            for gNum in groupDict.keys():
+                batGroup = self.GetBatGroup(bType, gNum)
+                if not batGroup:
+                    continue
+                for cID in batGroup.GetCityIDList():
+                    cityWall = batGroup.GetCityWall(cID)
+                    if not cityWall:
+                        continue
+                    if batType != bType or groupNum != gNum or cityID != cID:
+                        cityWall.SetPlayerOutCity(playerID)
+                    else:
+                        cityWall.SetPlayerInCity(playerID, serverGroupID)
+        return
+
+class FamilyGCZMgr():
+    ## 仙盟攻城战管理
+    
+    def __init__(self):
+        self.zoneDict = {} # 分区信息 {zone:FamilyGCZZone, ...}
+        self.familyIDZoneIDDict = {} # 仙盟ID所在分区 {familyID:zoneID, ...} 
+        self.playerIDZoneIDDict = {} # 玩家ID所在分区 {playerID:zoneID, ...} 
+        return
+    
+    def SetFamilyIDZoneID(self, familyID, zoneID):
+        self.familyIDZoneIDDict[familyID] = zoneID
+        return
+    
+    def SetPlayerIDZoneID(self, playerID, zoneID):
+        self.playerIDZoneIDDict[playerID] = zoneID
+        return
+    
+    def ClearGCZZone(self, zoneID):
+        self.zoneDict.pop(zoneID, None)
+        for familyID, fZoneID in self.familyIDZoneIDDict.items():
+            if fZoneID == zoneID:
+                self.familyIDZoneIDDict.pop(familyID)
+        for playerID, pZoneID in self.playerIDZoneIDDict.items():
+            if pZoneID == zoneID:
+                self.playerIDZoneIDDict.pop(playerID)
+        return
+    
+    def GetActZone(self, zoneID):
+        ## 获取分区管理
+        zone = None
+        if zoneID in self.zoneDict:
+            zone = self.zoneDict[zoneID]
+        else:
+            zone = FamilyGCZZone(zoneID)
+            self.zoneDict[zoneID] = zone
+        return zone
+    
+    def GetJoinFamily(self, familyID):
+        ## 直接获取某个参与仙盟信息
+        joinFamily = None
+        if familyID in self.familyIDZoneIDDict:
+            zoneID = self.familyIDZoneIDDict[familyID]
+            zone = self.GetActZone(zoneID)
+            joinFamily = zone.GetZoneJoinFamily(familyID)
+        elif False: # 不可能执行,提示代码用
+            joinFamily = FamilyGCZFamily()
+        return joinFamily
+    
+    def GetJoinFamilyByPlayerID(self, playerID):
+        ## 直接获取某个参与仙盟信息 - 根据成员玩家ID
+        joinFamily = None
+        if playerID in self.playerIDZoneIDDict:
+            zoneID = self.playerIDZoneIDDict[playerID]
+            zone = self.GetActZone(zoneID)
+            joinMember = zone.GetZoneJoinMember(playerID)
+            if joinMember:
+                joinFamily = zone.GetZoneJoinFamily(joinMember.familyID)
+        elif False: # 不可能执行,提示代码用
+            joinFamily = FamilyGCZFamily()
+        return joinFamily
+    
+    def GetJoinMember(self, playerID):
+        ## 直接获取某个参与玩家信息
+        joinMember = None
+        if playerID in self.playerIDZoneIDDict:
+            zoneID = self.playerIDZoneIDDict[playerID]
+            zone = self.GetActZone(zoneID)
+            joinMember = zone.GetZoneJoinMember(playerID)
+        elif False: # 不可能执行,提示代码用
+            joinMember = FamilyGCZMember()
+        return joinMember
+    
+def GetFamilyGCZMgr():
+    mgr = PyGameData.g_familyGCZMgr
+    if not mgr:
+        mgr = FamilyGCZMgr()
+        PyGameData.g_familyGCZMgr = mgr
+    return mgr
+
+def OnLoadPyData():
+    
+    if not GameWorld.IsCrossServer():
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+    
+    recDataDict = gameRecMgr.GetGameRecDataDict(ShareDefine.Def_GameRecType_FamilyGCZMgr)
+    for zoneID, zoneRecDataList in recDataDict.items():
+        if not zoneRecDataList:
+            continue
+        zoneRecData = zoneRecDataList[0] # 分区管理,每个分区只要一条记录
+        joinFamilyCnt = zoneRecData.GetValue1()
+        zoneDataDict = zoneRecData.GetUserDataDict()
+        
+        gczMgr.ClearGCZZone(zoneID)
+        
+        zone = gczMgr.GetActZone(zoneID)
+        zone.joinFamilyCnt = joinFamilyCnt
+        zone.lockFamilyIDList = zoneDataDict.get("lockFamilyIDList", [])
+        zone.familyGuessDict = zoneDataDict.get("familyGuessDict", {})
+        zone.playerGuessDict = zoneDataDict.get("playerGuessDict", {})
+        GameWorld.Log("加载分区记录! zoneID=%s,joinFamilyCnt=%s,lockCnt=%s,%s" % (zoneID, zone.joinFamilyCnt, len(zone.lockFamilyIDList), zone.lockFamilyIDList))
+        
+        familyRecList = gameRecMgr.GetGameRecDataList(ShareDefine.Def_GameRecType_FamilyGCZJoinFamily, zoneID)
+        for fNum, familyRecData in enumerate(familyRecList, 1):
+            familyID = familyRecData.GetValue1()
+            batType = familyRecData.GetValue2()
+            groupNum = familyRecData.GetValue3()
+            campLV = familyRecData.GetValue4()
+            campExp = familyRecData.GetValue5()
+            score = familyRecData.GetValue6()
+            familyDataDict = familyRecData.GetUserDataDict()
+            
+            joinFamily = zone.AddZoneJoinFamily(familyID)
+            joinFamily.batType = batType
+            joinFamily.groupNum = groupNum
+            joinFamily.campLV = campLV
+            joinFamily.campExp = campExp
+            joinFamily.score = score
+            joinFamily.familyName = familyDataDict.get("familyName", "")
+            joinFamily.leaderID = familyDataDict.get("leaderID", 0)
+            joinFamily.leaderName = familyDataDict.get("leaderName", "")
+            joinFamily.lv = familyDataDict.get("lv", 0)
+            joinFamily.emblemID = familyDataDict.get("emblemID", 0)
+            joinFamily.serverID = familyDataDict.get("serverID", 0)
+            joinFamily.fightPowerTotal = familyDataDict.get("fightPowerTotal", 0)
+            joinFamily.joinMemberIDList = familyDataDict.get("joinMemberIDList", [])
+            joinFamily.memAddCampExpInfo = familyDataDict.get("memAddCampExpInfo", {})
+            
+            GameWorld.Log("    加载分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,score=%s,,memAddCampExpInfo=%s" 
+                          % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.score, joinFamily.memAddCampExpInfo))
+            
+        memberRecList = gameRecMgr.GetGameRecDataList(ShareDefine.Def_GameRecType_FamilyGCZJoinMember, zoneID)
+        for pNum, memberRecData in enumerate(memberRecList, 1):
+            playerID = memberRecData.GetValue1()
+            familyID = memberRecData.GetValue2()
+            batType = memberRecData.GetValue3()
+            groupNum = memberRecData.GetValue4()
+            fmLV = memberRecData.GetValue5()
+            playerDataDict = memberRecData.GetUserDataDict()
+            
+            joinMember = zone.AddZoneJoinMember(playerID, familyID)
+            joinMember.batType = batType
+            joinMember.groupNum = groupNum
+            joinMember.fmLV = fmLV
+            joinMember.playerName = playerDataDict.get("playerName", "")
+            joinMember.job = playerDataDict.get("job", 1)
+            joinMember.realmLV = playerDataDict.get("realmLV", 1)
+            joinMember.face = playerDataDict.get("face", 0)
+            joinMember.facePic = playerDataDict.get("facePic", 0)
+            joinMember.lv = playerDataDict.get("lv", 1)
+            joinMember.totalHurtValue = playerDataDict.get("totalHurtValue", 0)
+            joinMember.awardState = playerDataDict.get("awardState", 0)
+            joinMember.hp = playerDataDict.get("hp", 0)
+            joinMember.hpMax = playerDataDict.get("hpMax", 0)
+            joinMember.hpBase = playerDataDict.get("hpBase", 0)
+            joinMember.fightPower = playerDataDict.get("fightPower", 0)
+            
+            GameWorld.Log("    加载分区成员 zoneID=%s,familyID=%s,%s,playerID=%s,hp=%s/%s,totalHurtValue=%s" 
+                      % (zoneID, familyID, pNum, playerID, joinMember.hp, joinMember.hpMax, joinMember.totalHurtValue))
+            
+        cityWallRecList = gameRecMgr.GetGameRecDataList(ShareDefine.Def_GameRecType_FamilyGCZCityWall, zoneID)
+        for cNum, cityRecData in enumerate(cityWallRecList, 1):
+            cityID = cityRecData.GetValue1()
+            batType = cityRecData.GetValue2()
+            groupNum = cityRecData.GetValue3()
+            cityLV = cityRecData.GetValue4()
+            familyID = cityRecData.GetValue5()
+            attrLock = cityRecData.GetValue6()
+            cityDataDict = cityRecData.GetUserDataDict()
+            batGroup = zone.GetBatGroup(batType, groupNum)
+            if not batGroup:
+                batGroup = zone.AddBatGroup(batType, groupNum)
+            if not batGroup:
+                continue
+            if familyID:
+                batGroup.AddGroupFamily(familyID)
+            else:
+                batGroup.CreateCityWall(cityID, cityLV)
+                
+            city = batGroup.GetCityWall(cityID)
+            if not city:
+                continue
+            
+            city.cityLV = cityLV
+            city.attrLock = attrLock
+            city.guardMemIDList = cityDataDict.get("guardMemIDList", [])
+            city.fighterIDList = cityDataDict.get("fighterIDList", [])
+            city.guardNPCCount = cityDataDict.get("guardNPCCount", 0)
+            city.hp = cityDataDict.get("hp", 0)
+            city.hpMax = cityDataDict.get("hpMax", 0)
+            city.hpBase = cityDataDict.get("hpBase", 0)
+            city.fightPowerTotal = cityDataDict.get("fightPowerTotal", 0)
+            city.atkReportDict = cityDataDict.get("atkReportDict", {})
+            city.fighterHurtDict = cityDataDict.get("fighterHurtDict", {})
+            
+            GameWorld.Log("    加载分区城池 zoneID=%s,batType=%s,batType=%s,%s,cityID=%s,familyID=%s,hp=%s/%s,fighterIDList=%s,%s" 
+                  % (zoneID, batType, groupNum, cNum, cityID, city.familyID, city.hp, city.hpMax, len(city.fighterIDList), city.fighterIDList))
+            
+    return
+
+def OnSavePyData():
+    if not GameWorld.IsCrossServer():
+        return
+    
+    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+    gameRecMgr.DelGameRecDataByType(ShareDefine.Def_GameRecType_FamilyGCZMgr)
+    gameRecMgr.DelGameRecDataByType(ShareDefine.Def_GameRecType_FamilyGCZJoinFamily)
+    gameRecMgr.DelGameRecDataByType(ShareDefine.Def_GameRecType_FamilyGCZJoinMember)
+    gameRecMgr.DelGameRecDataByType(ShareDefine.Def_GameRecType_FamilyGCZCityWall)
+    
+    gczMgr = GetFamilyGCZMgr()
+    zoneIDList = gczMgr.zoneDict.keys()
+    GameWorld.Log("保存仙盟攻城战分区记录! zoneIDList=%s" % zoneIDList)
+    for zoneID in zoneIDList:
+        if not zoneID:
+            continue
+        zone = gczMgr.GetActZone(zoneID)
+        zoneRecData = gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyGCZMgr, zoneID)
+        zoneRecData.SetValue1(zone.joinFamilyCnt)
+        lockFamilyIDList = zone.GetZoneLockFamilyIDList()
+        zoneData = {"lockFamilyIDList":lockFamilyIDList, "familyGuessDict":zone.familyGuessDict, "playerGuessDict":zone.playerGuessDict}
+        zoneRecData.SetUserData(zoneData)
+        
+        GameWorld.Log("----- zoneID=%s,joinFamilyCnt=%s,lockFamilyIDList=%s,%s" % (zoneID, zone.joinFamilyCnt, len(lockFamilyIDList), lockFamilyIDList))
+        
+        for batType, groupDict in zone.roundGroupDict.items():
+            for groupNum in groupDict.keys():
+                batGroup = zone.GetBatGroup(batType, groupNum)
+                if not batGroup:
+                    continue
+                cityIDList = batGroup.GetCityIDList()
+                for cNum, cityID in enumerate(cityIDList, 1):
+                    city = batGroup.GetCityWall(cityID)
+                    if not city:
+                        continue
+                    cityRecData = gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyGCZCityWall, zoneID)
+                    cityRecData.SetValue1(cityID)
+                    cityRecData.SetValue2(city.batType)
+                    cityRecData.SetValue3(city.groupNum)
+                    cityRecData.SetValue4(city.cityLV)
+                    cityRecData.SetValue5(city.familyID)
+                    cityRecData.SetValue6(city.attrLock)
+                    cityData = {"guardMemIDList":city.guardMemIDList, "fighterIDList":city.fighterIDList, "guardNPCCount":city.guardNPCCount,
+                                "hp":city.hp, "hpMax":city.hpMax, "hpBase":city.hpBase, "fightPowerTotal":city.fightPowerTotal,
+                                "atkReportDict":city.atkReportDict, "defReportDict":city.defReportDict, "fighterHurtDict":city.fighterHurtDict}
+                    cityRecData.SetUserData(cityData)
+                    GameWorld.Log("    保存分区城池 zoneID=%s,batType=%s,groupNum=%s,%s,cityID=%s,familyID=%s,attrLock=%s,hp=%s/%s,fighterIDList=%s,%s" 
+                                  % (zoneID, batType, groupNum, cNum, cityID, city.familyID, city.attrLock, city.hp, city.hpMax, len(city.fighterIDList), city.fighterIDList))
+                    
+        for fNum, familyID in enumerate(lockFamilyIDList, 1):
+            joinFamily = zone.GetZoneJoinFamily(familyID)
+            if not joinFamily:
+                continue
+            familyRecData = gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyGCZJoinFamily, zoneID)
+            familyRecData.SetValue1(familyID)
+            familyRecData.SetValue2(joinFamily.batType)
+            familyRecData.SetValue3(joinFamily.groupNum)
+            familyRecData.SetValue4(joinFamily.campLV)
+            familyRecData.SetValue5(joinFamily.campExp)
+            familyRecData.SetValue6(joinFamily.score)
+            
+            familyData = {"familyName":joinFamily.familyName, "leaderID":joinFamily.leaderID, "leaderName":joinFamily.leaderName,
+                          "lv":joinFamily.lv, "emblemID":joinFamily.emblemID, "serverID":joinFamily.serverID, "fightPowerTotal":joinFamily.fightPowerTotal,
+                          "joinMemberIDList":joinFamily.joinMemberIDList, "memAddCampExpInfo":joinFamily.memAddCampExpInfo
+                          }
+            familyRecData.SetUserData(familyData)
+            GameWorld.Log("    保存分区仙盟 zoneID=%s,%s,familyID=%s,campLV=%s-%s,score=%s," % (zoneID, fNum, familyID, joinFamily.campLV, joinFamily.campExp, joinFamily.score))
+            
+            for pNum, playerID in enumerate(joinFamily.joinMemberIDList, 1):
+                joinMember = zone.GetZoneJoinMember(playerID)
+                if not joinMember:
+                    continue
+                memberRecData = gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyGCZJoinMember, zoneID)
+                memberRecData.SetValue1(playerID)
+                memberRecData.SetValue2(familyID)
+                memberRecData.SetValue3(joinMember.batType)
+                memberRecData.SetValue4(joinMember.groupNum)
+                memberRecData.SetValue5(joinMember.fmLV)
+                
+                memberData = {"playerName":joinMember.playerName, "job":joinMember.job, "realmLV":joinMember.realmLV,
+                              "face":joinMember.face, "facePic":joinMember.facePic, "lv":joinMember.lv, "totalHurtValue":joinMember.totalHurtValue,
+                              "awardState":joinMember.awardState,
+                              "hp":joinMember.hp, "hpMax":joinMember.hpMax, "hpBase":joinMember.hpBase, "fightPower":joinMember.fightPower}
+                memberRecData.SetUserData(memberData)
+                GameWorld.Log("        保存分区成员 zoneID=%s,familyID=%s,%s,playerID=%s,hp=%s/%s,totalHurtValue=%s" 
+                              % (zoneID, familyID, pNum, playerID, joinMember.hp, joinMember.hpMax, joinMember.totalHurtValue))
+                
+    return
+
+def OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID):
+    ## 本服登录时跨服服务器需要处理的逻辑
+    
+    zoneID, familyID = GetPlayerActJoinInfo(playerID)
+    if not zoneID:
+        return
+    
+    Sync_FamilyGCZActInfo(zoneID, playerID)
+    Sync_FamilyGCZBatGroupInfo(zoneID, {playerID:serverGroupID})
+    Sync_FamilyGCZCampInfo(zoneID, familyID, {playerID:serverGroupID})
+    Sync_FamilyGCZGuessInfo(zoneID, playerID, playerID)
+    return
+
+def GetRoundState(state):
+    ## 获取轮次、状态信息
+    if state < 10 or state == FamilyGCZState_Award:
+        return 0, state
+    return state / 10, state % 10
+
+def OnCrossActIDChange(cfgID, zoneID, ipyData, state):
+    ## 跨服活动ID变更
+    DoMailReissueAward(zoneID, ipyData)
+    DelFamilyGCZZone(zoneID)
+    return
+
+def DelFamilyGCZZone(zoneID):
+    # 清空活动相关数据
+    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+    gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyGCZMgr, zoneID)
+    gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyGCZJoinFamily, zoneID)
+    gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyGCZJoinMember, zoneID)
+    gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyGCZCityWall, zoneID)
+    
+    # 清空活动相关榜单
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    billboardMgr.ClearBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
+    billboardMgr.ClearBillboard(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, zoneID)
+    billboardMgr.ClearBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, zoneID)
+    
+    GetFamilyGCZMgr().ClearGCZZone(zoneID)
+    return
+
+def OnCrossActStateChange(ipyData, actID, dbState, state):
+    ## 跨服活动状态变更
+    
+    zoneID = ipyData.GetZoneID()
+    if CrossActionControl.IsActFlowStateError(ShareDefine.CrossActName_FamilyGCZ, zoneID):
+        Sync_FamilyGCZActInfo(zoneID)
+        return
+    
+    if state == FamilyGCZState_Publicity:
+        Sync_FamilyGCZActInfo(zoneID)
+        return
+    
+    if state == FamilyGCZState_Award:
+        DoStartAward(zoneID)
+        return
+    
+    curRound, roundState = GetRoundState(state)
+    if curRound > 0:
+        # 分组
+        if roundState == FamilyGCZRoundState_Group:
+            if curRound == 1: # 第一轮分组前锁定名单
+                DoLockActFamily(actID, ipyData)
+            DoRoundGroup(curRound, zoneID, ipyData.GetJoinFamilyCnt())
+            
+        # 开始战斗
+        elif roundState == FamilyGCZRoundState_Fight:
+            gczMgr = GetFamilyGCZMgr()
+            zone = gczMgr.GetActZone(zoneID)
+            zone.OnRoundFightStart()
+            
+        # 结算
+        elif roundState == FamilyGCZRoundState_Over:
+            DoRoundOver(curRound, zoneID)
+            
+    else:
+        pass
+    
+    # 竞猜
+    DoGenerateGuess(zoneID, state)
+    return
+
+def GetPlayerActJoinInfo(playerID):
+    '''获取玩家参与的活动信息,如果返回的有分区ID及活动ID,代表一定是活动中的
+    必须是zoneID、actID都一直才能视为同一个活动,且actState非0
+    @return: [zoneID, familyID]
+    '''
+    
+    zoneID, familyID = 0, 0
+    defaultRet = (zoneID, familyID)
+    
+    gczMgr = GetFamilyGCZMgr()
+    joinMember = gczMgr.GetJoinMember(playerID)
+    # 有在参赛资格名单里
+    if joinMember:
+        zoneID, familyID = joinMember.zoneID, joinMember.familyID
+        return zoneID, familyID
+    
+    # 注: 公示期期间也是没有在资格里的,没有参与资格的,暂时都视为普通成员
+    # 没有在活动中参与资格里的,再按所在服ID判断资格:  当前所在盟服ID > 玩家所在服ID
+    # 
+    # 参与资格的相关跨服活动本身具有一定的数据延迟容错空间,逻辑统一由跨服服务器处理
+    # 故没有在锁定资格里的玩家可能存在以下情况
+    # 1. 没有跨服查看缓存,一般为未达到跨服条件的号或已流失7天的号,可视为不参与活动
+    # 2. 玩家缓存中的仙盟ID在跨服服务器还在,但子服已经解散了,一样还是用的缓存中的仙盟ID,因为活动本身具有一定的时间差容错空间,所以不影响
+    cacheDict = PlayerViewCache.GetCachePropDataDict(PlayerViewCache.FindViewCache(playerID))
+    if not cacheDict:
+        return defaultRet
+    
+    joinServerID = 0
+    familyIDNow = cacheDict.get("FamilyID", 0)
+    if familyIDNow:
+        curFamily = GameWorld.GetFamilyManager().FindFamily(familyIDNow)
+        if curFamily:
+            familyID = familyIDNow
+            joinServerID = curFamily.GetServerID()
+            
+    if not joinServerID:
+        joinServerID = GameWorld.GetAccIDServerID(cacheDict.get("AccID", ""))
+        
+    actInfo = CrossActionControl.GetCrossActInfoByServerGroupID(ShareDefine.CrossActName_FamilyGCZ, joinServerID)
+    if not actInfo or not actInfo.get(ShareDefine.ActKey_State):
+        # 非活动中直接返回
+        return defaultRet
+    ipyDataInfo = actInfo.get(ShareDefine.ActKey_IpyDataInfo)
+    if ipyDataInfo:
+        zoneID = ipyDataInfo.get("ZoneID", 0)
+    return zoneID, familyID
+
+def OnCrossJoinFamilyMemberUpd(curFamily, updMemIDList):
+    ## 本活动只更新参与活动成员战力,换仙盟的成员不更新了,以最后一次更新为准
+    if not curFamily:
+        return
+    
+    familyID = curFamily.GetID()
+    gczMgr = GetFamilyGCZMgr()
+    joinFamily = gczMgr.GetJoinFamily(familyID)
+    if not joinFamily:
+        # 非锁定的仙盟不更新,可能没有参与资格或未锁定
+        return
+    zoneID = joinFamily.zoneID
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        # 非活动中不更新
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    curRound, roundState = GetRoundState(state)
+    GameWorld.DebugLog("更新仙盟攻城战仙盟成员: familyID=%s,zoneID=%s,state=%s,curRound=%s,roundState=%s" 
+                       % (familyID, zoneID, state, curRound, roundState))
+    
+    zone = gczMgr.GetActZone(zoneID)
+    for playerID in updMemIDList:
+        joinMember = zone.GetZoneJoinMember(playerID)
+        if not joinMember:
+            continue
+        curMember = curFamily.FindMember(playerID)
+        if not curMember:
+            continue
+        joinMember.fightPower = max(joinMember.fightPower, PlayerFamily.GetMemberFightPower(curMember)) # 以较大战力为准
+        GameWorld.DebugLog("    playerID=%s,joinFamilyID=%s,updFightPower=%s" % (playerID, joinMember.familyID, joinMember.fightPower))
+        
+    return
+
+def DoLockActFamily(actID, ipyData):
+    ## 锁定参与仙盟名单
+    
+    zoneID = ipyData.GetZoneID()
+    serverIDList = ipyData.GetServerIDRangeList()
+    joinFamilyCnt = ipyData.GetJoinFamilyCnt()
+    GameWorld.Log("仙盟攻城战锁定参与仙盟名单: zoneID=%s,serverIDList=%s,joinFamilyCnt=%s" % (zoneID, serverIDList, joinFamilyCnt))
+    sortFamilyList = PlayerFamily.SortCrossFamily(serverIDList, joinFamilyCnt)[0]
+    
+    DelFamilyGCZZone(zoneID)
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    zone.joinFamilyCnt = joinFamilyCnt
+    
+    for num, family in enumerate(sortFamilyList, 1):
+        familyID = family.GetID()
+        serverID = family.GetServerID()
+        
+        joinFamily = zone.AddZoneJoinFamily(familyID)
+        familyBaseInfo = PlayerFamily.GetCrossFamilyBaseInfo(family)
+        joinFamily.familyName = familyBaseInfo.get("Name", "")
+        joinFamily.lv = familyBaseInfo.get("LV", 0)
+        joinFamily.leaderID = familyBaseInfo.get("LeaderID", 0)
+        joinFamily.leaderName = familyBaseInfo.get("LeaderName", "")
+        joinFamily.emblemID = familyBaseInfo.get("EmblemID", 0)
+        joinFamily.serverID = familyBaseInfo.get("ServerID", 0)
+        joinFamily.fightPowerTotal = familyBaseInfo.get("FightPower", 0)
+        joinFamily.campLV = 1
+        fightPowerTotal = joinFamily.fightPowerTotal
+        
+        for i in range(0, family.GetCount()):
+            member = family.GetAt(i)
+            playerID = member.GetPlayerID()
+            if not playerID:
+                continue
+            
+            memBaseInfo = PlayerFamily.GetCrossFamilyMemInfo(member)
+            joinMember = zone.AddZoneJoinMember(playerID, familyID)
+            joinMember.playerName = memBaseInfo.get("Name", "")
+            joinMember.lv = memBaseInfo.get("LV", 0)
+            joinMember.job = memBaseInfo.get("Job", 0)
+            joinMember.realmLV = memBaseInfo.get("OfficialRank", 0)
+            joinMember.face = memBaseInfo.get("Face", 0)
+            joinMember.facePic = memBaseInfo.get("FacePic", 0)
+            joinMember.fightPower = memBaseInfo.get("FightPower", 0)
+            joinMember.fmLV = memBaseInfo.get("FamilyLV", 0)
+            
+            joinFamily.joinMemberIDList.append(playerID)
+            
+        GameWorld.Log("    %s,familyID=%s,serverID=%s,fightPowerTotal=%s,memCnt=%s,%s" 
+                      % (num, familyID, serverID, fightPowerTotal, len(joinFamily.joinMemberIDList), joinFamily.joinMemberIDList))
+        
+    Sync_FamilyGCZActInfo(zoneID)
+    return
+
+def DoRoundGroup(curRound, zoneID, joinFamilyCnt):
+    ## 执行轮次分组
+    
+    GameWorld.Log("========== 仙盟攻城战轮次分组: zoneID=%s,curRound=%s,joinFamilyCnt=%s" % (zoneID, curRound, joinFamilyCnt))
+    # 参与仙盟数对应分组及晋级规则 {参与仙盟数:{轮次:{战场类型:[组数, 前x名晋级, x名开始降级], ...}, ...}, ...}
+    FamilyGCZGroupSet = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 1, {})
+    if joinFamilyCnt not in FamilyGCZGroupSet:
+        return
+    roundSetDict = FamilyGCZGroupSet[joinFamilyCnt]
+    if curRound not in roundSetDict:
+        return
+    batTypeDict = roundSetDict[curRound]
+    
+    groupValue1 = zoneID
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    
+    # 先处理上一轮晋级、保级、降级,需在重置前处理
+    nextBatTypeFamilyInfo = {} # 下一轮战场仙盟ID分配信息 {batType:[familyID, ...], ....}
+    preRound = curRound - 1
+    preBatTypeDict = roundSetDict.get(preRound, {})
+    for batType in BatTypeList: # 按战场类型顺序遍历处理
+        if batType not in zone.roundGroupDict:
+            continue
+        groupDict = zone.roundGroupDict[batType]
+        groupNumList = groupDict.keys()
+        groupNumList.sort()
+        preRoundSet = preBatTypeDict.get(batType, [0, 0])
+        upRank = preRoundSet[1] # 前x晋级
+        downRank = preRoundSet[2] if len(preRoundSet) > 2 else 0 # 大于等于x名降级,为0时不降级
+        GameWorld.Log("上轮战场类型分组: preRound=%s,batType=%s,upRank=%s,downRank=%s,%s" % (preRound, batType, upRank, downRank, groupNumList))
+        for groupNum in groupNumList:
+            groupValue2 = GetRoundHurtGroupID(batType, groupNum)
+            billboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1, groupValue2)
+            billboardObj.SortData()
+            upFamilyIDList, keepFamilyIDList, downFamilyIDList = [], [], []
+            for bIndex in range(billboardObj.GetCount()):
+                billData = billboardObj.At(bIndex)
+                familyID = billData.ID
+                rank = bIndex + 1
+                if rank <= upRank:
+                    nextBatType = min(BatType_High, batType + 1)
+                    upFamilyIDList.append(familyID)
+                elif downRank and rank >= downRank:
+                    nextBatType = max(BatType_Junior, batType - 1)
+                    downFamilyIDList.append(familyID)
+                # 除了升降级的就是保级
+                else:
+                    nextBatType = batType
+                    keepFamilyIDList.append(familyID)
+                    
+                if nextBatType not in nextBatTypeFamilyInfo:
+                    nextBatTypeFamilyInfo[nextBatType] = []
+                familyIDList = nextBatTypeFamilyInfo[nextBatType]
+                if familyID not in familyIDList:
+                    familyIDList.append(familyID)
+            GameWorld.Log("    升保降级zoneID=%s,batType=%s,groupNum=%s,up:%s,keep:%s,down:%s,%s" 
+                          % (zoneID, batType, groupNum, upFamilyIDList, keepFamilyIDList, downFamilyIDList, nextBatTypeFamilyInfo))
+            
+    #每轮重新分组时,重置该分区下的轮次伤害相关榜单
+    billboardMgr.ClearBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1)
+    zone.OnRoundReset()
+    
+    for batType in BatTypeList: # 按战场类型顺序遍历处理
+        if batType not in batTypeDict:
+            # 本轮不需要此战场类型分组
+            continue
+        groupCnt = batTypeDict[batType][0] # 本轮该战场类型分组
+        if not groupCnt:
+            GameWorld.Log("本轮战场不需要分组: zoneID=%s,curRound=%s,batType=%s" % (zoneID, curRound, batType))
+            continue
+        
+        batFamilyIDList = [] # 该战场的仙盟ID列表
+        if curRound == 1:
+            # 首轮分组默认取锁定名单
+            batFamilyIDList = zone.GetZoneLockFamilyIDList()
+            GameWorld.Log("首轮战场分组直接取分区锁定仙盟名单: zoneID=%s,curRound=%s,batType=%s,%s,%s" % (zoneID, curRound, batType, len(batFamilyIDList), batFamilyIDList))
+            
+        # 上一轮该战场类型有比赛的
+        elif batType in nextBatTypeFamilyInfo:
+            batFamilyIDList = nextBatTypeFamilyInfo[batType]
+            GameWorld.Log("非首轮分组直接取上轮晋保降仙盟名单: zoneID=%s,curRound=%s,batType=%s,%s,%s" % (zoneID, curRound, batType, len(batFamilyIDList), batFamilyIDList))
+            
+        else:
+            GameWorld.ErrLog("本轮分组可能没有仙盟名单: zoneID=%s,curRound=%s,batType=%s,%s,%s" % (zoneID, curRound, batType, len(batFamilyIDList), batFamilyIDList))
+            
+        # 生成空分组,确保即使没有仙盟也有对应分组
+        for groupNum in range(1, 1 + groupCnt):
+            zone.AddBatGroup(batType, groupNum)
+            
+        # 先按分组规则排序
+        groupFamilyDict = {}
+        groupFamilySortList = []
+        for familyID in batFamilyIDList:
+            joinFamily = zone.GetZoneJoinFamily(familyID)
+            if not joinFamily:
+                continue
+            groupFamilySortList.append(joinFamily)
+            groupFamilyDict[familyID] = [joinFamily.fightPowerTotal, joinFamily.score]
+            
+        # 先按总战力倒序,可扩展积分等
+        groupFamilySortList.sort(key=lambda f: (f.fightPowerTotal, f.score), reverse=True)
+        groupFamilyIDSortList = [f.familyID for f in groupFamilySortList]
+        
+        GameWorld.Log("开始分组按排序后的顺序循环插入每组! 仙盟数:%s, %s, %s" % (len(groupFamilyIDSortList), groupFamilyIDSortList, groupFamilyDict))
+        
+        groupNum = 0
+        for familyID in groupFamilyIDSortList:
+            groupNum += 1
+            if groupNum > groupCnt:
+                groupNum = 1
+            batGroup = zone.GetBatGroup(batType, groupNum)
+            if not batGroup:
+                GameWorld.ErrLog("仙盟分组时战斗分组不存在! zoneID=%s,batType=%s,groupNum=%s,familyID=%s" % (zoneID, batType, groupNum, familyID))
+                continue
+            joinFamily = batGroup.AddGroupFamily(familyID)
+            if not joinFamily:
+                GameWorld.ErrLog("仙盟分配到战斗分组失败! zoneID=%s,batType=%s,groupNum=%s,familyID=%s" % (zoneID, batType, groupNum, familyID))
+                continue
+            UndFamilyRoundHurtBillboard(joinFamily)
+            GameWorld.Log("    仙盟ID分组: zoneID=%s,curRound=%s,batType=%s,groupNum=%s,familyID=%s,%s" % (zoneID, curRound, batType, groupNum, familyID, groupFamilyDict[familyID]))
+            
+    Sync_FamilyGCZBatGroupInfo(zoneID)
+    return
+
+def UndFamilyRoundHurtBillboard(joinFamily):
+    ## 更新仙盟轮次伤害榜单
+    if not joinFamily:
+        return
+    dataID = joinFamily.familyID
+    name1 = joinFamily.familyName
+    id2 = joinFamily.leaderID
+    name2 = joinFamily.leaderName
+    type2, value1, value2 = 0, 0, 0
+    value3 = joinFamily.emblemID
+    value5 = joinFamily.serverID
+    groupValue1 = joinFamily.zoneID
+    groupValue2 = GetRoundHurtGroupID(joinFamily.batType, joinFamily.groupNum)
+    roundTotalHurt = sum(joinFamily.cityWall.fighterHurtDict.values())
+    cmpValue = roundTotalHurt / ChConfig.Def_PerPointValue
+    cmpValue2 = roundTotalHurt % ChConfig.Def_PerPointValue
+    CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, groupValue1, dataID, name1, name2, type2, value1, value2,
+                                     cmpValue, cmpValue2, groupValue2=groupValue2, id2=id2, autoSort=True, value3=value3, value5=value5)
+    return
+
+def DoRoundOver(curRound, zoneID):
+    ## 执行轮次结算
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    batTypeScoreInfo = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 2, {})
+    GameWorld.Log("========== 仙盟攻城战轮次结算: zoneID=%s,curRound=%s" % (zoneID, curRound))
+    for batType, groupDict in zone.roundGroupDict.items():
+        rankScoreInfo = batTypeScoreInfo.get(str(batType), {})
+        if not rankScoreInfo:
+            GameWorld.ErrLog("仙盟攻城战战场类型没有配置名次积分! batType=%s" % batType)
+        rankScoreDict = {int(k):v for k, v in rankScoreInfo.items()}
+        groupNumList = groupDict.keys()
+        GameWorld.Log("zoneID=%s,batType=%s,groupCnt=%s,rankScoreDict=%s" % (zoneID, batType, len(groupNumList), rankScoreDict))
+        for groupNum in groupNumList:
+            batGroup = zone.GetBatGroup(batType, groupNum)
+            if not batGroup:
+                continue
+            
+            groupValue2 = GetRoundHurtGroupID(batType, groupNum)
+            billboardObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, zoneID, groupValue2)
+            billboardObj.SortData()
+            GameWorld.Log("zoneID=%s,batType=%s,groupNum=%s,roundHurtFamilyCnt=%s" % (zoneID, batType, groupNum, billboardObj.GetCount()))
+            for bIndex in range(billboardObj.GetCount()):
+                billData = billboardObj.At(bIndex)
+                familyID = billData.ID
+                hurtValue = billData.CmpValue
+                hurtValueEx = billData.CmpValue2
+                hurtValueTotal = hurtValue * ChConfig.Def_PerPointValue + hurtValueEx
+                rank = bIndex + 1
+                addScore = GameWorld.GetOrderValueByDict(rankScoreDict, rank, False, 0)
+                
+                joinFamily = zone.GetZoneJoinFamily(familyID)
+                if not joinFamily:
+                    continue
+                joinFamily.score += addScore
+                GameWorld.Log("    zoneID=%s,batType=%s,groupNum=%s,rank=%s,familyID=%s,addScore=%s,updScore=%s,hurtValueTotal=%s" 
+                              % (zoneID, batType, groupNum, rank, familyID, addScore, joinFamily.score, hurtValueTotal))
+                
+                dataID = joinFamily.familyID
+                name1 = joinFamily.familyName
+                id2 = joinFamily.leaderID
+                name2 = joinFamily.leaderName
+                type2, value1, value2 = 0, 0, 0
+                value3 = joinFamily.emblemID
+                value5 = joinFamily.serverID
+                cmpValue = joinFamily.score
+                CrossBillboard.UpdCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID, dataID, name1, name2, type2, value1, value2,
+                                                 cmpValue, id2=id2, autoSort=False, value3=value3, value5=value5)
+                
+    # 都加完积分后统一排序
+    familyScoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
+    familyScoreBillObj.SortData()
+    
+    playerHurtBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, zoneID)
+    playerHurtBillObj.SortData()
+    return
+
+def DoGenerateGuess(zoneID, state):
+    ## 生成竞猜数据
+    if state not in IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGuess", 1):
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    
+    guessRankMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 2)
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    scoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
+    for index in range(guessRankMax):
+        billData = scoreBillObj.At(index)
+        familyID = billData.ID
+        if familyID not in zone.familyGuessDict:
+            zone.familyGuessDict[familyID] = 0
+            GameWorld.Log("仙盟攻城战生成竞猜备选名单: zoneID=%s,index=%s,familyID=%s" % (zoneID, index, familyID))
+            
+    Sync_FamilyGCZGuessInfo(zoneID)
+    return
+
+def DoStartAward(zoneID):
+    ## 开始领奖
+    
+    # 下发竞猜统计结果
+    Sync_FamilyGCZGuessInfo(zoneID)
+    return
+
+def GetRoundHurtGroupID(batType, groupNum): return batType * 100 + groupNum
+def GetRoundHurtGroupIDInfo(groupValue2):
+    batType = groupValue2 / 100
+    groupNum = groupValue2 % 100
+    return batType, groupNum
+
+def Send_CrossServerMsg_FamilyGCZ(actMsgType, msgInfo, serverGroupIDList=None):
+    msgInfo["ActMsgType"] = actMsgType
+    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyGCZ, msgInfo, serverGroupIDList)
+    return
+
+#def GetMaxRound(joinFamilyCnt):
+#    ## 获取最大轮次
+#    FamilyGCZGroupSet = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGroupSet", 1, {})
+#    if joinFamilyCnt not in FamilyGCZGroupSet:
+#        return 0
+#    roundSetDict = FamilyGCZGroupSet[joinFamilyCnt]
+#    if not roundSetDict:
+#        return 0
+#    return max(roundSetDict)
+
+def ClientServerMsg_FamilyGCZ(serverGroupID, msgData):
+    ActMsgType = msgData["ActMsgType"]
+    
+    # 加经验
+    if ActMsgType == "AddCampExp":
+        __clientServer_AddCampExp(msgData)
+        
+    elif ActMsgType == "Query":
+        # 查询类型:1-成员捐献值;2-进入城池场景;3-退出城池场景;4-进入城池;5-退出城池;6-战报;7-分组仙盟成员伤害
+        # "ActMsgType":"Query", "playerID":playerID, "queryType":queryType, "batType":batType, "groupNum":groupNum, "familyID":familyID
+        queryType = msgData["queryType"]
+        playerID = msgData["playerID"]
+        batType = msgData["batType"]
+        groupNum = msgData["groupNum"]
+        tagFamilyID = msgData["familyID"]
+        gczMgr = GetFamilyGCZMgr()
+        joinMember = gczMgr.GetJoinMember(playerID)
+        if not joinMember:
+            return
+        zoneID = joinMember.zoneID
+        zone = gczMgr.GetActZone(zoneID)
+        
+        if queryType == 1: # 成员捐献值
+            __clientServer_QueryContributionInfo(playerID, serverGroupID, msgData)
+        elif queryType == 2: # 进入城池场景 
+            zone.SetPlayerInBatScene(playerID, batType, groupNum, serverGroupID)
+        elif queryType == 3: # 退出城池场景
+            zone.SetPlayerInBatScene(playerID)
+        elif queryType == 4: # 进入城池
+            zone.SetPlayerInBatCity(playerID, batType, groupNum, tagFamilyID, serverGroupID)
+        elif queryType == 5: # 退出城池
+            zone.SetPlayerInBatCity(playerID)
+        elif queryType == 6: # 战报
+            repFamilyID = tagFamilyID if tagFamilyID else joinMember.familyID
+            __clientServer_QueryBatReport(playerID, serverGroupID, repFamilyID)
+        elif queryType == 7: # 分组仙盟成员伤害
+            repFamilyID = tagFamilyID if tagFamilyID else joinMember.familyID
+            __clientServer_QueryGroupFamilyMemHurt(playerID, serverGroupID, zoneID, repFamilyID)
+            
+    elif ActMsgType == "GCZAtk":
+        __clientServer_Atk(serverGroupID, msgData)
+        
+    elif ActMsgType == "Guess":
+        __clientServer_Guess(serverGroupID, msgData)
+        
+    elif ActMsgType == "GetAward":
+        __clientServer_GetAward(serverGroupID, msgData)
+        
+    elif ActMsgType == "GMCMD":
+        __clientServer_GMCMD(serverGroupID, msgData)
+        
+    return
+
+def __clientServer_AddCampExp(msgData):
+    playerID = msgData["playerID"]
+    addCampExp = msgData["addCampExp"]
+    
+    gczMgr = GetFamilyGCZMgr()
+    joinMember = gczMgr.GetJoinMember(playerID)
+    if not joinMember:
+        GameWorld.ErrLog("非仙盟攻城战参赛成员无法加经验!", playerID)
+        return
+    familyID = joinMember.familyID
+    joinFamily = gczMgr.GetJoinFamily(familyID)
+    if not joinFamily:
+        GameWorld.ErrLog("非仙盟攻城战参赛仙盟无法加经验! familyID=%s" % familyID, playerID)
+        return
+    joinFamily.AddCampExp(playerID, addCampExp)
+    return
+
+def __clientServer_GMCMD(serverGroupID, msgData):
+    ''' GM命令,方便开发测试用
+    '''
+    playerID = msgData["playerID"]
+    msgList = msgData["msgList"]
+        
+    gczMgr = GetFamilyGCZMgr()
+    joinMember = gczMgr.GetJoinMember(playerID)
+    if not joinMember:
+        GameWorld.ErrLog("非仙盟攻城战参赛成员!", playerID)
+        return
+    zoneID = joinMember.zoneID
+    familyID = joinMember.familyID
+    zone = gczMgr.GetActZone(zoneID)
+    joinFamily = zone.GetZoneJoinFamily(familyID)
+    if not joinFamily:
+        GameWorld.ErrLog("非仙盟攻城战参赛仙盟! familyID=%s" % familyID, playerID)
+        return
+    batType, groupNum = joinMember.batType, joinMember.groupNum
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "没有战斗分组:batType-group:%s-%s" % (batType, groupNum))
+        return
+    
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    curRound, roundState = GetRoundState(state)
+    
+    value0 = msgList[0]
+    if value0 in ["a", "h", "k"]:
+        if not curRound or (curRound and roundState != FamilyGCZRoundState_Fight):
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "非战斗阶段!state=%s" % (state))
+            return
+        
+    #攻击城池: FamilyGCZ a 城池ID [攻击次数  仙盟ID]
+    if value0 == "a":
+        tagCityID = msgList[1] if len(msgList) > 1 else 0
+        atkCntTotal = msgList[2] if len(msgList) > 2 else 1
+        atkFamilyID = msgList[3] if len(msgList) > 3 else familyID
+        atkMemberID = 0
+        if atkFamilyID == familyID:
+            atkJoinFamily = joinFamily
+            if atkCntTotal == 1:
+                atkMemberID = playerID
+        else:
+            atkJoinFamily = zone.GetZoneJoinFamily(atkFamilyID)
+        if not atkJoinFamily:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "攻击方不存在:%s" % atkFamilyID)
+            return
+        curCityID = atkJoinFamily.cityWall.cityID
+        tagCity = batGroup.GetCityWall(tagCityID)
+        if not tagCity:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "目标城池不存在:%s" % (tagCityID))
+            return
+        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, atkCntTotal, 0, atkMemberID)
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK, tagCity.hp))
+        
+    #攻击城池: FamilyGCZ a 城池ID 剩余血量 [攻击方人数 仙盟ID]
+    elif value0 == "h":
+        tagCityID = msgList[1] if len(msgList) > 1 else 0
+        remainHP = msgList[2] if len(msgList) > 2 else 0
+        atkCntTotal = msgList[3] if len(msgList) > 3 else 0
+        atkFamilyID = msgList[4] if len(msgList) > 4 else familyID
+        atkMemberID = 0
+        if atkFamilyID == familyID:
+            atkJoinFamily = joinFamily
+            if atkCntTotal == 1:
+                atkMemberID = playerID
+        else:
+            atkJoinFamily = zone.GetZoneJoinFamily(atkFamilyID)
+        if not atkJoinFamily:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "攻击方不存在:%s" % atkFamilyID)
+            return
+        curCityID = atkJoinFamily.cityWall.cityID
+        tagCity = batGroup.GetCityWall(tagCityID)
+        if not tagCity:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "目标城池不存在:%s" % (tagCityID))
+            return
+        if tagCity.hp <= remainHP:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "剩余血量不能超过当前血量:%s" % (tagCity.hp))
+            return
+        hurtValueTotal = tagCity.hp - remainHP
+        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, 0, hurtValueTotal, atkMemberID)
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "atkOK:%s,remainHP:%s" % (atkOK, tagCity.hp))
+    
+    #击毁城池: FamilyGCZ k [城池ID 攻击方仙盟ID]    
+    elif value0 == "k":
+        tagCityID = msgList[1] if len(msgList) > 1 else 0
+        atkFamilyID = msgList[2] if len(msgList) > 2 else familyID
+        atkJoinFamily = zone.GetZoneJoinFamily(atkFamilyID)
+        if not atkJoinFamily:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "攻击方不存在:%s" % atkFamilyID)
+            return
+        curCityID = atkJoinFamily.cityWall.cityID
+        #随机一个城池击毁
+        if not tagCityID:
+            cityIDList = batGroup.GetCityIDList()
+        else:
+            cityIDList = [tagCityID]
+        tagCity = None
+        for cityID in cityIDList[::-1]:
+            city = batGroup.GetCityWall(cityID)
+            if not city:
+                continue
+            if city.IsBroken() or cityID in [XiuluoCityID, atkFamilyID]:
+                continue
+            tagCity = city
+            break
+        if not tagCity:
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "没有可击毁的城池了!")
+            return
+        tagCityID = tagCity.cityID
+        hurtValueTotal = tagCity.hp
+        atkOK = __DoGMAtkCity(playerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, 0, hurtValueTotal)
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "killOK:%s,目标:%s,攻击方:%s" % (atkOK, tagCityID, curCityID))
+        
+    #输出城池: FamilyGCZ p [战场类型 分组编号]
+    elif value0 == "p":
+        bType = msgList[1] if len(msgList) > 1 else batType
+        gNum = msgList[2] if len(msgList) > 2 else groupNum
+        __GMPrintGroupCity(zoneID, bType, gNum, playerID, serverGroupID)
+        
+    #山寨竞猜: FamilyGCZ g 人数 [竞猜ID1 ID2 ID3]
+    elif value0 == "g":
+        guessCnt = msgList[1] if len(msgList) > 1 else 1
+        guessFamilyIDList = msgList[2:]
+        __DoGMGuess(zoneID, state, guessCnt, guessFamilyIDList, playerID, serverGroupID)
+        
+    #成员捐献: FamilyGCZ x 经验 [成员数]
+    elif value0 == "x":
+        addCampExp = msgList[1] if len(msgList) > 1 else 1
+        memCnt = msgList[2] if len(msgList) > 2 else 1
+        if memCnt > 1:
+            memIDList = copy.deepcopy(joinFamily.joinMemberIDList)
+            random.shuffle(memIDList)
+            playerIDList = memIDList[:memCnt]
+        else:
+            playerIDList = [playerID]
+            
+        for memID in playerIDList:
+            joinFamily.AddCampExp(memID, addCampExp, False)
+            
+        Sync_FamilyGCZCampInfo(joinFamily.zoneID, joinFamily.familyID)
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "成员捐献:人数(%s)x%s:campLV(%s),Exp(%s)" % (len(playerIDList), addCampExp, joinFamily.campLV, joinFamily.campExp))
+        
+    #设大本营: FamilyGCZ c 等级 [经验]
+    elif value0 == "c":
+        campLV = msgList[1] if len(msgList) > 1 else joinFamily.campLV
+        campExp = msgList[2] if len(msgList) > 2 else joinFamily.campExp
+        if campLV and not IpyGameDataPY.GetIpyGameData("CrossActFamilyGCZCampLV", campLV):
+            GameWorld.DebugAnswerCross(playerID, serverGroupID, "大本营没有该等级:%s" % campExp)
+            return
+        joinFamily.campLV = campLV
+        joinFamily.campExp = campExp
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "设置大本营等级:%s,Exp:%s" % (campLV, campExp))
+        joinFamily.RefreshFamilyAttr()
+        
+    return
+
+def __GMPrintGroupCity(zoneID, batType, groupNum, playerID, serverGroupID):
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    cityIDList = batGroup.GetCityIDList()
+    GameWorld.DebugAnswerCross(playerID, serverGroupID, "---分区(%s)分组类型(%s)组(%s)城池数(%s)" % (zoneID, batType, groupNum, len(cityIDList)))
+    for cNum, cityID in enumerate(cityIDList, 1):
+        city = batGroup.GetCityWall(cityID)
+        if not city:
+            continue
+        broken = "已击毁" if city.IsBroken() else ""
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "城池%02d:%s,防守人员ID:%s,人数:%s/%s" 
+                                   % (cNum, cityID, city.GetCurGuardID(), len(city.fighterIDList), len(city.guardMemIDList)))
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "    HP: %s/%s %s" % (city.hp, city.hpMax, broken))
+    return
+
+def __DoGMAtkCity(gmPlayerID, serverGroupID, zoneID, curRound, batType, groupNum, curCityID, tagCityID, atkCntTotal=0, hurtValueTotal=0, atkMemberID=0):
+    '''GM攻击城池
+    @param curCityID: 攻击方城池ID
+    @param tagCityID: 防守方城池ID
+    @param atkCntTotal: 大于0时指定总攻击次数
+    @param hurtValueTotal: 大于0时指定总伤害
+    @param atkMemberID: 发起攻击的成员ID,如果为0则所有成员平摊攻击
+    '''
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    if curCityID == tagCityID:
+        GameWorld.DebugAnswerCross(gmPlayerID, serverGroupID, "不能攻击自己的城池")
+        return
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        return
+    curCity = batGroup.GetCityWall(curCityID)
+    tagCity = batGroup.GetCityWall(tagCityID)
+    if not curCity:
+        GameWorld.DebugAnswerCross(gmPlayerID, serverGroupID, "攻击方不存在:%s" % curCityID)
+        return
+    if not tagCity:
+        GameWorld.DebugAnswerCross(gmPlayerID, serverGroupID, "被攻击方不存在:%s" % tagCityID)
+        return
+    curFamilyID = curCity.familyID
+    tagFamilyID = tagCity.familyID
+    curJoinFamily = zone.GetZoneJoinFamily(curFamilyID)
+    if not curJoinFamily:
+        GameWorld.DebugAnswerCross(gmPlayerID, serverGroupID, "无归属仙盟城池不能发起攻击")
+        return
+    
+    GameWorld.DebugLog("GM攻击城池: zoneID=%s,batType=%s,groupNum=%s,curCityID=%s,tagCityID=%s,atkCntTotal=%s,hurtValueTotal=%s,atkMemberID=%s" 
+                       % (zoneID, batType, groupNum, curCityID, tagCityID, atkCntTotal, hurtValueTotal, atkMemberID))
+    
+    remainHurtTotal = 0
+    memHurtDict = {}
+    atkMemIDList = [atkMemberID] if (atkMemberID and atkMemberID in curJoinFamily.joinMemberIDList) else curJoinFamily.joinMemberIDList
+    # 按次数输出
+    if atkCntTotal:
+        remainAtkCnt = atkCntTotal
+        while remainAtkCnt > 0 and atkMemIDList:
+            remainAtkCnt -= 1
+            atkMemID = atkMemIDList.pop(0)
+            atkMemIDList.append(atkMemID)
+            atkMember = zone.GetZoneJoinMember(atkMemID)
+            memHurt = atkMember.fightPower
+            remainHurtTotal += atkMember.fightPower
+            memHurtDict[atkMemID] = memHurtDict.get(atkMemID, 0) + memHurt
+        GameWorld.DebugLog("按总次数攻击: atkCntTotal=%s,remainHurtTotal=%s,atkMem=%s,%s" % (atkCntTotal, remainHurtTotal, len(memHurtDict), memHurtDict))
+        
+    # 按总伤害输出
+    elif hurtValueTotal:
+        remainHurtTotal = hurtValueTotal
+        while hurtValueTotal > 0 and atkMemIDList:
+            atkMemID = atkMemIDList.pop(0)
+            atkMemIDList.append(atkMemID)
+            atkMember = zone.GetZoneJoinMember(atkMemID)
+            memHurt = atkMember.fightPower
+            hurtValueTotal -= memHurt
+            memHurtDict[atkMemID] = memHurtDict.get(atkMemID, 0) + memHurt
+        GameWorld.DebugLog("按总伤害攻击: remainHurtTotal=%s,atkMem=%s,%s" % (remainHurtTotal, len(memHurtDict), memHurtDict))
+        
+    killCnt = 0
+    curTime = int(time.time())
+    atkedGuardIDList = [] # 被攻击的人员ID列表
+    # GM攻击不限制防守人员击杀上限,直到伤害用完为止
+    GameWorld.DebugLog("计算GM攻击城池: tagCityID=%s,cityHP=%s/%s,remainHurtTotal=%s" % (tagCityID, tagCity.hp, tagCity.hpMax, remainHurtTotal))
+    while not tagCity.IsBroken() and remainHurtTotal > 0:
+        fighterID = tagCity.fighterIDList[0]
+        if tagCity.IsGuardNPC(fighterID):
+            tagGuard = tagCity.guardNPC
+        else:
+            tagGuard = zone.GetZoneJoinMember(fighterID)
+        if not tagGuard or tagGuard.IsDead():
+            tagCity.NextGuard()
+            continue
+        atkedGuardIDList.append(fighterID)
+        tagCity.atkedTime = curTime
+        
+        hpBef = tagGuard.hp
+        tagGuard.hp = max(0, hpBef - remainHurtTotal)
+        lostHP = hpBef - tagGuard.hp # 该防守队员实际掉血
+        tagCity.hp = max(0, tagCity.hp - lostHP) # 城池同步扣血
+        
+        # 修罗城防爆
+        if tagCityID == XiuluoCityID and tagCity.hp <= 0:
+            tagCity.hp = 1
+            tagGuard.hp = 1
+            lostHP = remainHurtTotal
+            GameWorld.Log("    GM攻击修罗城防爆: zoneID=%s,tagCityID=%s" % (zoneID, tagCityID))
+            
+        GameWorld.DebugLog("    tagGuardID=%s,remainHurtTotal=%s,lostHP=%s,hpBef=%s/%s,hpUpd=%s,cityHP=%s" 
+                           % (fighterID, remainHurtTotal, lostHP, hpBef, tagGuard.hpMax, tagGuard.hp, tagCity.hp))
+        
+        remainHurtTotal -= lostHP # 剩余伤害值
+        
+        # 战报以实际掉血计算,总量为100%
+        curCity.atkReportDict[tagCityID] = curCity.atkReportDict.get(tagCityID, 0) + lostHP
+        tagCity.defReportDict[curCityID] = tagCity.defReportDict.get(curCityID, 0) + lostHP
+        
+        if tagGuard.IsDead():
+            killCnt += 1
+            tagCity.NextGuard()
+            
+        GameWorld.DebugLog("        remainHurtTotal=%s,killCnt=%s,atkReportDict=%s,defReportDict=%s" 
+                           % (remainHurtTotal, killCnt, curCity.atkReportDict, tagCity.defReportDict))
+        
+    GameWorld.DebugLog("cityHP=%s/%s,atkedGuardIDList=%s" % (tagCity.hp, tagCity.hpMax, atkedGuardIDList))
+    
+    for memID, memHurt in memHurtDict.items():
+        GameWorld.DebugLog("攻击方成员输出: memID=%s,%s" % (memID, memHurt))
+        joinMember = zone.GetZoneJoinMember(memID)
+        if not joinMember:
+            continue
+        joinMember.totalHurtValue += memHurt
+        cmpValue = joinMember.totalHurtValue / ChConfig.Def_PerPointValue
+        cmpValue2 = joinMember.totalHurtValue % ChConfig.Def_PerPointValue
+        CrossBillboard.UpdCrossBillboardPlayer(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, memID, zoneID, cmpValue, cmpValue2, autoSort=False)
+        if curCity.IsBroken():
+            continue
+        curCity.fighterHurtDict[memID] = curCity.fighterHurtDict.get(memID, 0) + memHurt
+        playerRoundHurtTotal = curCity.fighterHurtDict[memID] # 只算生存伤害
+        _, topHurtPlayerValue = batGroup.GetTopHurtPlayerIDValue()
+        if playerRoundHurtTotal > topHurtPlayerValue:
+            batGroup.UpdTopHurtPlayerIDValue(memID, playerRoundHurtTotal)
+            
+    if not curCity.IsBroken():
+        UndFamilyRoundHurtBillboard(curJoinFamily)
+        
+    if not CheckXiuluoCity(zoneID, curRound, batType, groupNum, gmPlayerID):
+        Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum)
+    Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, tagCityID)
+    if tagFamilyID:
+        Sync_FamilyGCZCampInfo(zoneID, tagFamilyID, defMemIDList=atkedGuardIDList)
+    return True
+
+def __clientServer_Atk(serverGroupID, msgData):
+    ## 攻击
+    zoneID = msgData["zoneID"]
+    playerID = msgData["playerID"]
+    atkType = msgData["atkType"] # 攻击类型: 1-普通单攻;2-技能单攻;3-技能群攻;
+    tagCityID = msgData["tagCityID"] # 目标城池ID,一般是仙盟ID或者特殊城池ID如修罗城城池,普攻单攻需指定目标,群攻技能发0
+    tagGuardID = msgData["tagGuardID"] # 目标守卫ID,一般是玩家ID或者特殊守卫ID如修罗城守卫,普攻单攻需指定目标,技能攻击发0
+    hurtMulti = msgData["hurtMulti"]
+    #useItemID = msgData["useItemID"]
+    hurtFamilyCnt = msgData["hurtFamilyCnt"]
+    
+    GameWorld.DebugLog("仙盟攻城战攻击: zoneID=%s,atkType=%s,tagCityID=%s,tagGuardID=%s,hurtMulti=%s,hurtFamilyCnt=%s" 
+                       % (zoneID, atkType, tagCityID, tagGuardID, hurtMulti, hurtFamilyCnt), playerID)
+    
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    curRound, roundState = GetRoundState(state)
+    if not curRound or (curRound and roundState != FamilyGCZRoundState_Fight):
+        GameWorld.ErrLog("仙盟攻城战非攻击阶段: state=%s,curRound=%s,roundState=%s" % (state, curRound, roundState), playerID)
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    joinMember = gczMgr.GetJoinMember(playerID)
+    if not joinMember:
+        GameWorld.ErrLog("非仙盟攻城战参赛成员!", playerID)
+        return
+    if zoneID != joinMember.zoneID:
+        GameWorld.ErrLog("非仙盟攻城战参赛成员! zoneID=%s" % joinMember.zoneID, playerID)
+        return
+    zone = gczMgr.GetActZone(zoneID)
+    familyID = joinMember.familyID
+    batType, groupNum = joinMember.batType, joinMember.groupNum
+    curJoinFamily = zone.GetZoneJoinFamily(familyID)
+    if not curJoinFamily:
+        GameWorld.ErrLog("非仙盟攻城战参赛仙盟! familyID=%s" % familyID, playerID)
+        return
+    curCity = curJoinFamily.cityWall
+    curCityID = curCity.cityID
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        GameWorld.ErrLog("仙盟攻城战分组不存在! batType=%s,groupNum=%s" % (batType, groupNum), playerID)
+        return
+    
+    canAtkedCityList = [] # 可被攻击的城池列表
+    # 群攻技能
+    if atkType == AtkType_SkillArea:
+        # 群攻时强制目标为0
+        tagCityID, tagGuardID = 0, 0
+        for tagCID in batGroup.cityWallDict.keys(): # 遍历同组城池
+            if tagCID == curCityID:
+                #不打自己
+                continue
+            tagCity = batGroup.GetCityWall(tagCID)
+            if not tagCity or tagCity.IsBroken():
+                # 已被摧毁的
+                continue
+            canAtkedCityList.append(tagCity)
+        if len(canAtkedCityList) > hurtFamilyCnt:
+            random.shuffle(canAtkedCityList)
+            canAtkedCityList = canAtkedCityList[:hurtFamilyCnt]
+            
+    # 单攻技能
+    elif atkType == AtkType_SkillSingle:
+        if not tagCityID or tagCityID == curCityID:
+            SendFamilyGCZAtkResult(AtkRet_Other, "TagIDError-tagCityID", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        tagCity = batGroup.GetCityWall(tagCityID)
+        if not tagCity:
+            SendFamilyGCZAtkResult(AtkRet_Other, "GroupNoTagCity", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        if tagCity.IsBroken():
+            SendFamilyGCZAtkResult(AtkRet_TagBroken, "TagBroken", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        tagGuardID = 0 # 单攻技能指定城池,不指定目标
+        canAtkedCityList.append(tagCity)
+        
+    # 普通单攻
+    else:
+        if not tagCityID or not tagGuardID or tagCityID == curCityID:
+            SendFamilyGCZAtkResult(AtkRet_Other, "TagIDError-tagCityID-GuardID", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        tagCity = batGroup.GetCityWall(tagCityID)
+        if not tagCity:
+            SendFamilyGCZAtkResult(AtkRet_Other, "GroupNoTagCity", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        if tagCity.IsBroken():
+            SendFamilyGCZAtkResult(AtkRet_TagBroken, "TagBroken", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        if tagCity.GetCurGuardID() != tagGuardID:
+            GameWorld.DebugLog("CurGuardID=%s,fighterIDList=%s" % (tagCity.GetCurGuardID(), tagCity.fighterIDList))
+            SendFamilyGCZAtkResult(AtkRet_TagDead, "TagDead", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+            return
+        canAtkedCityList.append(tagCity)
+        
+    if not canAtkedCityList:
+        SendFamilyGCZAtkResult(AtkRet_Other, "NoAtkedTag", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+        return
+    
+    batFightPower = joinMember.fightPower
+    oneHurtTotal = totalHurtValue = batFightPower * hurtMulti # 总伤害
+    if len(canAtkedCityList) > 1 :
+        oneHurtTotal = totalHurtValue / len(canAtkedCityList) # 所有可攻击的目标仙盟平摊伤害
+    GameWorld.DebugLog("batFightPower=%s,totalHurtValue=%s,oneHurtTotal=%s" % (batFightPower, totalHurtValue, oneHurtTotal), playerID)
+    
+    curTime = int(time.time())
+    oneCanAtkDefMax = IpyGameDataPY.GetFuncCfg("FamilyGCZCity", 2) # 单盟最大可攻击x个防守人员
+    hurtDict = {} # 最终伤血信息
+    killCntTotal = 0 # 本次攻击击杀数
+    
+    for tagCity in canAtkedCityList:
+        if not tagCity or tagCity.IsBroken():
+            continue
+        killCnt = 0
+        tagCityID = tagCity.cityID
+        remainHurtTotal = oneHurtTotal
+        atkedGuardIDList = [] # 被攻击的人员ID列表
+        GameWorld.DebugLog("计算攻击城池: tagCityID=%s,cityHP=%s/%s,oneHurtTotal=%s,oneCanAtkDefMax=%s,fighterIDList=%s" 
+                           % (tagCityID, tagCity.hp, tagCity.hpMax, oneHurtTotal, oneCanAtkDefMax, tagCity.fighterIDList), playerID)
+        while not tagCity.IsBroken() and len(atkedGuardIDList) < oneCanAtkDefMax and remainHurtTotal > 0:
+            fighterID = tagCity.fighterIDList[0]
+            if tagCity.IsGuardNPC(fighterID):
+                tagGuard = tagCity.guardNPC
+            else:
+                tagGuard = zone.GetZoneJoinMember(fighterID)
+            if not tagGuard or tagGuard.IsDead():
+                tagCity.NextGuard()
+                continue
+            atkedGuardIDList.append(fighterID)
+            tagCity.atkedTime = curTime
+            
+            hpBef = tagGuard.hp
+            tagGuard.hp = max(0, hpBef - remainHurtTotal)
+            lostHP = hpBef - tagGuard.hp # 该防守队员实际掉血
+            tagCity.hp = max(0, tagCity.hp - lostHP) # 城池同步扣血
+            
+            # 修罗城防爆
+            if tagCityID == XiuluoCityID and tagCity.hp <= 0:
+                tagCity.hp = 1
+                tagGuard.hp = 1
+                lostHP = remainHurtTotal
+                GameWorld.Log("    修罗城防爆: zoneID=%s,familyID=%s" % (zoneID, familyID), playerID)
+                
+            GameWorld.DebugLog("    tagGuardID=%s,remainHurtTotal=%s,lostHP=%s,hpBef=%s/%s,hpUpd=%s,cityHPUpd=%s" 
+                               % (fighterID, remainHurtTotal, lostHP, hpBef, tagGuard.hpMax, tagGuard.hp, tagCity.hp), playerID)
+            
+            remainHurtTotal -= lostHP # 剩余伤害值
+            
+            # 战报以实际掉血计算,总量为100%
+            curCity.atkReportDict[tagCityID] = curCity.atkReportDict.get(tagCityID, 0) + lostHP
+            tagCity.defReportDict[curCityID] = tagCity.defReportDict.get(curCityID, 0) + lostHP
+            
+            if tagGuard.IsDead():
+                killCnt += 1
+                killCntTotal += 1
+                tagCity.NextGuard()
+                
+            GameWorld.DebugLog("        remainHurtTotal=%s,killCnt=%s,atkReportDict=%s,defReportDict=%s" 
+                               % (remainHurtTotal, killCnt, curCity.atkReportDict, tagCity.defReportDict), playerID)
+            
+        if not atkedGuardIDList:
+            continue
+        
+        hurtDict[tagCityID] = oneHurtTotal # 飘血按伤害
+        # 通知被攻击盟成员大本营城池信息
+        if tagCity.familyID:
+            Sync_FamilyGCZCampInfo(zoneID, tagCity.familyID, defMemIDList=atkedGuardIDList)
+        Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, tagCityID, AtkRet_OK, playerID, oneHurtTotal, killCnt)
+        
+    if not hurtDict:
+        SendFamilyGCZAtkResult(AtkRet_Other, "NoHurt", msgData, serverGroupID, zoneID, curRound, batType, groupNum)
+        return
+    
+    # 更新个人活动总伤害
+    joinMember.totalHurtValue += totalHurtValue
+    GameWorld.DebugLog("    更新玩家活动总伤害榜值! memTotalHurt=%s" % (joinMember.totalHurtValue), playerID)
+    cmpValue = joinMember.totalHurtValue / ChConfig.Def_PerPointValue
+    cmpValue2 = joinMember.totalHurtValue % ChConfig.Def_PerPointValue
+    CrossBillboard.UpdCrossBillboardPlayer(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, playerID, zoneID, cmpValue, cmpValue2, autoSort=False)
+    
+    # 本盟还未被摧毁,可计算轮次伤害
+    if not curCity.IsBroken():
+        # 本轮成员贡献伤害
+        curCity.fighterHurtDict[playerID] = curCity.fighterHurtDict.get(playerID, 0) + totalHurtValue
+        GameWorld.DebugLog("    更新本轮仙盟成员贡献伤害! familyID=%s,playerID=%s,addHurtValue=%s,%s" 
+                           % (familyID, playerID, totalHurtValue, curCity.fighterHurtDict), playerID)
+        UndFamilyRoundHurtBillboard(curJoinFamily)
+        
+        playerRoundHurtTotal = curCity.fighterHurtDict[playerID] # 只算生存伤害
+        topHurtPlayerID, topHurtPlayerValue = batGroup.GetTopHurtPlayerIDValue()
+        if playerRoundHurtTotal > topHurtPlayerValue:
+            batGroup.UpdTopHurtPlayerIDValue(playerID, playerRoundHurtTotal)
+            GameWorld.DebugLog("    更新为本组伤害第一玩家: playerRoundHurtTotal=%s > %s" % (playerRoundHurtTotal, topHurtPlayerValue), playerID)
+        else:
+            GameWorld.DebugLog("    未超过本组伤害第一玩家: playerRoundHurtTotal=%s <= %s,topPlayerHurtID=%s" 
+                               % (playerRoundHurtTotal, topHurtPlayerValue, topHurtPlayerID), playerID)
+    else:
+        GameWorld.DebugLog("    自己仙盟城池已被摧毁,不再累加本轮仙盟伤害榜值! familyID=%s" % familyID, playerID)
+        
+    SendFamilyGCZAtkResult(AtkRet_OK, "OK", msgData, serverGroupID, zoneID, curRound, batType, groupNum, hurtDict, killCntTotal)
+    return
+
+def CheckXiuluoCity(zoneID, curRound, batType, groupNum, playerID=0):
+    ## 检查是否刷新修罗城
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    #maxRound = GetMaxRound(zone.joinFamilyCnt)
+    appearRoundList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZXiuluo", 4)
+    if curRound not in appearRoundList:
+        return
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        return
+    xiuluoCity = batGroup.GetCityWall(XiuluoCityID)
+    if xiuluoCity:
+        return
+    aliveFamilyCnt = 0
+    for tagCity in batGroup.cityWallDict.values():
+        if tagCity.IsBroken():
+            continue
+        aliveFamilyCnt += 1
+        if aliveFamilyCnt > 1:
+            return
+    GameWorld.Log("剩下一个仙盟未被摧毁,刷新修罗城! zoneID=%s,curRound=%s,batType=%s,groupNum=%s" % (zoneID, curRound, batType, groupNum), playerID)
+    xiuluoCityLV = IpyGameDataPY.GetFuncCfg("FamilyGCZXiuluo", 3)
+    guardNPCCount = IpyGameDataPY.GetFuncCfg("FamilyGCZXiuluo", 2)
+    batGroup.CreateCityWall(XiuluoCityID, xiuluoCityLV, guardNPCCount=guardNPCCount, isAttrLock=True)
+    Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum)
+    return True
+
+def SendFamilyGCZAtkResult(atkRet, errMsg, msgData, serverGroupID, zoneID, curRound, batType, groupNum, hurtDict={}, killCntTotal=0):
+    atkPlayerID = msgData["playerID"]
+    atkType = msgData["atkType"]
+    tagCityID = msgData["tagCityID"]
+    
+    if atkPlayerID and atkRet != AtkRet_OK:
+        GameWorld.ErrLog("仙盟攻城战攻击失败: atkRet=%s:%s,atkType=%s,%s" % (atkRet, errMsg, atkType, msgData), atkPlayerID)
+        # 失败时仅通知发起攻击玩家刷新最新信息即可
+        if atkType == AtkType_Normal:
+            Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, tagCityID, atkRet, atkPlayerID, toPlayerServerDict={atkPlayerID:serverGroupID})
+        else:
+            Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum, atkPlayerID, atkType, toPlayerServerDict={atkPlayerID:serverGroupID})
+            
+    # 有玩家攻击成功,需要同步通知在城池场景内及城池内的玩家,因为无论在哪里攻击都会触发城池内城池外双方信息的变化
+    if atkPlayerID and atkRet == AtkRet_OK:
+        #Sync_FamilyGCZBatCityInfo 这个在攻击遍历中已通知,仅通知被攻击的城池
+        Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum, atkPlayerID, atkType, hurtDict, killCntTotal)
+        
+    msgInfo = {"atkRet":atkRet, "errMsg":errMsg, "reqMsg":msgData, "hurtDict":hurtDict, "killCntTotal":killCntTotal}
+    Send_CrossServerMsg_FamilyGCZ("FamilyGCZAtkResult", msgInfo, [serverGroupID])
+    
+    CheckXiuluoCity(zoneID, curRound, batType, groupNum, atkPlayerID)
+    return
+
+def Sync_FamilyGCZBatSceneInfo(zoneID, batType, groupNum, atkPlayerID=0, atkType=0, hurtDict={}, killCntTotal=0, toPlayerServerDict=None):
+    ## 通知城池场景信息
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        return
+    
+    onlinePlayerIDDict = {}
+    if toPlayerServerDict != None:
+        onlinePlayerIDDict = toPlayerServerDict
+    else:
+        onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+        notifyPlayerIDList = batGroup.inBatScenePlayerIDList # + [atkPlayerID]
+        for playerID in notifyPlayerIDList:
+            if not playerID or playerID in onlinePlayerIDDict:
+                continue
+            serverGroupID = onlineMgr.GetOLPlayerServerGroupID(playerID)
+            if serverGroupID:
+                onlinePlayerIDDict[playerID] = serverGroupID
+        GameWorld.DebugLog("Sync_FamilyGCZBatSceneInfo zoneID=%s,batType=%s,groupNum=%s,%s,%s" 
+                           % (zoneID, batType, groupNum, notifyPlayerIDList, onlinePlayerIDDict), atkPlayerID)
+        
+    if not onlinePlayerIDDict:
+        return
+    
+    sceneShowPlayerIDList = [] # 场景中需要展示的玩家ID列表
+    topFamilyID, topLeaderID = 0, 0
+    
+    groupValue2 = GetRoundHurtGroupID(batType, groupNum)
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    roundHurtBillboard = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZRoundHurt, zoneID, groupValue2)
+    idOrderDict = roundHurtBillboard.GetIDOrderDict()
+    if roundHurtBillboard.GetCount():
+        topFamilyBillboard = roundHurtBillboard.At(0)
+        topFamilyID = topFamilyBillboard.ID
+        topFamily = zone.GetZoneJoinFamily(topFamilyID)
+        if topFamily:
+            topLeaderID = topFamily.leaderID
+            if topLeaderID not in sceneShowPlayerIDList:
+                sceneShowPlayerIDList.append(topLeaderID)
+            
+    topPlayerID = batGroup.GetTopHurtPlayerIDValue()[0]
+    topPlayerFamilyID = 0
+    if topPlayerID:
+        if topPlayerID not in sceneShowPlayerIDList:
+            sceneShowPlayerIDList.append(topPlayerID)
+        topMember = zone.GetZoneJoinMember(topPlayerID)
+        if topMember:
+            topPlayerFamilyID = topMember.familyID
+            
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatSceneInfo()
+    clientPack.BatType = batType
+    clientPack.GroupNum = groupNum
+    clientPack.TopFamilyID = topFamilyID
+    clientPack.TopLeaderID = topLeaderID
+    clientPack.TopPlayerID = topPlayerID
+    clientPack.TopPlayerFamilyID = topPlayerFamilyID
+    # 场景信息只通知技能攻击
+    if atkType != AtkType_Normal and hurtDict:
+        if atkPlayerID not in sceneShowPlayerIDList:
+            sceneShowPlayerIDList.append(atkPlayerID)
+        clientPack.AtkPlayerID = atkPlayerID
+        clientPack.AtkType = atkType
+        clientPack.KillCnt = killCntTotal
+        clientPack.HurtList = []
+        for cityID, hurtTotal in hurtDict.items():
+            hurt = ChPyNetSendPack.tagGCFamilyGCZBatSceneHurt()
+            hurt.CityID = cityID
+            hurt.HurtValue = hurtTotal % ChConfig.Def_PerPointValue
+            hurt.HurtValueEx = hurtTotal / ChConfig.Def_PerPointValue
+            clientPack.HurtList.append(hurt)
+        clientPack.HurtCnt = len(clientPack.HurtList)
+        
+    # 场景有变化的城池,暂时都同步,不然可能名次显示有问题
+    #if hurtDict:
+    #    syncCityIDList = hurtDict.keys()
+    #else:
+    syncCityIDList = batGroup.GetCityIDList()
+    clientPack.CityList = []
+    for cityID in syncCityIDList:
+        cityWall = batGroup.GetCityWall(cityID)
+        if not cityWall:
+            continue
+        city = ChPyNetSendPack.tagGCFamilyGCZBatSceneCity()
+        city.CityID = cityID
+        city.CityLV = cityWall.cityLV
+        city.FamilyID = cityWall.familyID
+        city.Rank = idOrderDict.get(cityWall.familyID, 0)
+        city.HP = cityWall.hp % ChConfig.Def_PerPointValue
+        city.HPEx = cityWall.hp / ChConfig.Def_PerPointValue
+        city.HPMax = cityWall.hpMax % ChConfig.Def_PerPointValue
+        city.HPMaxEx = cityWall.hpMax / ChConfig.Def_PerPointValue
+        city.LastAtkedTime = cityWall.atkedTime
+        clientPack.CityList.append(city)
+    clientPack.CityCnt = len(clientPack.CityList)
+    
+    # 场景需要用到的玩家信息
+    clientPack.PlayerInfoList = []
+    for playerID in sceneShowPlayerIDList:
+        joinMember = zone.GetZoneJoinMember(playerID)
+        if not joinMember:
+            continue
+        scenePlayer = ChPyNetSendPack.tagGCFamilyGCZBatScenePlayer()
+        scenePlayer.PlayerID = playerID
+        scenePlayer.Name = joinMember.playerName
+        scenePlayer.Face = joinMember.face
+        scenePlayer.FacePic = joinMember.facePic
+        clientPack.PlayerInfoList.append(scenePlayer)
+    clientPack.PlayerCnt = len(clientPack.PlayerInfoList)
+    
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    return
+
+def Sync_FamilyGCZBatCityInfo(zoneID, batType, groupNum, cityID, atkRet=0, atkPlayerID=0, hurtValue=0, killCnt=0, toPlayerServerDict=None):
+    ## 通知城池信息
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    batGroup = zone.GetBatGroup(batType, groupNum)
+    if not batGroup:
+        return
+    cityWall = batGroup.GetCityWall(cityID)
+    if not cityWall:
+        return
+    
+    onlinePlayerIDDict = {}
+    if toPlayerServerDict != None:
+        onlinePlayerIDDict = toPlayerServerDict
+    else:
+        onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+        notifyPlayerIDList = cityWall.inCityPlayerIDList # + [atkPlayerID]
+        for playerID in notifyPlayerIDList:
+            if not playerID or playerID in onlinePlayerIDDict:
+                continue
+            serverGroupID = onlineMgr.GetOLPlayerServerGroupID(playerID)
+            if serverGroupID:
+                onlinePlayerIDDict[playerID] = serverGroupID
+        GameWorld.DebugLog("Sync_FamilyGCZBatCityInfo zoneID=%s,batType=%s,groupNum=%s,cityID=%s,%s,%s" 
+                           % (zoneID, batType, groupNum, cityID, notifyPlayerIDList, onlinePlayerIDDict), atkPlayerID)
+        
+    if not onlinePlayerIDDict:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatCityInfo()
+    clientPack.CityID = cityID
+    clientPack.CityLV = cityWall.cityLV
+    clientPack.FamilyID = cityWall.familyID
+    clientPack.GuardID = cityWall.GetCurGuardID()
+    clientPack.HP = cityWall.hp % ChConfig.Def_PerPointValue
+    clientPack.HPEx = cityWall.hp / ChConfig.Def_PerPointValue
+    clientPack.HPMax = cityWall.hpMax % ChConfig.Def_PerPointValue
+    clientPack.HPMaxEx = cityWall.hpMax / ChConfig.Def_PerPointValue
+    clientPack.AtkPlayerID = atkPlayerID
+    clientPack.AtkRet = atkRet
+    clientPack.KillCnt = killCnt
+    clientPack.HurtValue = hurtValue % ChConfig.Def_PerPointValue    
+    clientPack.HurtValueEx = hurtValue / ChConfig.Def_PerPointValue    
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    return
+
+def __clientServer_QueryContributionInfo(playerID, serverGroupID, msgData):
+    ## 查询贡献值信息
+    gczMgr = GetFamilyGCZMgr()
+    joinFamily = gczMgr.GetJoinFamilyByPlayerID(playerID)
+    if not joinFamily:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZContributionInfo()
+    clientPack.ContriList = []
+    for memID, value in joinFamily.memAddCampExpInfo.items():
+        contri = ChPyNetSendPack.tagGCFamilyGCZContribution()
+        contri.PlayerID = memID
+        contri.ContributionValue = value
+        clientPack.ContriList.append(contri)
+    clientPack.ContriCnt = len(clientPack.ContriList)
+    NetPackCommon.SendFakePackByCross({playerID:serverGroupID}, clientPack)
+    return
+
+def __clientServer_QueryBatReport(playerID, serverGroupID, repFamilyID):
+    ## 查询战报
+    
+    gczMgr = GetFamilyGCZMgr()
+    repFamily = gczMgr.GetJoinFamily(repFamilyID)
+    if not repFamily:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatReport()
+    clientPack.FamilyID = repFamilyID
+    clientPack.DefRepList = []
+    for tagFamilyID, hurtValue in repFamily.cityWall.defReportDict.items():
+        defRep = ChPyNetSendPack.tagGCFamilyGCZBatRepHurt()
+        defRep.TagFamilyID = tagFamilyID
+        defRep.HurtValue = hurtValue % ChConfig.Def_PerPointValue
+        defRep.HurtValueEx = hurtValue / ChConfig.Def_PerPointValue
+        clientPack.DefRepList.append(defRep)
+    clientPack.DefRepCnt = len(clientPack.DefRepList)
+    
+    clientPack.AtkRepList = []
+    for tagFamilyID, hurtValue in repFamily.cityWall.atkReportDict.items():
+        atkRep = ChPyNetSendPack.tagGCFamilyGCZBatRepHurt()
+        atkRep.TagFamilyID = tagFamilyID
+        atkRep.HurtValue = hurtValue % ChConfig.Def_PerPointValue
+        atkRep.HurtValueEx = hurtValue / ChConfig.Def_PerPointValue
+        clientPack.AtkRepList.append(atkRep)
+    clientPack.AtkRepCnt = len(clientPack.AtkRepList)
+    
+    NetPackCommon.SendFakePackByCross({playerID:serverGroupID}, clientPack)
+    return
+
+def __clientServer_QueryGroupFamilyMemHurt(playerID, serverGroupID, zoneID, tagFamilyID):
+    ## 查询分组仙盟成员伤害信息
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    joinFamily = zone.GetZoneJoinFamily(tagFamilyID)
+    if not joinFamily:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZGroupFamilyMemHurtInfo()
+    clientPack.FamilyID = tagFamilyID
+    clientPack.HurtMemList = []
+    for memID, value in joinFamily.cityWall.fighterHurtDict.items():
+        joinMember = zone.GetZoneJoinMember(memID)
+        memHurt = ChPyNetSendPack.tagGCFamilyGCZGroupFamilyMemHurt()
+        memHurt.PlayerID = memID
+        memHurt.Name = joinMember.playerName if joinMember else ""
+        memHurt.HurtValue = value % ChConfig.Def_PerPointValue
+        memHurt.HurtValueEx = value / ChConfig.Def_PerPointValue
+        clientPack.HurtMemList.append(memHurt)
+    clientPack.HurtMemCnt = len(clientPack.HurtMemList)
+    NetPackCommon.SendFakePackByCross({playerID:serverGroupID}, clientPack)
+    return
+
+def __clientServer_Guess(serverGroupID, msgData):
+    ## 竞猜 - 无参与资格的也可以竞猜
+    
+    playerID = msgData["playerID"]
+    selectFamilyIDList = msgData["selectFamilyIDList"]
+        
+    zoneID = GetPlayerActJoinInfo(playerID)[0]
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    canGuessStateList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGuess", 1)
+    if state not in canGuessStateList:
+        GameWorld.ErrLog("仙盟攻城战非竞猜阶段: zoneID=%s,state=%s not in %s" % (zoneID, state, canGuessStateList), playerID)
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    guessCountMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 3)
+    selectFamilyIDList = selectFamilyIDList[:guessCountMax]
+    for familyID in selectFamilyIDList:
+        if familyID not in zone.familyGuessDict:
+            GameWorld.ErrLog("仙盟攻城战该仙盟ID不可竞猜! zoneID=%s,selectFamilyIDList=%s,familyID=%s not in %s" 
+                             % (zoneID, selectFamilyIDList, familyID, zone.familyGuessDict.keys()), playerID)
+            return
+        
+    for familyID in selectFamilyIDList:
+        zone.familyGuessDict[familyID] = zone.familyGuessDict.get(familyID, 0) + 1
+        
+    zone.playerGuessDict[playerID] = selectFamilyIDList
+    GameWorld.DebugLog("仙盟攻城战竞猜: %s,familyGuessDict=%s" % (selectFamilyIDList, zone.familyGuessDict), playerID)
+    
+    Sync_FamilyGCZGuessInfo(zoneID, selectPlayerID=playerID)
+    return
+
+def __DoGMGuess(zoneID, state, guessCnt, selectFamilyIDList, playerID, serverGroupID):
+    ## GM山寨竞猜数据
+    
+    canGuessStateList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZGuess", 1)
+    if state not in canGuessStateList:
+        GameWorld.DebugAnswerCross(playerID, serverGroupID, "非竞猜阶段!state=%s" % (state))
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    guessCountMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 3)
+    selectFamilyIDList = selectFamilyIDList[:guessCountMax]
+    
+    # 没有指定竞猜,则随机
+    if not selectFamilyIDList:
+        familyIDList = zone.familyGuessDict.keys()
+        random.shuffle(familyIDList)
+        selectFamilyIDList = familyIDList[:guessCountMax]
+        
+    for familyID in selectFamilyIDList:
+        zone.familyGuessDict[familyID] = zone.familyGuessDict.get(familyID, 0) + guessCnt
+        
+    maxFackID = 0
+    for pID in zone.playerGuessDict.keys():
+        if pID < 10000 and pID > maxFackID:
+            maxFackID = pID
+            
+    for pID in range(maxFackID + 1, maxFackID + 1 + guessCnt + 1):
+        zone.playerGuessDict[pID] = selectFamilyIDList
+        
+    GameWorld.DebugAnswerCross(playerID, serverGroupID, "新增竞猜人数:%s, 总:%s" % (guessCnt, len(zone.playerGuessDict)))
+    Sync_FamilyGCZGuessInfo(zoneID)
+    return
+
+def __clientServer_GetAward(serverGroupID, msgData):
+    ## 领奖
+    playerID = msgData["playerID"]
+    awardType = msgData["awardType"]
+    
+    zoneID = GetPlayerActJoinInfo(playerID)[0]
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    actID = actInfo.get(ShareDefine.ActKey_ID)
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state or state != FamilyGCZState_Award:
+        GameWorld.ErrLog("仙盟攻城战非领奖阶段: zoneID=%s,state=%s" % (zoneID, state), playerID)
+        return
+        
+    ipyDataInfo = actInfo.get(ShareDefine.ActKey_IpyDataInfo)
+    if not ipyDataInfo:
+        return
+    
+    # 跨服领奖不验证领奖状态,领奖状态只标记已推送,实际领奖由子服地图玩家自行验证
+    
+    # 竞猜
+    if awardType == AwwardType_Guess:
+        awardTemplateID = ipyDataInfo.get("GuessTemplateID", 0)
+        __clientServer_GetAward_Guess(zoneID, playerID, serverGroupID, actID, state, awardType, awardTemplateID)
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    joinMember = zone.GetZoneJoinMember(playerID)
+    if not joinMember:
+        GameWorld.ErrLog("非仙盟攻城战参赛成员!", playerID)
+        return
+    zoneID = joinMember.zoneID
+    familyID = joinMember.familyID
+    fmLV = joinMember.fmLV
+    
+    billID = playerID
+    billboardType = 0
+    awardTemplateID = 0
+    billValue = None
+    
+    # 个人排行
+    if awardType == AwwardType_PlayerHurt:
+        billID = playerID
+        billboardType = ShareDefine.Def_CBT_FamilyGCZPlayerHurt
+        awardTemplateID = ipyDataInfo.get("PersonalTemplateID", 0)
+        
+    # 仙盟排行
+    elif awardType == AwwardType_FamilyScore:
+        billID = familyID
+        billboardType = ShareDefine.Def_CBT_FamilyGCZScore
+        awardTemplateID = ipyDataInfo.get("FamilyTemplateID", 0)
+        
+    else:
+        return
+    
+    if not awardTemplateID:
+        GameWorld.ErrLog("仙盟攻城战领奖时没有排行奖励模版! zoneID=%s,awardType=%s" % (zoneID, awardType), playerID)
+        return
+    
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    billObj = billboardMgr.GetCrossBillboard(billboardType, zoneID)
+    idRankDict = billObj.GetIDOrderDict()
+    billRank = idRankDict.get(billID, 0)
+    
+    if not billRank:
+        GameWorld.ErrLog("仙盟攻城战领奖时没有上榜! zoneID=%s,awardType=%s,billboardType=%s,billID=%s" 
+                         % (zoneID, awardType, billboardType, billID), playerID)
+        return
+    
+    playerAwardItemList = GameWorld.GetActBillboardTempAward(playerID, billID, billRank, awardTemplateID, billValue, fmLV)
+    if not playerAwardItemList:
+        return
+    
+    GameWorld.Log("仙盟攻城战玩家请求领取奖励! zoneID=%s,awardType=%s,playerAwardItemList=%s" 
+                  % (zoneID, awardType, playerAwardItemList), playerID)
+    
+    joinMember.awardState = joinMember.awardState | pow(2, awardType) # 标记已推送
+    msgInfo = {"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardValue":billRank, 
+               "awardItemList":playerAwardItemList, "fmLV":fmLV}
+    Send_CrossServerMsg_FamilyGCZ("FamilyGCZAward", msgInfo, [serverGroupID])
+    return
+
+def __clientServer_GetAward_Guess(zoneID, playerID, serverGroupID, actID, state, awardType, guessTemplateID):
+    ## 竞猜领奖
+    playerAwardID, awardItemList = GetGuessAwardInfo(zoneID, state, guessTemplateID, playerID)
+    if not awardItemList:
+        GameWorld.ErrLog("仙盟攻城战玩家没有竞猜奖励! zoneID=%s" % (zoneID), playerID)
+        return
+    GameWorld.Log("仙盟攻城战玩家请求领取竞猜奖励! zoneID=%s,playerAwardID=%s,awardItemList=%s" 
+                  % (zoneID, playerAwardID, awardItemList), playerID)
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    joinMember = zone.GetZoneJoinMember(playerID)
+    if joinMember:
+        joinMember.awardState = joinMember.awardState | pow(2, awardType) # 标记已推送
+    msgInfo = {"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardValue":playerAwardID, "awardItemList":awardItemList}
+    Send_CrossServerMsg_FamilyGCZ("FamilyGCZAward", msgInfo, [serverGroupID])
+    return
+
+def GetGuessAwardInfo(zoneID, state, guessTemplateID, playerID):
+    ## 获取竞猜奖励
+    playerAwardID = 0
+    awardItemList = []
+    _, statGuessRetDict = __statGuessFinal(zoneID, state, guessTemplateID)
+    for awardID, rightPlayerIDList in statGuessRetDict.items():
+        if playerID in rightPlayerIDList:
+            playerAwardID = awardID
+            break
+        
+    if playerAwardID:
+        ipyData = IpyGameDataPY.GetIpyGameDataByCondition("ActGuess", {"TemplateID":guessTemplateID, "AwardID":playerAwardID}, False)
+        if ipyData:
+            awardItemList = ipyData.GetAwardItemList()
+            
+    return playerAwardID, awardItemList
+
+def DoMailReissueAward(zoneID, ipyData):
+    ## 邮件补发奖励 - 仅针对有参与资格的,无参与资格的玩家奖励暂不补发,如竞猜奖励
+    
+    if not ipyData:
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    if not zone.joinMemberDict:
+        return
+    
+    GameWorld.Log("仙盟攻城战邮件补发未领取奖励: zoneID=%s" % zoneID)
+    
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    for playerID in zone.joinMemberDict.keys():
+        if playerID < 10000:
+            continue
+        joinMember = zone.GetZoneJoinMember(playerID)
+        if not joinMember:
+            continue
+        familyID = joinMember.familyID
+        fmLV = joinMember.fmLV
+        
+        # 个人伤害名次奖励
+        if not joinMember.awardState&pow(2, AwwardType_PlayerHurt):
+            joinMember.awardState = joinMember.awardState | pow(2, AwwardType_PlayerHurt)
+            billObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZPlayerHurt, zoneID)
+            idRankDict = billObj.GetIDOrderDict()
+            billRank = idRankDict.get(playerID, 0)
+            billValue = None
+            awardTemplateID = ipyData.GetPersonalTemplateID()
+            playerAwardItemList = GameWorld.GetActBillboardTempAward(playerID, playerID, billRank, awardTemplateID, billValue, fmLV)
+            if playerAwardItemList:
+                paramList = [billRank]
+                PlayerCompensation.SendMailByKey("FamilyGCZAwardHurt", [playerID], playerAwardItemList, paramList, crossMail=True)
+                
+        # 仙盟积分名次奖励
+        if not joinMember.awardState&pow(2, AwwardType_FamilyScore):
+            joinMember.awardState = joinMember.awardState | pow(2, AwwardType_FamilyScore)
+            billObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
+            idRankDict = billObj.GetIDOrderDict()
+            billRank = idRankDict.get(familyID, 0)
+            billValue = None
+            awardTemplateID = ipyData.GetFamilyTemplateID()
+            playerAwardItemList = GameWorld.GetActBillboardTempAward(playerID, familyID, billRank, awardTemplateID, billValue, fmLV)
+            if playerAwardItemList:
+                paramList = [billRank]
+                PlayerCompensation.SendMailByKey("FamilyGCZAwardScore", [playerID], playerAwardItemList, paramList, crossMail=True)
+                
+        # 竞猜奖励
+        if not joinMember.awardState&pow(2, AwwardType_Guess):
+            joinMember.awardState = joinMember.awardState | pow(2, AwwardType_Guess)
+            awardTemplateID = ipyData.GetGuessTemplateID()
+            _, awardItemList = GetGuessAwardInfo(zoneID, None, awardTemplateID, playerID)
+            if awardItemList:
+                paramList = []
+                PlayerCompensation.SendMailByKey("FamilyGCZAwardGuess", [playerID], awardItemList, paramList, crossMail=True)
+                
+    return
+
+def Sync_FamilyGCZGuessInfo(zoneID, playerID=0, selectPlayerID=0):
+    ## 同步竞猜信息 - 需同步全服玩家,包含没有参与资格的
+    # @param selectPlayerID: 同步竞猜玩家ID竞猜记录
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    ipyDataInfo = actInfo.get(ShareDefine.ActKey_IpyDataInfo)
+    if not ipyDataInfo:
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    if not zone.familyGuessDict:
+        # 暂无竞猜名单
+        return
+    
+    onlinePlayerIDDict = {}
+    onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+    if not playerID:
+        for playerID, serverGroupID in onlineMgr.onlinePlayerDict.items():
+            pActInfo = GetPlayerActJoinInfo(playerID)
+            if zoneID != pActInfo[0]:
+                continue
+            onlinePlayerIDDict[playerID] = serverGroupID
+    else:
+        onlinePlayerIDDict = {playerID:onlineMgr.GetOLPlayerServerGroupID(playerID)}
+        
+    if not onlinePlayerIDDict:
+        return
+    
+    selectFamilyIDList = []
+    if selectPlayerID:
+        selectFamilyIDList = zone.playerGuessDict.get(selectPlayerID, [])
+        
+    guessTemplateID = ipyDataInfo.get("GuessTemplateID", 0)
+    finalFamilyIDList, statGuessRetDict = __statGuessFinal(zoneID, state, guessTemplateID)
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZGuessInfo()
+    clientPack.PlayerID = selectPlayerID
+    clientPack.SelectFamilyIDList = selectFamilyIDList
+    clientPack.SelectCnt = len(clientPack.SelectFamilyIDList)
+    
+    clientPack.FinalFamilyIDList = finalFamilyIDList
+    clientPack.FinalCnt = len(clientPack.FinalFamilyIDList)
+    
+    clientPack.RightInfoList = []
+    for awardID, rightPlayerIDList in statGuessRetDict.items():
+        right = ChPyNetSendPack.tagGCFamilyGCZGuessRight()
+        right.AwardID = awardID
+        right.RightPlayerCnt = len(rightPlayerIDList)
+        clientPack.RightInfoList.append(right)
+    clientPack.RightCnt = len(clientPack.RightInfoList)
+    
+    clientPack.GuessFamilyList = []
+    for familyID, guessValue in zone.familyGuessDict.items():
+        familyGuess = ChPyNetSendPack.tagGCFamilyGCZGuessFamily()
+        familyGuess.FamilyID = familyID
+        familyGuess.GuessValue = guessValue
+        clientPack.GuessFamilyList.append(familyGuess)
+    clientPack.FamilyCnt = len(clientPack.GuessFamilyList)
+    
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    return
+
+def __statGuessFinal(zoneID, state, guessTemplateID):
+    ## 统计竞猜最终结果
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    if zone.statGuessRetDict != None:
+        statGuessRetDict = zone.statGuessRetDict
+        finalFamilyIDList = zone.guessFinalFamilyIDList
+        return finalFamilyIDList, statGuessRetDict
+    
+    finalFamilyIDList = []
+    statGuessRetDict = {}
+    if state != None and state != FamilyGCZState_Award:
+        return finalFamilyIDList, statGuessRetDict
+    
+    guessCountMax = IpyGameDataPY.GetFuncCfg("FamilyGCZGuess", 3)
+    billboardMgr = PyDataManager.GetCrossBillboardManager()
+    scoreBillObj = billboardMgr.GetCrossBillboard(ShareDefine.Def_CBT_FamilyGCZScore, zoneID)
+    for index in range(guessCountMax):
+        billData = scoreBillObj.At(index)
+        finalFamilyIDList.append(billData.ID)
+        
+    # 统计竞猜结果
+    rightRankAwardIDDict = GameWorld.GetActGuessRightRankAwardIDDict(guessTemplateID)
+    GameWorld.Log("仙盟攻城战统计竞猜结果: zoneID=%s,guessTemplateID=%s,finalFamilyIDList=%s,%s" % (zoneID, guessTemplateID, finalFamilyIDList, rightRankAwardIDDict))
+    
+    statGuessRetDict = {}
+    for playerID, guessFamilyIDList in zone.playerGuessDict.items():
+        GameWorld.StatActGuessRet(playerID, guessFamilyIDList, finalFamilyIDList, rightRankAwardIDDict, statGuessRetDict, "FamilyGCZ")
+        
+    GameWorld.Log("    zoneID=%s,statGuessRetDict=%s" % (zoneID, statGuessRetDict))
+    zone.statGuessRetDict = statGuessRetDict
+    zone.guessFinalFamilyIDList = finalFamilyIDList
+    return finalFamilyIDList, statGuessRetDict
+
+def Sync_FamilyGCZActInfo(zoneID, playerID=0):
+    ## 同步活动时间信息 - 需同步全服玩家,包含没有参与资格的
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    if not actInfo.get(ShareDefine.ActKey_State):
+        return
+    ipyDataInfo = actInfo.get(ShareDefine.ActKey_IpyDataInfo)
+    if not ipyDataInfo:
+        return
+    
+    playerActInfo = {}
+    onlinePlayerIDDict = {}
+    onlineMgr = ChPlayer.GetOnlinePlayerMgr()
+    if not playerID:
+        for playerID, serverGroupID in onlineMgr.onlinePlayerDict.items():
+            pActInfo = GetPlayerActJoinInfo(playerID)
+            if zoneID != pActInfo[0]:
+                continue
+            playerActInfo[playerID] = pActInfo
+            onlinePlayerIDDict[playerID] = serverGroupID
+    else:
+        playerActInfo[playerID] = GetPlayerActJoinInfo(playerID)
+        onlinePlayerIDDict = {playerID:onlineMgr.GetOLPlayerServerGroupID(playerID)}
+        
+    if not onlinePlayerIDDict:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZActInfo()
+    clientPack.Clear()
+    clientPack.ServerIDRangeInfo = str(actInfo.get(ShareDefine.ActKey_ServerIDRangeList, []))
+    clientPack.ServerInfoLen = len(clientPack.ServerIDRangeInfo)
+    clientPack.ZoneID = zoneID
+    clientPack.ActID = actInfo.get(ShareDefine.ActKey_ID, 0)
+    clientPack.StartDate = ipyDataInfo.get("StartDate", "")
+    clientPack.EndtDate = ipyDataInfo.get("EndDate", "")
+    clientPack.JoinFamilyCnt = ipyDataInfo.get("JoinFamilyCnt", 0)
+    clientPack.ActFlowID = ipyDataInfo.get("ActFlowID", 0)
+    clientPack.GuessTemplateID = ipyDataInfo.get("GuessTemplateID", 0)
+    clientPack.PersonalTemplateID = ipyDataInfo.get("PersonalTemplateID", 0)
+    clientPack.FamilyTemplateID = ipyDataInfo.get("FamilyTemplateID", 0)
+    clientPack.StateError = actInfo.get(ShareDefine.ActKey_StateError, 0)
+    
+    clientPack.ActFamilyList = []
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    for familyID in zone.GetZoneLockFamilyIDList():
+        joinFamily = zone.GetZoneJoinFamily(familyID)
+        if not joinFamily:
+            continue
+        
+        actFamily = ChPyNetSendPack.tagGCFamilyGCZActFamily()
+        actFamily.FamilyID = familyID
+        actFamily.Name = joinFamily.familyName
+        actFamily.LV = joinFamily.lv
+        actFamily.ServerID = joinFamily.serverID
+        actFamily.EmblemID = joinFamily.emblemID
+        actFamily.FightPower = joinFamily.fightPowerTotal % ChConfig.Def_PerPointValue
+        actFamily.FightPowerEx = joinFamily.fightPowerTotal / ChConfig.Def_PerPointValue
+        actFamily.LeaderID = joinFamily.leaderID
+        actFamily.LeaderName = joinFamily.leaderName
+        
+        leaderMem = zone.GetZoneJoinMember(actFamily.LeaderID)
+        if leaderMem:
+            actFamily.LeaderFace = leaderMem.face
+            actFamily.LeaderFacePic = leaderMem.facePic
+            
+        clientPack.ActFamilyList.append(actFamily)    
+    clientPack.FamilyCount = len(clientPack.ActFamilyList)
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    
+    #同步场景、同步玩家攻击结果
+    #下发分区活动的时候,顺便下发玩家活动信息
+    msgInfo = {"zoneID":zoneID, "playerActInfo":playerActInfo}
+    Send_CrossServerMsg_FamilyGCZ("FamilyGCZPlayerActInfo", msgInfo, onlinePlayerIDDict.values())
+    return
+
+def Sync_FamilyGCZBatGroupInfo(zoneID, onlinePlayerIDDict=None):
+    ## 同步战场类型分组信息 - 只同步有参与资格的即可
+    
+    actInfo = CrossActionControl.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State)
+    if not state:
+        return
+    curRound, _ = GetRoundState(state)
+    if not curRound:
+        return
+    
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    if onlinePlayerIDDict == None:
+        onlinePlayerIDDict = zone.GetOLServerGroupIDDict()
+        
+    if not onlinePlayerIDDict:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZBatGroupInfo()
+    clientPack.RoundNum = curRound
+    clientPack.BatList = []
+    for batType, groupNumDict in zone.roundGroupDict.items():
+        batInfo = ChPyNetSendPack.tagGCFamilyGCZBat()
+        batInfo.BatType = batType
+        batInfo.GroupList = []
+        for groupNum in groupNumDict.keys():
+            batGroup = zone.GetBatGroup(batType, groupNum)
+            if not batGroup:
+                continue
+            group = ChPyNetSendPack.tagGCFamilyGCZBatGroup()
+            group.GroupNum = groupNum
+            group.FamilyIDList = batGroup.GetGroupFamilyIDList()
+            group.FamilyIDCnt = len(group.FamilyIDList)
+            batInfo.GroupList.append(group)
+        batInfo.GroupCnt = len(batInfo.GroupList)
+        
+        clientPack.BatList.append(batInfo)
+    clientPack.BatTypeCnt = len(clientPack.BatList)
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    return
+
+def Sync_FamilyGCZCampInfo(zoneID, familyID, onlinePlayerIDDict=None, defMemIDList=None):
+    ## 同步仙盟大本营信息 - 只同步某个锁定盟的锁定成员即可
+        
+    gczMgr = GetFamilyGCZMgr()
+    zone = gczMgr.GetActZone(zoneID)
+    joinFamily = zone.GetZoneJoinFamily(familyID)
+    if not joinFamily:
+        return
+    
+    if onlinePlayerIDDict == None:
+        onlinePlayerIDDict = joinFamily.GetOLMemServerGroupIDDict()
+        
+    #GameWorld.DebugLog("Sync_FamilyGCZCampInfo zoneID=%s,familyID=%s,%s,defMemIDList=%s" % (zoneID, familyID, onlinePlayerIDDict, defMemIDList))
+    if not onlinePlayerIDDict:
+        return
+    
+    clientPack = ChPyNetSendPack.tagGCFamilyGCZCampInfo()
+    clientPack.Clear()
+    clientPack.FamilyID = familyID
+    clientPack.Score = joinFamily.score
+    clientPack.CampLV = joinFamily.campLV
+    clientPack.CampExp = joinFamily.campExp
+    clientPack.CityLV = joinFamily.cityWall.cityLV
+    clientPack.HPBase = joinFamily.cityWall.hpBase % ChConfig.Def_PerPointValue
+    clientPack.HPBaseEx = joinFamily.cityWall.hpBase / ChConfig.Def_PerPointValue
+    clientPack.HPMax = joinFamily.cityWall.hpMax % ChConfig.Def_PerPointValue
+    clientPack.HPMaxEx = joinFamily.cityWall.hpMax / ChConfig.Def_PerPointValue
+    clientPack.HP = joinFamily.cityWall.hp % ChConfig.Def_PerPointValue
+    clientPack.HPEx = joinFamily.cityWall.hp / ChConfig.Def_PerPointValue
+    clientPack.DefMemList = []
+    for playerID in joinFamily.joinMemberIDList:                
+        if defMemIDList != None and playerID not in defMemIDList:
+            continue
+        joinMember = zone.GetZoneJoinMember(playerID)
+        if not joinMember:
+            continue
+        
+        defMem = ChPyNetSendPack.tagGCFamilyGCZCampMem()
+        defMem.PlayerID = playerID
+        defMem.Name = joinMember.playerName
+        defMem.FamilyLV = joinMember.fmLV
+        defMem.Face = joinMember.face
+        defMem.FacePic = joinMember.facePic
+        defMem.FightPower = joinMember.fightPower % ChConfig.Def_PerPointValue
+        defMem.FightPowerEx = joinMember.fightPower / ChConfig.Def_PerPointValue
+        defMem.HP = joinMember.hp % ChConfig.Def_PerPointValue
+        defMem.HPEx = joinMember.hp / ChConfig.Def_PerPointValue
+        defMem.HPMax = joinMember.hpMax % ChConfig.Def_PerPointValue
+        defMem.HPMaxEx = joinMember.hpMax / ChConfig.Def_PerPointValue
+        defMem.TotalHurt = joinMember.totalHurtValue % ChConfig.Def_PerPointValue
+        defMem.TotalHurtEx = joinMember.totalHurtValue / ChConfig.Def_PerPointValue
+        clientPack.DefMemList.append(defMem)
+        
+    clientPack.DefMemCnt = len(clientPack.DefMemList)
+    NetPackCommon.SendFakePackByCross(onlinePlayerIDDict, clientPack)
+    return
+
+## -------------------------------------------------------------------------------------------------
+def OnPlayerLogin(curPlayer):
+    PlayerControl.DoOfflineUnprocessed(curPlayer, "FamilyGCZAtkResult", __DoOfflineFamilyGCZAtkResult)
+    PlayerControl.DoOfflineUnprocessed(curPlayer, "FamilyGCZAward", __DoOfflineFamilyGCZAward)
+    return
+
+def ClientServer_CrossActInfo():
+    ## 子服处理 - 活动状态信息
+    return
+
+def MapServer_FamilyGCZ(curPlayer, msgList):
+    mapID = curPlayer.GetRealMapID()
+    playerID = curPlayer.GetPlayerID()
+    GameWorld.DebugLog("MapServer_FamilyGCZ mapID=%s,msgList=%s" % (mapID, msgList), playerID)
+    if not msgList:
+        return
+    
+    #msgType, dataMsg = msgList
+    ret = None
+    
+    if ret == None:
+        return
+    return msgList + (ret if isinstance(ret, list) else [ret])
+
+def MapServer_QueryPlayerResult(curPlayer, msgType, dataMsg=[]):
+    ret = 1
+    msgInfo = str([msgType, dataMsg, ret])
+    curPlayer.MapServer_QueryPlayerResult(0, 0, "FamilyGCZ", msgInfo, len(msgInfo))
+    return
+
+#// C0 26 仙盟攻城战查询 #tagCGFamilyGCZQuery
+#
+#struct    tagCGFamilyGCZQuery
+#{
+#    tagHead        Head;
+#    BYTE        QueryType;    //查询类型:1-成员捐献值;2-进入城池场景;3-退出城池场景;4-进入城池;5-退出城池;6-战报;7-分组仙盟成员伤害;
+#    BYTE        BatType;        //指定战场类型,需要发送的查询类型: 2、4
+#    BYTE        GroupNum;    //指定分组编号,需要发送的查询类型: 2、4
+#    DWORD        FamilyID;        //指定仙盟ID或城池ID,查自己盟的可不发,需要发的类型:2、4、6、7
+#};
+def OnFamilyGCZQuery(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    queryType = clientData.QueryType
+    batType = clientData.BatType
+    groupNum = clientData.GroupNum
+    familyID = clientData.FamilyID
+    
+    playerID = curPlayer.GetPlayerID()
+    dataMsg = {"ActMsgType":"Query", "playerID":playerID, "queryType":queryType, "batType":batType, "groupNum":groupNum, "familyID":familyID}
+    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+    return
+
+def CrossServerMsg_FamilyGCZ(dataMsg):
+    ## 收到跨服服务器同步的跨服运营活动状态
+    
+    actMsgType = dataMsg["ActMsgType"]
+    if actMsgType == "FamilyGCZPlayerActInfo":
+        __clientServer_FamilyGCZPlayerActInfo(dataMsg)
+        
+    elif actMsgType == "FamilyGCZAtkResult":
+        atkRet = dataMsg["atkRet"]
+        playerID = dataMsg["reqMsg"]["playerID"]
+        if not PlayerControl.GetDBPlayerAccIDByID(playerID):
+            return
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+        if not curPlayer:
+            if atkRet != AtkRet_OK:
+                # 不是攻击成功的,玩家不在线可以不处理
+                return
+            PlayerControl.AddOfflineUnprocessed(playerID, "FamilyGCZAtkResult", dataMsg)
+            return
+        MapServer_QueryPlayerResult(curPlayer, "FamilyGCZAtkResult", dataMsg)
+        
+    elif actMsgType == "FamilyGCZAward":
+        playerID = dataMsg["playerID"]
+        if not PlayerControl.GetDBPlayerAccIDByID(playerID):
+            return
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+        if not curPlayer:
+            PlayerControl.AddOfflineUnprocessed(playerID, "FamilyGCZAward", dataMsg)
+            return
+        MapServer_QueryPlayerResult(curPlayer, "FamilyGCZAward", dataMsg)
+        
+    return
+
+def __DoOfflineFamilyGCZAtkResult(curPlayer, recData, eventName, eventData):
+    ## 执行离线未处理的战斗结果
+    MapServer_QueryPlayerResult(curPlayer, "FamilyGCZAtkResult", eventData)
+    return
+
+def __DoOfflineFamilyGCZAward(curPlayer, recData, eventName, eventData):
+    ## 执行离线未处理的
+    MapServer_QueryPlayerResult(curPlayer, "FamilyGCZAward", eventData)
+    return
+
+def __clientServer_FamilyGCZPlayerActInfo(dataMsg):
+    playerActInfo = dataMsg["playerActInfo"]
+    
+    playerManager = GameWorld.GetPlayerManager()
+    for playerID, pActInfo in playerActInfo.items():
+        if not PlayerControl.GetDBPlayerAccIDByID(playerID):
+            # 非本服玩家
+            return
+        curPlayer = playerManager.FindPlayerByID(playerID)
+        if curPlayer:
+            MapServer_QueryPlayerResult(curPlayer, "FamilyGCZPlayerActInfo", pActInfo)
+            
+    return
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
index a7f3d39..451dbf4 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/CrossRealmMsg.py
@@ -18,9 +18,11 @@
 import GameWorld
 import ShareDefine
 import PlayerAssist
+import PlayerFamily
 import PlayerControl
 import PlayerPackData
 import PlayerFuncTeam
+import NetPackCommon
 import CrossLuckyCloudBuy
 import IPY_GameServer
 import PlayerViewCache
@@ -35,6 +37,7 @@
 import CrossActAllRecharge
 import CrossChampionship
 import CrossBattlefield
+import CrossFamilyGCZ
 import CrossBillboard
 import CrossYaomoBoss
 import GameWorldBoss
@@ -224,6 +227,12 @@
         elif msgType == ShareDefine.ClientServerMsg_LianqiScore:
             PlayerActLianqi.ClientServerMsg_LianqiScore(serverGroupID, msgData)
             
+        elif msgType == ShareDefine.ClientServerMsg_SyncFamilyInfo:
+            PlayerFamily.ClientServerMsg_SyncFamilyInfo(serverGroupID, msgData)
+            
+        elif msgType == ShareDefine.ClientServerMsg_FamilyGCZ:
+            CrossFamilyGCZ.ClientServerMsg_FamilyGCZ(serverGroupID, msgData)
+            
         elif msgType == ShareDefine.ClientServerMsg_CreateFuncTeam:
             PlayerFuncTeam.ClientServerMsg_CreateFuncTeam(serverGroupID, msgData)
             
@@ -405,6 +414,9 @@
         elif msgType == ShareDefine.CrossServerMsg_PlayerLoginout:
             ChPlayer.CrossServerMsg_PlayerLoginout(msgData)
             
+        elif msgType == ShareDefine.CrossServerMsg_SendFakePack:
+            NetPackCommon.CrossServerMsg_SendFakePack(msgData)
+            
         elif msgType == ShareDefine.CrossServerMsg_ChatCrossWorld:
             PlayerTalk.CrossServerMsg_ChatCrossWorld(msgData)
             
@@ -458,6 +470,12 @@
             
         elif msgType == ShareDefine.CrossServerMsg_FuncTeamList:
             PlayerFuncTeam.CrossServerMsg_FuncTeamList(msgData)
+            
+        elif msgType == ShareDefine.CrossServerMsg_FamilyDelRet:
+            PlayerFamily.CrossServerMsg_FamilyDelRet(msgData)
+            
+        elif msgType == ShareDefine.CrossServerMsg_FamilyGCZ:
+            CrossFamilyGCZ.CrossServerMsg_FamilyGCZ(msgData)
             
         elif msgType == ShareDefine.CrossServerMsg_Worship:
             GameWorship.CrossServerMsg_Worship(msgData)
@@ -636,5 +654,12 @@
         SendMsgToCrossServer(ShareDefine.ClientServerMsg_ServerInitOK, dataMsg)
     return
 
-
-    
\ No newline at end of file
+def OnCrossServerStateOpen():
+    '''子服收到跨服服务器通知  - 跨服服务器状态正常开放中,可能是启动、重连、或者维护后重新开放
+    这里可以处理一些子服与跨服正式建立常规逻辑通讯(注意与socket连接区分,这里指的是逻辑通讯)后的逻辑
+    注:跨服子服socket连接后,并不能正式开始逻辑消息通讯,需要等待双方都启动成功后才可以
+    即子服发送  ClientServerMsg_ServerInitOK 告知启动成功
+    然后跨服回复开启状态后才算正式建立常规逻辑通讯
+    '''
+    PlayerFamily.Sync_ClientFamilyAllToCrossServer()
+    return
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameRecData.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameRecData.py
index 8690970..f1bb836 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameRecData.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameRecData.py
@@ -58,7 +58,7 @@
     def SetValue7(self, value7): self.dbRecData.Value7 = value7
     def GetValue8(self): return self.dbRecData.Value8
     def SetValue8(self, value8): self.dbRecData.Value8 = value8
-    def __GetUserDataDict(self):
+    def GetUserDataDict(self):
         if self.__userDataDict == None:
             try:
                 self.__userDataDict = eval(self.dbRecData.UserData)
@@ -66,12 +66,12 @@
                 self.__userDataDict = {}
         return self.__userDataDict
     def GetUserDataByKey(self, key, defaultValue=None):
-        userDataDict = self.__GetUserDataDict()
+        userDataDict = self.GetUserDataDict()
         if key in userDataDict:
             return userDataDict[key]
         return defaultValue
     def SetUserDataByKey(self, key, value):
-        userDataDict = self.__GetUserDataDict()
+        userDataDict = self.GetUserDataDict()
         userDataDict[key] = value
         self.__userDataChange = True
         return
@@ -112,7 +112,7 @@
         return {"RecID":self.GetRecID(), "RecType":self.GetRecType(), "Time":self.GetTime(), 
                 "Value1":self.GetValue1(), "Value2":self.GetValue2(), "Value3":self.GetValue3(), "Value4":self.GetValue4(),
                 "Value5":self.GetValue5(), "Value6":self.GetValue6(), "Value7":self.GetValue7(), "Value8":self.GetValue8(), 
-                "UserDataDict":self.__GetUserDataDict()
+                "UserDataDict":self.GetUserDataDict()
                 }
     def SetAttr(self, attrDict, isClear=False):
         if isClear:
@@ -324,6 +324,33 @@
             matchRecDataList.append(recData)
         return matchRecDataList
     
+    def GetGameRecDataMatchEx(self, recType, valueList, findone=False):
+        '''获取记录类型对应的匹配value值记录
+        @param recType: 记录类型
+        @param valueList: [value1, value2, ...] value为None时不判断该值
+        @param findone: 是否只匹配一条满足的记录
+        @return: recData or [recData, ...] or None
+        '''
+        if recType not in self.recTypeDict:
+            return
+        recDataDict = self.recTypeDict[recType]
+        for recID in recDataDict.keys():
+            matchRet = self.GetGameRecDataMatch(recType, recID, valueList, findone)
+            if matchRet:
+                return matchRet
+        return
+    
+    def GetGameRecDataFirst(self, recType, recID, isAddNew):
+        ## 获取记录类型对应的第一条记录,适用于仅需一条的记录类型
+        # @param isAddNew: 没有记录时是否添加一条新记录,一般获取后需要更新数据的可以设置为True,仅判断用的建议设置为False,减少产生多余空数据
+        recData = None
+        recDataList = self.GetGameRecDataList(recType, recID)
+        if recDataList:
+            recData = recDataList[0]
+        elif isAddNew:
+            recData = self.AddGameRecData(recType, recID)
+        return recData
+    
     def GetGameRecDataList(self, recType, recID):
         ## 获取记录类型对应记录列表 [recData, ...]
         if recType not in self.recTypeDict:
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
index 54f790c..4cd71d1 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/GameWorldProcess.py
@@ -97,7 +97,6 @@
 import IpyGameDataPY
 import PlayerFamilyParty
 import PlayerFamilyZhenfa
-import PlayerFamilyEmblem
 import GameWorldFamilyWar
 import GameWorldArena
 import CrossLuckyCloudBuy
@@ -396,12 +395,8 @@
     AuctionHouse.OnAuctionItemMinuteProcess(tick)
     #福地
     GameWorldMineArea.OnProcessOnMinute()
-    #每5分钟触发一次仙盟总战力更新
-    if curMinute % 5 == 0:
-        PlayerFamily.UpdFamilyTotalFightPower()
-        PlayerFamilyRedPacket.CheckDelRedpacketData()
-        PlayerFamilyEmblem.CheckExpireEmblem()
-        
+    #仙盟
+    PlayerFamily.OnMinuteProcess(curMinute)        
     return
 
 def CheckServerHasPlayerLoginAfterInitOK():
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py b/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
index d5d148a..b711674 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/IpyGameDataPY.py
@@ -646,6 +646,54 @@
                         ("dict", "ScoreAwardEx", 0),
                         ),
 
+                "ActTimeFlow":(
+                        ("DWORD", "FlowID", 1),
+                        ("BYTE", "StartDay", 0),
+                        ("BYTE", "StartHour", 0),
+                        ("BYTE", "StartMinute", 0),
+                        ("BYTE", "EndDay", 0),
+                        ("BYTE", "EndHour", 0),
+                        ("BYTE", "EndMinute", 0),
+                        ("WORD", "StateValue", 0),
+                        ),
+
+                "ActBillboardAwardTemp":(
+                        ("DWORD", "TemplateID", 1),
+                        ("BYTE", "Rank", 0),
+                        ("DWORD", "NeedValue", 0),
+                        ("dict", "ValueAwardEx", 0),
+                        ("list", "AwardItemList", 0),
+                        ("list", "LeaderAwardItemList", 0),
+                        ("list", "EliteAwardItemList", 0),
+                        ),
+
+                "ActGuess":(
+                        ("DWORD", "TemplateID", 1),
+                        ("DWORD", "AwardID", 0),
+                        ("list", "RightRankList", 0),
+                        ("list", "AwardItemList", 0),
+                        ),
+
+                "CrossActFamilyGCZ":(
+                        ("DWORD", "CfgID", 1),
+                        ("char", "ActGroupName", 0),
+                        ("BYTE", "ZoneID", 0),
+                        ("list", "ServerIDRangeList", 0),
+                        ("char", "StartDate", 0),
+                        ("char", "EndDate", 0),
+                        ("BYTE", "JoinFamilyCnt", 0),
+                        ("WORD", "ActFlowID", 0),
+                        ("WORD", "GuessTemplateID", 0),
+                        ("WORD", "PersonalTemplateID", 0),
+                        ("WORD", "FamilyTemplateID", 0),
+                        ),
+
+                "CrossActFamilyGCZCampLV":(
+                        ("DWORD", "CampLV", 1),
+                        ("DWORD", "LVUPNeedExp", 0),
+                        ("DWORD", "AddHPPer", 0),
+                        ),
+
                 "ActXianXiaMJ":(
                         ("DWORD", "CfgID", 1),
                         ("list", "PlatformList", 0),
@@ -2141,6 +2189,79 @@
     def GetNeedScore(self): return self.attrTuple[3] # 上榜所需积分 DWORD
     def GetScoreAwardEx(self): return self.attrTuple[4] # 达标积分额外奖励 {积分:[[物品ID,个数,是否拍品], ...], ...} dict
 
+# 活动时间流程表
+class IPY_ActTimeFlow():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetFlowID(self): return self.attrTuple[0] # DWORD
+    def GetStartDay(self): return self.attrTuple[1] # 开始天 BYTE
+    def GetStartHour(self): return self.attrTuple[2] # 开始时 BYTE
+    def GetStartMinute(self): return self.attrTuple[3] # 开始分 BYTE
+    def GetEndDay(self): return self.attrTuple[4] # 结束天 BYTE
+    def GetEndHour(self): return self.attrTuple[5] # 结束时 BYTE
+    def GetEndMinute(self): return self.attrTuple[6] # 结束分 BYTE
+    def GetStateValue(self): return self.attrTuple[7] # 状态值 WORD
+
+# 活动榜单奖励模版表
+class IPY_ActBillboardAwardTemp():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetTemplateID(self): return self.attrTuple[0] # 模板编号 DWORD
+    def GetRank(self): return self.attrTuple[1] # 名次 BYTE
+    def GetNeedValue(self): return self.attrTuple[2] # 上榜所需值 DWORD
+    def GetValueAwardEx(self): return self.attrTuple[3] # 达标值额外奖励 {值:[[物品ID,个数,是否拍品], ...], ...} dict
+    def GetAwardItemList(self): return self.attrTuple[4] # 奖励物品列表[[物品ID,个数,是否拍品], ...] list
+    def GetLeaderAwardItemList(self): return self.attrTuple[5] # 仙盟榜盟主奖励物品信息列表[[物品ID,个数,是否拍品], ...] list
+    def GetEliteAwardItemList(self): return self.attrTuple[6] # 仙盟榜精英奖励物品信息列表[[物品ID,个数,是否拍品], ...] list
+
+# 活动竞猜表
+class IPY_ActGuess():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetTemplateID(self): return self.attrTuple[0] # 模板编号 DWORD
+    def GetAwardID(self): return self.attrTuple[1] # 奖励ID DWORD
+    def GetRightRankList(self): return self.attrTuple[2] # 猜对名次列表 list
+    def GetAwardItemList(self): return self.attrTuple[3] # 对应奖励列表 [[物品ID,个数,是否拍品], ...] list
+
+# 仙盟攻城战活动表
+class IPY_CrossActFamilyGCZ():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetCfgID(self): return self.attrTuple[0] # 配置ID DWORD
+    def GetActGroupName(self): return self.attrTuple[1] # 活动组名(同组活动的名字需相同) char
+    def GetZoneID(self): return self.attrTuple[2] # 组内分组编号 BYTE
+    def GetServerIDRangeList(self): return self.attrTuple[3] # 活动的服务器ID范围列表 [[serverIDA, serverIDB], ...] list
+    def GetStartDate(self): return self.attrTuple[4] # 开启日期 char
+    def GetEndDate(self): return self.attrTuple[5] # 结束日期 char
+    def GetJoinFamilyCnt(self): return self.attrTuple[6] # 参与仙盟数 BYTE
+    def GetActFlowID(self): return self.attrTuple[7] # 活动流程ID,对应H.活动时间流程表 WORD
+    def GetGuessTemplateID(self): return self.attrTuple[8] # 竞猜奖励模版,对应H.活动竞猜表 WORD
+    def GetPersonalTemplateID(self): return self.attrTuple[9] # 个人伤害排行奖励模版,对应H.活动榜单奖励模版表 WORD
+    def GetFamilyTemplateID(self): return self.attrTuple[10] # 仙盟积分排行奖励模版,对应H.活动榜单奖励模版表 WORD
+
+# 仙盟攻城战大本营等级表
+class IPY_CrossActFamilyGCZCampLV():
+    
+    def __init__(self):
+        self.attrTuple = None
+        return
+        
+    def GetCampLV(self): return self.attrTuple[0] # 大本营等级 DWORD
+    def GetLVUPNeedExp(self): return self.attrTuple[1] # 升下一级所需经验 DWORD
+    def GetAddHPPer(self): return self.attrTuple[2] # 生命累计总加成百分比 DWORD
+
 # 仙匣秘境活动时间表
 class IPY_ActXianXiaMJ():
     
@@ -3111,6 +3232,11 @@
         self.__LoadFileData("ActGubaoBillTemp", onlyCheck)
         self.__LoadFileData("CrossActLianqi", onlyCheck)
         self.__LoadFileData("ActLianqiBillTemp", onlyCheck)
+        self.__LoadFileData("ActTimeFlow", onlyCheck)
+        self.__LoadFileData("ActBillboardAwardTemp", onlyCheck)
+        self.__LoadFileData("ActGuess", onlyCheck)
+        self.__LoadFileData("CrossActFamilyGCZ", onlyCheck)
+        self.__LoadFileData("CrossActFamilyGCZCampLV", onlyCheck)
         self.__LoadFileData("ActXianXiaMJ", onlyCheck)
         self.__LoadFileData("CrossActXianXiaMJ", onlyCheck)
         self.__LoadFileData("ActXianXiaMJBillTemp", onlyCheck)
@@ -3795,6 +3921,41 @@
         self.CheckLoadData("ActLianqiBillTemp")
         return self.ipyActLianqiBillTempCache[index]
 
+    def GetActTimeFlowCount(self):
+        self.CheckLoadData("ActTimeFlow")
+        return self.ipyActTimeFlowLen
+    def GetActTimeFlowByIndex(self, index):
+        self.CheckLoadData("ActTimeFlow")
+        return self.ipyActTimeFlowCache[index]
+
+    def GetActBillboardAwardTempCount(self):
+        self.CheckLoadData("ActBillboardAwardTemp")
+        return self.ipyActBillboardAwardTempLen
+    def GetActBillboardAwardTempByIndex(self, index):
+        self.CheckLoadData("ActBillboardAwardTemp")
+        return self.ipyActBillboardAwardTempCache[index]
+
+    def GetActGuessCount(self):
+        self.CheckLoadData("ActGuess")
+        return self.ipyActGuessLen
+    def GetActGuessByIndex(self, index):
+        self.CheckLoadData("ActGuess")
+        return self.ipyActGuessCache[index]
+
+    def GetCrossActFamilyGCZCount(self):
+        self.CheckLoadData("CrossActFamilyGCZ")
+        return self.ipyCrossActFamilyGCZLen
+    def GetCrossActFamilyGCZByIndex(self, index):
+        self.CheckLoadData("CrossActFamilyGCZ")
+        return self.ipyCrossActFamilyGCZCache[index]
+
+    def GetCrossActFamilyGCZCampLVCount(self):
+        self.CheckLoadData("CrossActFamilyGCZCampLV")
+        return self.ipyCrossActFamilyGCZCampLVLen
+    def GetCrossActFamilyGCZCampLVByIndex(self, index):
+        self.CheckLoadData("CrossActFamilyGCZCampLV")
+        return self.ipyCrossActFamilyGCZCampLVCache[index]
+
     def GetActXianXiaMJCount(self):
         self.CheckLoadData("ActXianXiaMJ")
         return self.ipyActXianXiaMJLen
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py b/ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py
index 6ca8e2a..e354686 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/NetPackCommon.py
@@ -31,6 +31,8 @@
 import ChMapToGamePyPack
 import CrossRealmMsg
 import PlayerControl
+import ChPlayer
+import ShareDefine
 #-------------------------------------------------------------------------------
 #---全局变量---
 
@@ -241,6 +243,34 @@
         SendFakePack(curPlayer, clientPack)
     return
 
+def SendFakePackByCross(onlinePlayerIDDict, clientPack):
+    '''由跨服服务器直接给子服在线玩家发送封包,适用于接受的玩家封包数据一致的
+    @param onlinePlayerIDDict: 在线玩家GroupID字典 {playerID:groupID, ...}
+        功能自行通过 onlineMgr = ChPlayer.GetOnlinePlayerMgr() 过滤
+     onlineMgr.GetOLPlayerServerGroupID(playerID) 获取ServerGroupID
+    '''
+    if not onlinePlayerIDDict:
+        return
+    innerPackData = clientPack.GetBuffer()
+    dataMsg = {"playerIDList":onlinePlayerIDDict.keys(), "innerPackData":innerPackData}
+    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_SendFakePack, dataMsg, onlinePlayerIDDict.values())
+    return
+
+def CrossServerMsg_SendFakePack(msgData):
+    ## 子服收到由跨服直接发送给玩家的封包
+    playerIDList = msgData["playerIDList"]
+    innerPackData = msgData["innerPackData"]
+    
+    playerManager = GameWorld.GetPlayerManager()
+    for playerID in playerIDList:
+        curPlayer = playerManager.FindPlayerByID(playerID)
+        if curPlayer == None:
+            continue
+        if PlayerControl.GetIsTJG(curPlayer):
+            continue
+        curPlayer.Sync_GeneralPack(len(innerPackData), innerPackData)
+            
+    return
 #-------------------------------------------------------------------------------
 #---Py封包注册信息
 PyPackTable = ReadPyPackTable("PyNetPack")
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
index 5232ad5..a260ded 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/ChPlayer.py
@@ -81,6 +81,7 @@
 import PlayerRecData
 import GameWorship
 import GameXiangong
+import CrossFamilyGCZ
 #---------------------------------------------------------------------
 
 #---------------------------------------------------------------------
@@ -95,6 +96,8 @@
         return
     
     def IsOnline(self, playerID): return playerID in self.onlinePlayerDict
+    
+    def GetOLPlayerServerGroupID(self, playerID): return self.onlinePlayerDict.get(playerID, 0)
     
     def __SetOnline(self, playerID, serverGroupID):
         self.onlinePlayerDict[playerID] = serverGroupID
@@ -287,6 +290,8 @@
         CrossBattlefield.OnPlayerLogin(curPlayer)
         #跨服排位
         CrossChampionship.OnPlayerLogin(curPlayer, tick)
+        #仙盟攻城战
+        CrossFamilyGCZ.OnPlayerLogin(curPlayer)
         #跨服全民充值
         CrossActAllRecharge.OnPlayerLogin(curPlayer)
         #跨服妖魔boss
@@ -708,6 +713,8 @@
         CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_PlayerPackDataState, syncStateInfo, [serverGroupID])
         
         GameXiangong.OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID)
+        #仙盟攻城战
+        CrossFamilyGCZ.OnPlayerLogin_CrossLogic(serverGroupID, serverID, playerID)
         
     # 下线
     else:
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
index 7dbf112..d070849 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/CrossRealmPlayer.py
@@ -167,7 +167,9 @@
             if curPlayer == None:
                 continue
             PlayerControl.SetCrossMapID(curPlayer, 0)
-            
+    else:
+        CrossRealmMsg.OnCrossServerStateOpen()
+        
     GameWorld.GetGameWorld().SendCrossServerStateToLoginServer(isOpen)
     # 通知地图
     GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_CrossServerOpen, isOpen)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerDBGSEvent.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerDBGSEvent.py
index 674d42a..e103424 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerDBGSEvent.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerDBGSEvent.py
@@ -195,6 +195,8 @@
 Def_ActHorsePetTrainAwardC = "ActHorsePetTrainAwardC_%s"
 #跨服炼器结算状态,参数(zoneID)
 Def_ActLianqiAwardC = "ActLianqiAwardC_%s"
+#活动流程状态是否已经异常 - 按流程走的活动只能按状态顺序执行,不能跳状态,否则视为活动异常,参数(actName, zoneID),存储的值为异常的活动ID
+Def_ActFlowStateError = "SE%s%s"
 
 def SetInitOpenServerTime(initTime):
     openDatetime = GameWorld.ChangeTimeNumToDatetime(initTime)
@@ -268,7 +270,7 @@
 #  @param value 设置value
 #  @return 返回trig
 #  @remarks 函数详细说明.
-def SetDBGSTrig_ByKey( key , value ):
+def SetDBGSTrig_ByKey( key , value):
     gsEventTrigManager = GameWorld.GetGameWorld().GetDBGameServerEventTrigManager()
     trig = gsEventTrigManager.Find( key )
     
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
index d9af3a4..ff60117 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -44,6 +44,8 @@
 import PlayerFamilySWRH
 import PlayerViewCache
 import GameWorldBoss
+import CrossRealmMsg
+import CrossFamilyGCZ
 import AuctionHouse
 import PlayerAssist
 import PlayerTalk
@@ -61,6 +63,62 @@
 ImpeachLastTime  # 弹劾需要持续的时间
 ) = range(3)
 
+class FamilyMgr():
+    
+    def __init__(self):
+        self.sortFamilyIDList = [] #本服仙盟排序顺序 [familyID, ...]
+        self.fightPowerChangeFamilyIDList = [] # 仙盟成员战力有变更的仙盟ID [familyID, ...]
+        
+        #这里仅针对增改信息,删除的另外处理,因为删除的需要确保成功删除,所以需要入库未成功删除的记录
+        #而变更同步的会定时同步,每次重连服务器也会强制同步,所以不需要有成功回复
+        self.syncCrossFamilyDict = {} # 需要同步跨服的仙盟 {familyID:[需要同步的成员ID, ...], ...}
+        return
+    
+    def OnDeleteFamilyID(self, familyID):
+        if familyID in self.sortFamilyIDList:
+            self.sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
+        self.SetSyncCrossFamilyDel(familyID) # 解散仙盟
+        return
+    
+    def GetFamilyIDRank(self, familyID):
+        if familyID not in self.sortFamilyIDList:
+            return len(self.sortFamilyIDList) + 1
+        return self.sortFamilyIDList.index(familyID) + 1
+    
+    def AddFamilyIDToFightPowerChangeList(self, familyID, playerID=0):
+        if familyID not in self.fightPowerChangeFamilyIDList:
+            self.fightPowerChangeFamilyIDList.append(familyID)
+            GameWorld.DebugLog("仙盟战力变更待处理列表: fightPowerChangeFamilyIDList=%s" % self.fightPowerChangeFamilyIDList)
+        self.SetSyncCrossFamilyUpd(familyID, playerID) # 仙盟战力变更、成员战力变更
+        return
+    
+    def SetSyncCrossFamilyUpd(self, familyID, playerID=0, syncNow=False):
+        if familyID not in self.syncCrossFamilyDict:
+            self.syncCrossFamilyDict[familyID] = []
+        if playerID:
+            needSyncMemIDList = self.syncCrossFamilyDict[familyID]
+            if playerID not in needSyncMemIDList:
+                needSyncMemIDList.append(playerID)
+        # 变更数据是否立即同步跨服,否则等待定时同步即可
+        if syncNow:
+            Sync_ClientFamilyUpdToCrossServer()
+        return
+    
+    def SetSyncCrossFamilyDel(self, familyID, playerID=0):
+        ## 设置同步跨服服务器仙盟删除
+        # @param playerID: 如果有值代表仅成员删除
+        valueSetList = [playerID]
+        gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+        gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyDelSyncCross, familyID, valueSetList)
+        Sync_ClientFamilyDelToCrossServer() # 删除的立马同步
+        return
+    
+def GetFamilyMgr():
+    mgr = PyGameData.g_familyMgr
+    if not mgr:
+        mgr = FamilyMgr()
+        PyGameData.g_familyMgr = mgr
+    return mgr
 
 ## ------------------ 仙盟 ----------------------
 ## 仙盟联赛排名
@@ -71,6 +129,7 @@
 def SetFamilyTotalFightPower(curFamily, totalFightPower):
     curFamily.SetExtra4(totalFightPower / ChConfig.Def_PerPointValue)
     curFamily.SetExtra5(totalFightPower % ChConfig.Def_PerPointValue)
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 仙盟战力变更
     return
 def GetFamilyTotalFightPowerByID(familyID):
     family = GameWorld.GetFamilyManager().FindFamily(familyID)
@@ -79,7 +138,10 @@
     return GetFamilyTotalFightPower(family)
 # 徽章ID
 def GetFamilyEmblemID(curFamily): return curFamily.GetExtra6()
-def SetFamilyEmblemID(curFamily, emblemID): return curFamily.SetExtra6(emblemID)
+def SetFamilyEmblemID(curFamily, emblemID):
+    curFamily.SetExtra6(emblemID)
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 徽章变更
+    return
 
 # 公告修改次数
 def GetFamilyBroadcastCnt(curFamily): return curFamily.GetExtra3()
@@ -108,11 +170,17 @@
 
 def OnGameServerInitOK():
     ## 服务器启动成功处理
-    DoFamilySort()
+    if GameWorld.IsCrossServer():
+        pass
+    else:
+        DoFamilySort()
     return
 
 def OnMixServerInit():
     ## 合服后首次启动成功处理
+    
+    if GameWorld.IsCrossServer():
+        return
     
     # 仙盟联赛重置
     GameWorldFamilyWar.DoFamilyWarReset()
@@ -440,7 +508,7 @@
     #加入家族
     familyMember = curFamily.AddMember(jionPlayer)
     #刷新基本信息
-    RefreshFamilyMemberBaseMsg(familyMember, jionPlayer)
+    RefreshFamilyMemberBaseMsg(curFamily, familyMember, jionPlayer)
     
     #族长设置
     if jionFamilySetLv == IPY_GameServer.fmlLeader:
@@ -486,7 +554,7 @@
     #仙盟拍品
     AuctionHouse.Sync_FamilyAuctionItemInfo(jionPlayer, curFamily.GetID())
     SetMemberFightPower(familyMember, PlayerControl.GetFightPower(jionPlayer))
-    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
+    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), jionPlayer.GetPlayerID())
     
     #通知仙盟盛宴题目
     PlayerFamilyParty.NotifyFamilyPartyQuestion(jionPlayer)
@@ -678,7 +746,7 @@
 # @remarks 通知客户端服务器家族信息
 def Sync_AllFamilyInfo(curPlayer, viewPage, pageCnt=ChConfig.Def_ViewAllFamilyPageCount, sortRule=IPY_GameServer.fsrHornor):
     #familyCount = GameWorld.GetFamilyManager().GetCount()
-    familyCount = len(PyGameData.g_sortFamilyIDList)
+    familyCount = len(GetFamilyMgr().sortFamilyIDList)
     allPageCnt = GameWorld.GetIntUpper(familyCount, pageCnt)
 
     if allPageCnt != 0 and (viewPage < 0 or viewPage >= allPageCnt):
@@ -716,7 +784,8 @@
     return
 
 def Sync_PyAllFamilyInfo(curPlayer, allPageCnt, viewPage, startIndex, endIndex):
-    familyCount = len(PyGameData.g_sortFamilyIDList)
+    sortFamilyIDList = GetFamilyMgr().sortFamilyIDList
+    familyCount = len(sortFamilyIDList)
     if startIndex < 0 or endIndex >= familyCount:
         return
     
@@ -727,7 +796,7 @@
     familyViewPack.CurPage = viewPage
     familyViewPack.Family = []
     for i in xrange(startIndex, endIndex + 1):
-        familyID = PyGameData.g_sortFamilyIDList[i]
+        familyID = sortFamilyIDList[i]
         family = familyMgr.FindFamily(familyID)
         if not family:
             continue
@@ -785,7 +854,7 @@
     familyViewPack.TotalCount = 1
     #familyViewPack.CurPage = viewPage
     familyViewPack.Family = []
-    for i, familyID in enumerate(PyGameData.g_sortFamilyIDList):
+    for i, familyID in enumerate(GetFamilyMgr().sortFamilyIDList):
         family = familyMgr.FindFamily(familyID)
         if not family:
             continue
@@ -822,7 +891,7 @@
     familyViewPack.TotalCount = 1
     #familyViewPack.CurPage = viewPage
     familyViewPack.Family = []
-    for i, familyID in enumerate(PyGameData.g_sortFamilyIDList):
+    for i, familyID in enumerate(GetFamilyMgr().sortFamilyIDList):
         family = familyMgr.FindFamily(familyID)
         if not family:
             continue
@@ -908,6 +977,7 @@
             continue
         curPlayer.ChatMi(notifyPlayer, 1, pack.GetMsg(), 0, PlayerTalk.GetTalkExtraValue(curPlayer))
         PyDataManager.GetContactsManager().AddContactsBoth(curPlayer.GetID(), notifyPlayer.GetID())
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 公告变更
     return
 
 ## 检测目标玩家是否可以加入家族
@@ -1696,11 +1766,12 @@
     PlayerFamilyAction.DelFamilyOfficerModelEquip(curFamily.GetID(), leavePlayerID)
     # 玩家战盟名变更处理
     __OnFamilyNameChange(leavePlayerID, '')
-    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
+    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), leavePlayerID)
     PlayerViewCache.OnPlayerFamilyChange(leavePlayerID, 0, "")
     PlayerAssist.OnPlayerLeaveFamily(curFamily.GetID(), leavePlayerID, tagPlayer)
     if leavePlayerID in PyGameData.g_autoViceleaderDict.get(curFamily.GetID(),[]):
         PyGameData.g_autoViceleaderDict[curFamily.GetID()].remove(leavePlayerID)
+    GetFamilyMgr().SetSyncCrossFamilyDel(curFamily.GetID(), leavePlayerID) # 成员离开、踢出
     return
 
 #//////////////////////////////////////////////////////////////
@@ -1845,7 +1916,7 @@
     
     SetMemberFightPower(curMember, fightPower)
     GameWorld.DebugLog("仙盟成员战力变更 familyID=%s,fightPower=%s" % (familyID, fightPower), playerID)
-    AddFamilyIDToFightPowerChangeList(familyID)
+    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(familyID, playerID)
     return
 
 ## A4 07 升级家族#tagCGFamilyLVUp
@@ -1954,6 +2025,7 @@
     #世界服务器家族重新排序
     #GameWorld.GetFamilyManager().SortByLV()
     DoFamilySort() # 升级直接强排一次
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 仙盟等级变更
     return True
 
 #---------------------------------------------------------------------
@@ -2002,6 +2074,8 @@
 #  @remarks 函数详细说明.
 def OnPlayerChangeMap(curPlayer, tick):
     #同步给玩家, 最新的家族信息(家族等级刷新)
+    if GameWorld.IsCrossServer():
+        return
     curPlayer.MapServer_FamilyRefresh()
     return
 
@@ -2151,7 +2225,7 @@
 #  @param curPlayer 真实玩家
 #  @return None
 #  @remarks 刷新家族成员基本信息
-def RefreshFamilyMemberBaseMsg(curMember, curPlayer):
+def RefreshFamilyMemberBaseMsg(curFamily, curMember, curPlayer):
     curMember.SetName(curPlayer.GetName())
     curMember.SetLV(curPlayer.GetLV())
     curMember.SetReincarnationLv(curPlayer.GetReincarnationLv())
@@ -2160,6 +2234,7 @@
     curMember.SetOfficialRank(curPlayer.GetOfficialRank())
     curMember.SetFace(curPlayer.GetFace())
     curMember.SetFacePic(curPlayer.GetFacePic())
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), curPlayer.GetPlayerID()) # 成员基础信息刷新,含加入仙盟刷新
     return
 #---------------------------------------------------------------------
 ## 玩家刷新
@@ -2174,9 +2249,9 @@
     if curMember == None:
         return
     
-    RefreshFamilyMemberBaseMsg(curMember, curPlayer)
-    #家族长境界
     family = curPlayer.GetFamily()
+    RefreshFamilyMemberBaseMsg(family, curMember, curPlayer)
+    #家族长境界
     if family.GetLeaderID() == curPlayer.GetID():
         family.SetLeaderOfficialRank(curPlayer.GetOfficialRank())
     return
@@ -2292,8 +2367,7 @@
     
     #重新排序家族
     #GameWorld.GetFamilyManager().SortByLV()
-    if familyID in PyGameData.g_sortFamilyIDList:
-        PyGameData.g_sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
+    GetFamilyMgr().OnDeleteFamilyID(familyID)
         
     #家族科技删除, 改为地图直接处理, 暂屏蔽
     #PlayerFamilyTech.DelFamilyTechData(familyID)
@@ -2322,6 +2396,7 @@
     #设置族长权限
     ChangeFamilyMemberLv(familyMember, IPY_GameServer.fmlLeader)
     GameWorldFamilyWar.OnChangeFamilyLeader(curFamily.GetID(), familyMember.GetPlayerID())
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 盟主变更
     return
 #---------------------------------------------------------------------
 ##更改家族成员等级.
@@ -2353,6 +2428,8 @@
     # 变为普通成员,删除模型装备信息
     elif changeFamilyLV == IPY_GameServer.fmlMember:
         PlayerFamilyAction.DelFamilyOfficerModelEquip(familyMember.GetFamilyID(), familyMember.GetPlayerID())
+        
+    GetFamilyMgr().SetSyncCrossFamilyUpd(familyMember.GetFamilyID(), familyMember.GetPlayerID(), syncNow=True) # 成员职位变更
     return
 
 #---------------------------------------------------------------------
@@ -2531,6 +2608,7 @@
     #通知家族刷新
     curFamily.Broadcast_FamilyChange()
     
+    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamilyID, syncNow=True) # 仙盟改名
     playerManager = GameWorld.GetPlayerManager()
     
     #仙盟联赛
@@ -2711,6 +2789,8 @@
 # @return 返回值无意义
 # @remarks 家族过天
 def FamilyOnDay(tick):
+    if GameWorld.IsCrossServer():
+        return
     #---设置所有玩家可以再次加入家族---
     GameWorld.GetPlayerManager().ClearForbiddenEnterFamily()
     #---扣除地图上所有家族的维护费---
@@ -2769,6 +2849,8 @@
     return
 
 def FamilyOnDayEx(tick):
+    if GameWorld.IsCrossServer():
+        return
     familyManager = GameWorld.GetFamilyManager()
     for i in range(0, familyManager.GetCount()):
         family = familyManager.GetAt(i)
@@ -2784,7 +2866,8 @@
 # @return 返回值无意义
 # @remarks 家族过周
 def FamilyOnWeek(tick):
-    
+    if GameWorld.IsCrossServer():
+        return
     #---计算上周家族活跃度---
     familyManager = GameWorld.GetFamilyManager()
     for i in range(0, familyManager.GetCount()):
@@ -2816,6 +2899,8 @@
     return
 
 def FamilyOnHour():
+    if GameWorld.IsCrossServer():
+        return
     familyManager = GameWorld.GetFamilyManager()
     for i in xrange(familyManager.GetCount()):
         family = familyManager.GetAt(i)
@@ -2885,12 +2970,31 @@
             return cmp(GetMemberJoinTime(member1), GetMemberJoinTime(member2))
     return ret
 
+def SortCrossFamily(serverIDList, top=0):
+    ''' 跨服仙盟排序, 排序规则: 总战力  > 等级 > ID
+    @param serverIDList: 仙盟所属区服ID范围列表
+    @param top: 返回排序靠前x个仙盟,0则全部返回
+    '''
+    familyList = []
+    familyManager = GameWorld.GetFamilyManager()
+    for i in xrange(familyManager.GetCount()):
+        family = familyManager.GetAt(i)
+        serverID = family.GetServerID()
+        if not GameWorld.CheckServerIDInList(serverID, serverIDList):
+            continue
+        familyList.append(family)
+    familyList.sort(key=lambda f: (GetFamilyTotalFightPower(f), f.GetLV(), f.GetID()), reverse=True)
+    totalCnt = len(familyList)
+    return familyList[:top] if top else familyList, totalCnt
+    
 #---------------------------------------------------------------------
 ##通知地图服务器, 玩家家族属性刷新
 # @param curFamily 家族实例
 # @return 返回值无意义
 # @remarks IPY_MFamilyRefresh
 def SendPack_MapServer_PlayerFamilyRefresh(curFamily):
+    if GameWorld.IsCrossServer():
+        return
 #===============================================================================
 #    当家族以下权限变更时要通知地图服务器 IPY_MFamilyRefresh
 #    GetFamilyLV
@@ -3534,21 +3638,30 @@
             break
     return leaderLV
 
-def AddFamilyIDToFightPowerChangeList(familyID):
-    if familyID not in PyGameData.g_fightPowerChangeFamilyIDList:
-        PyGameData.g_fightPowerChangeFamilyIDList.append(familyID)
-        GameWorld.DebugLog("仙盟战力变更待处理列表: fightPowerChangeFamilyIDList=%s" % PyGameData.g_fightPowerChangeFamilyIDList)
+def OnMinuteProcess(curMinute):
+    if GameWorld.IsCrossServer():
+        return
+    
+    #每5分钟触发一次仙盟更新
+    if curMinute % 5 != 0:
+        return
+    
+    UpdFamilyTotalFightPower()
+    PlayerFamilyRedPacket.CheckDelRedpacketData()
+    PlayerFamilyEmblem.CheckExpireEmblem()
+    Sync_ClientFamilyUpdToCrossServer()
     return
 
 def UpdFamilyTotalFightPower():
     ## 更新仙盟总战力
-    if not PyGameData.g_fightPowerChangeFamilyIDList:
+    mgr = GetFamilyMgr()
+    if not mgr.fightPowerChangeFamilyIDList:
         #GameWorld.DebugLog("不需要更新仙盟总战力!")
         return
         
-    GameWorld.DebugLog("更新仙盟总战力 fightPowerChangeFamilyIDList=%s" % PyGameData.g_fightPowerChangeFamilyIDList)
+    GameWorld.DebugLog("更新仙盟总战力 fightPowerChangeFamilyIDList=%s" % mgr.fightPowerChangeFamilyIDList)
     familyManager = GameWorld.GetFamilyManager()
-    for familyID in PyGameData.g_fightPowerChangeFamilyIDList:    
+    for familyID in mgr.fightPowerChangeFamilyIDList:    
         family = familyManager.FindFamily(familyID)
         if not family:
             continue
@@ -3560,19 +3673,17 @@
         SetFamilyTotalFightPower(family, totalFightPower)
         GameWorld.DebugLog("    familyID=%s,totalFightPower=%s" % (familyID, totalFightPower))
         
-    PyGameData.g_fightPowerChangeFamilyIDList = []
+    mgr.fightPowerChangeFamilyIDList = []
     
     DoFamilySort(False) # 此处必须为False
     return True
 
-def GetSortFamilyIDList(): return PyGameData.g_sortFamilyIDList
+def GetSortFamilyIDList(): return GetFamilyMgr().sortFamilyIDList
 def GetFamilyIDRank(familyID):
     '''获取仙盟的排名, 注意与联赛排名区分
     每个仙盟一定有排名,但是不一定有联赛排名,联赛排名只是决定仙盟最终排名的一个比较因素
     '''
-    if familyID not in PyGameData.g_sortFamilyIDList:
-        return len(PyGameData.g_sortFamilyIDList) + 1
-    return PyGameData.g_sortFamilyIDList.index(familyID) + 1
+    return GetFamilyMgr().GetFamilyIDRank(familyID)
 
 def DoFamilySort(isUpdTotalFightPower=True):
     ''' 仙盟排序, 排序规则: 联赛评级 > 总战力 > 等级 > 创建时间
@@ -3590,12 +3701,13 @@
         familyList.append(family)
     familyList.sort(cmp=CmpFamilySort)
     
-    PyGameData.g_sortFamilyIDList = []
+    mgr = GetFamilyMgr()
+    mgr.sortFamilyIDList = []
     for i, family in enumerate(familyList, 1):
         GameWorld.DebugLog("    i=%s,warRank=%s,fightPower=%s,LV=%s,CreateTime=%s,familyID=%s" 
                            % (i, GetFamilyWarRank(family), GetFamilyTotalFightPower(family), family.GetLV(), family.GetCreateTime(), family.GetID()))
-        PyGameData.g_sortFamilyIDList.append(family.GetID())
-    GameWorld.DebugLog("    sortFamilyIDList=%s" % PyGameData.g_sortFamilyIDList)
+        mgr.sortFamilyIDList.append(family.GetID())
+    GameWorld.DebugLog("    sortFamilyIDList=%s" % mgr.sortFamilyIDList)
     return
 
 def CmpFamilySort(family1, family2):
@@ -3836,6 +3948,17 @@
 
 ##--------------------------------------------------------------------------------------------------
 
+def CrossServerMsg_FamilyDelRet(msgData):
+    ## 跨服仙盟删除结果,有收到结果即代表成功
+    familyID = msgData["familyID"]
+    playerID = msgData.get("playerID", 0)
+    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+    if playerID:
+        gameRecMgr.DelGameRecDataByTypeValue(ShareDefine.Def_GameRecType_FamilyDelSyncCross, [playerID], familyID)
+    else:
+        gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyDelSyncCross, familyID)
+    return
+
 def GetFamilyBillboardInfo(curFamily):
     ## 获取仙盟榜单信息 区服ID、徽章、仙盟名、盟主名、仙盟总战力、仙盟等级
     familyID = curFamily.GetID()
@@ -3851,3 +3974,222 @@
     return {"id":familyID, "name":name, "id2":id2, "name2":name2, "value1":value1, "value2":value2, 
             "value3":value3, "value4":value4, "value5":value5}
 
+def Sync_ClientFamilyUpdToCrossServer():
+    ## 定时同步仙盟变更数据到跨服服务器
+    
+    Sync_ClientFamilyDelToCrossServer() # 防止未删除成功,这里补通知
+    
+    mgr = GetFamilyMgr()
+    if not mgr.syncCrossFamilyDict:
+        return
+    
+    familyManager = GameWorld.GetFamilyManager()
+    for familyID, updMemIDList in mgr.syncCrossFamilyDict.items():
+        family = familyManager.FindFamily(familyID)
+        if not family:
+            continue
+        Send_ClientServerMsg_SyncFamilyInfo("FamilyUpd", GetSyncCrossServerFamilyInfo(family, updMemIDList))
+        
+    mgr.syncCrossFamilyDict = {}
+    return
+
+def Sync_ClientFamilyAllToCrossServer():
+    ## 同步子服所有仙盟信息到跨服
+    
+    if GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_SyncFamilyAllToCross):
+        return
+    GameWorld.GetGameWorld().SetDict(ChConfig.Def_WorldKey_SyncFamilyAllToCross, 1)
+    GameWorld.Log("开始同步本服所有仙盟到跨服服务器!")
+    
+    Sync_ClientFamilyDelToCrossServer() # 防止未删除成功,这里补通知
+    
+    familyManager = GameWorld.GetFamilyManager()
+    for i in xrange(familyManager.GetCount()):
+        family = familyManager.GetAt(i)
+        if not family:
+            continue
+        Send_ClientServerMsg_SyncFamilyInfo("FamilyUpd", GetSyncCrossServerFamilyInfo(family))
+    return
+
+def Sync_ClientFamilyDelToCrossServer():
+    ## 同步仙盟删除数据到跨服服务器
+    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
+    recDataDict = gameRecMgr.GetGameRecDataDict(ShareDefine.Def_GameRecType_FamilyDelSyncCross)
+    for familyID, recDataList in recDataDict.items():
+        for recData in recDataList:
+            playerID = recData.GetValue1()
+            Send_ClientServerMsg_SyncFamilyInfo("FamilyDel", {"familyID":familyID, "playerID":playerID})
+    return
+
+def GetCrossFamilyBaseInfo(family):
+    return {"ID": family.GetID(), "Name": family.GetName(), "LV":family.GetLV(), "FightPower":GetFamilyTotalFightPower(family), 
+            "LeaderID": family.GetLeaderID(), "LeaderName": family.GetLeaderName(), "EmblemID":GetFamilyEmblemID(family), 
+            "ServerID":family.GetServerID(), "Broadcast":family.GetBroadcast()}
+    
+def GetCrossFamilyMemInfo(member):
+    return {"Name":member.GetName(), "LV":member.GetLV(), "Job":member.GetJob(), "OfficialRank":member.GetOfficialRank(), 
+            "Face":member.GetFace(), "FacePic":member.GetFacePic(), "FamilyLV":member.GetFamilyLV(), "FightPower":GetMemberFightPower(member)}
+    
+def GetSyncCrossServerFamilyInfo(family, memIDList=None):
+    ## 获取仙盟跨服所需信息
+    # @param memIDList: 需要获取成员信息ID列表,不传则取所有成员
+    
+    crossFamilyInfo = GetCrossFamilyBaseInfo(family)
+    
+    memAll = True if memIDList == None else False
+    memInfo = {}
+    for m in xrange(family.GetCount()):
+        member = family.GetAt(m)
+        memID = member.GetPlayerID()
+        if not memID:
+            continue
+        if memIDList and memID not in memIDList:
+            continue
+        memInfo[memID] = GetCrossFamilyMemInfo(member)
+        
+    crossFamilyInfo.update({"memInfo":memInfo, "memAll":memAll})
+    return crossFamilyInfo
+
+def Send_ClientServerMsg_SyncFamilyInfo(syncType, syncInfo):
+    dataMsg = {"syncType":syncType, "syncInfo":syncInfo}
+    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_SyncFamilyInfo, dataMsg)
+    return
+
+def ClientServerMsg_SyncFamilyInfo(serverGroupID, msgData):
+    ## 收到子服 - 仙盟信息
+    
+    syncType = msgData["syncType"]
+    syncInfo = msgData["syncInfo"]
+    
+    if syncType == "FamilyUpd":
+        __CrossServer_FamilyUpd(syncInfo)
+        
+    elif syncType == "FamilyDel":
+        __CrossServer_FamilyDel(serverGroupID, syncInfo)
+        
+    return
+
+def __CrossServer_FamilyDel(serverGroupID, syncInfo):
+    ## 跨服服务器删除子服仙盟、成员
+    familyID = syncInfo["familyID"]
+    playerID = syncInfo.get("playerID", 0)
+    if playerID:
+        curFamily = GameWorld.GetFamilyManager().FindFamily(familyID)
+        if curFamily:
+            curFamily.DeleteMember(playerID)
+    else:
+        #删除家族 
+        GameWorld.GetFamilyManager().DelFamily(familyID)
+        #删除家族行为数据
+        PlayerFamilyAction.ClearFamilyAction(familyID)
+        
+    # 直接回复就是删除成功
+    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyDelRet, syncInfo, [serverGroupID])
+    return
+
+def __CrossServer_FamilyUpd(familyInfo):
+    ## 跨服服务器更新子服仙盟 - 这里只覆盖更新,不考虑删除的情况
+    
+    familyID = familyInfo["ID"]
+    familyName = familyInfo["Name"]
+    curFamily = FindAndFixCrossFamilyByIDName(familyID, familyName)
+    if not curFamily:
+        GameWorld.ErrLog("跨服更新仙盟失败! 仙盟不存在或者创建失败! familyID=%s" % familyID)
+        return
+    
+    #curFamily.SetName(familyInfo["Name"]) # 这里不再更新名称
+    curFamily.SetLV(familyInfo["LV"])
+    SetFamilyTotalFightPower(curFamily, familyInfo["FightPower"])
+    curFamily.SetLeaderID(familyInfo["LeaderID"])
+    curFamily.SetLeaderName(familyInfo["LeaderName"])
+    SetFamilyEmblemID(curFamily, familyInfo["EmblemID"])
+    curFamily.SetServerID(familyInfo["ServerID"])
+    curFamily.SetBroadcast(familyInfo["Broadcast"])
+    
+    updMemIDList = []
+    memAll = familyInfo.get("memAll", False)
+    memInfoDict = familyInfo.get("memInfo", {})
+    if memInfoDict:
+        for m in range(curFamily.GetCount())[::-1]:
+            member = curFamily.GetAt(m)
+            memID = member.GetPlayerID()
+            if not memID:
+                continue
+            if memID not in memInfoDict:
+                if memAll:
+                    curFamily.DeleteMember(memID)
+                continue
+            memInfo = memInfoDict.pop(memID)
+            __updCrossFamilyMemberInfo(curFamily, member, memInfo)
+            updMemIDList.append(memID)
+            
+        # 剩下的就是新增的成员
+        for memID, memInfo in memInfoDict.items():
+            member = curFamily.AddMemberEx(memID)
+            if not member:
+                continue
+            __updCrossFamilyMemberInfo(curFamily, member, memInfo)
+            updMemIDList.append(memID)
+            
+    # 相关活动数据更新
+    CrossFamilyGCZ.OnCrossJoinFamilyMemberUpd(curFamily, updMemIDList)
+    return
+
+def FindAndFixCrossFamilyByIDName(familyID, familyName):
+    ## 按仙盟名及ID查找跨服仙盟,重名时系统会自动修改仙盟名
+    
+    familyManager = GameWorld.GetFamilyManager()
+    curFamily = familyManager.FindFamily(familyID)
+    if not curFamily:
+        for i in range(100):
+            crossFamilyName = familyName if i == 0 else ("%s_%s" % (familyName, i))
+            curFamily = familyManager.AddFamilyEx(crossFamilyName, familyID)
+            if curFamily:
+                GameWorld.DebugLog("跨服添加新仙盟! familyID=%s, i=%s" % (familyID, i))
+                break
+        return curFamily
+    
+    # 验证当前名字是否还是唯一的,因为延迟同步的原因,仙盟可能改名时在本服唯一,但是在跨服不唯一
+    nameFamily = familyManager.FindFamilyByName(familyName)
+    if not nameFamily:
+        GameWorld.DebugLog("新同步的名字已经不存在仙盟了,可直接替换新仙盟名! familyID=%s" % familyID)
+        curFamily.SetName(familyName)
+        return curFamily
+    
+    if nameFamily.GetID() == familyID:
+        #GameWorld.DebugLog("还是自己原来的仙盟名,直接返回! familyID=%s" % familyID)
+        return curFamily
+    
+    # 尝试修改仙盟名,如果没有唯一名,则不修改保留最后一次同步的仙盟名,至少最后一次的名字还是唯一的
+    for i in range(1, 100):
+        fixFamilyName = "%s_%s" % (familyName, i)
+        family = familyManager.FindFamilyByName(fixFamilyName)
+        if family:
+            if family.GetID() == familyID:
+                #GameWorld.DebugLog("保留原系统修改的仙盟名! familyID=%s" % familyID)
+                break
+            # 已存在该仙盟名,且不是自己的仙盟
+            continue
+        curFamily.SetName(fixFamilyName)
+        GameWorld.Log("跨服强制修改仙盟名: familyID=%s,%s,%s" % (familyID, i, fixFamilyName))
+        break
+    
+    return curFamily
+
+def __updCrossFamilyMemberInfo(curFamily, member, memInfo):
+    member.SetName(memInfo.get("Name", ""))
+    member.SetLV(memInfo.get("LV", 1))
+    member.SetJob(memInfo.get("Job", 1))
+    member.SetOfficialRank(memInfo.get("OfficialRank", 1))
+    member.SetFace(memInfo.get("Face", 0))
+    member.SetFacePic(memInfo.get("FacePic", 0))
+    SetMemberFightPower(member, memInfo.get("FightPower", 0))
+    memFamilyLV = memInfo.get("FamilyLV", 0)
+    member.SetFamilyLV(memFamilyLV)
+    if memFamilyLV == IPY_GameServer.fmlLeader:
+        curFamily.SetLeaderID(member.GetPlayerID())
+        curFamily.SetLeaderName(member.GetName())
+        curFamily.SetLeaderOfficialRank(member.GetOfficialRank())
+        
+    return
+
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
index a4c6352..c892648 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerQuery.py
@@ -77,6 +77,7 @@
 import CrossChampionship
 import CrossBattlefield
 import CrossActAllRecharge
+import CrossFamilyGCZ
 import PlayerActGubao
 import PlayerActHorsePetTrain
 import PlayerActLianqi
@@ -1052,6 +1053,16 @@
             return
         resultName = '%s' % ret
         
+    # 仙盟攻城战
+    if callName == "FamilyGCZ":
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
+        if not curPlayer:
+            return
+        ret = CrossFamilyGCZ.MapServer_FamilyGCZ(curPlayer, eval(resultName))
+        if ret == None:
+            return
+        resultName = '%s' % ret
+        
     # 仙盟珍宝阁
     if callName =="FamilyZhenbaoge":
         curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(srcPlayerID)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
index dcadebf..e087772 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -132,10 +132,14 @@
     
     PlayerPackData.DelOutofTimePackData()
     
+    onlineMgr = ChPlayer.GetOnlinePlayerMgr()
     pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
     playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
     for playerID, viewCache in playerViewCachePyDict.items():
         if IsSaveDBViewCache(viewCache):
+            continue
+        if onlineMgr.IsOnline(playerID):
+            #在线的先不删除
             continue
         playerViewCachePyDict.pop(playerID)
         
@@ -234,24 +238,28 @@
 
 def GetSyncCrossCacheBase(curPlayer):
     ## 获取同步跨服基础查看缓存,主要用于个别功能需要提前先同步玩家基础缓存到跨服,因为跨服不一定有玩家缓存,需要提前同步
-    playerID = curPlayer.GetPlayerID()
+    if isinstance(curPlayer, int):
+        playerID = curPlayer
+        curPlayer = None
+    else:
+        playerID = curPlayer.GetPlayerID()
     cacheDict = GetCachePropDataDict(FindViewCache(playerID))
     cacheBase = {
-                 "AccID":curPlayer.GetAccID(),
-                 "LV":curPlayer.GetLV(),
-                 "RealmLV":curPlayer.GetOfficialRank(),
-                 "Job":curPlayer.GetJob(),
-                 "VIPLV":curPlayer.GetVIPLv(),
-                 "Name":CrossRealmPlayer.GetCrossPlayerName(curPlayer),
-                 "Face":curPlayer.GetFace(),
-                 "FacePic":curPlayer.GetFacePic(),
-                 "FamilyID":curPlayer.GetFamilyID(),
+                 "AccID":curPlayer.GetAccID() if curPlayer else cacheDict.get("AccID", ""),
+                 "LV":curPlayer.GetLV() if curPlayer else cacheDict.get("LV", 1),
+                 "RealmLV":curPlayer.GetOfficialRank() if curPlayer else cacheDict.get("RealmLV", 1),
+                 "Job":curPlayer.GetJob() if curPlayer else cacheDict.get("Job", 1),
+                 "VIPLV":curPlayer.GetVIPLv() if curPlayer else cacheDict.get("VIPLV", 0),
+                 "Name":curPlayer.GetName() if curPlayer else cacheDict.get("Name", ""), # 此处不用跨服名称,如前端需要展示跨服名称,可通过ServerID或AccID取得ServerID展示
+                 "Face":curPlayer.GetFace() if curPlayer else cacheDict.get("Face", 0),
+                 "FacePic":curPlayer.GetFacePic() if curPlayer else cacheDict.get("FacePic", 0),
+                 "FamilyID":curPlayer.GetFamilyID() if curPlayer else cacheDict.get("FacmilyID", 0),
                  "FamilyName":cacheDict.get("FamilyName", ""),
                  "TitleID":cacheDict.get("TitleID", 0),
-                 "FightPower":PlayerControl.GetFightPower(curPlayer),
+                 "FightPower":PlayerControl.GetFightPower(curPlayer) if curPlayer else cacheDict.get("FightPower", 0),
                  "EquipShowSwitch":cacheDict.get("EquipShowSwitch", 0),
                  "EquipShowID":cacheDict.get("EquipShowID", []),
-                 "ServerGroupID":PlayerControl.GetPlayerServerGroupID(curPlayer),
+                 "ServerGroupID":PlayerControl.GetPlayerServerGroupID(curPlayer) if curPlayer else cacheDict.get("ServerGroupID", GameWorld.GetServerGroupID()),
                  }
     return cacheBase
 
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py b/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
index 32db39d..c2e882c 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyDataManager.py
@@ -15,6 +15,7 @@
 import GameWorld
 import PlayerSocial
 import CrossBillboard
+import CrossFamilyGCZ
 import PlayerFamilyStore
 import PlayerCompensation
 import PlayerBourse
@@ -39,6 +40,10 @@
 import time
 
 def GetSavePyData():
+    
+    #存储数据前,一些功能业务数据先转化为存档数据
+    CrossFamilyGCZ.OnSavePyData()
+    
     pyGameDataMgr = GetPyGameDataManager()
     result = pyGameDataMgr.GetSaveData()
     GameWorld.Log("GetSavePyData!! id = %s-%s"%(id(pyGameDataMgr), len(result)))
@@ -52,7 +57,12 @@
 def LoadPyGameData(gameBuffer, pos):
     pyGameDataMgr = GetPyGameDataManager()
     GameWorld.Log("LoadPyGameData!!id = %s %s"%(id(pyGameDataMgr), len(gameBuffer)))
-    return pyGameDataMgr.LoadGameData(gameBuffer, pos)
+    pos = pyGameDataMgr.LoadGameData(gameBuffer, pos)
+    
+    #加载数据后,一些功能转化为功能业务数据
+    CrossFamilyGCZ.OnLoadPyData()
+    
+    return pos
 
 #协助感谢表
 class PlayerAssistThanksPyManager(object):
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
index 5c20298..0aef8b5 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameData.py
@@ -58,12 +58,13 @@
 #请求列表 玩家的AddRequest废弃
 g_playerRequests = {}
 
-g_fightPowerChangeFamilyIDList = [] #仙盟成员战力有变更的仙盟ID [familyID, ...]
-g_sortFamilyIDList = [] #仙盟排序顺序 [familyID, ...]
+g_familyMgr = None # 仙盟管理
 g_familyWarFightingIDList = [] # 仙盟联赛中正在战斗中的仙盟ID列表 [familyID, ...]
 g_familyWarFamilyIDList = [] # 仙盟联赛参赛仙盟ID列表 [familyID, ...]
 g_familyWarMemDict = {} # 仙盟联赛参赛人员名单 {playerID:memRecData, ...}
 
+g_familyGCZMgr = None
+
 #boss刷新排序列表
 g_sortBOSSRefreshList = []
 g_swrhJoinRecord = [] #守护人皇家族今日参加记录(已正常结算)[familyID]
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py b/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
index 809da18..d28397e 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/PyGameDataStruct.py
@@ -813,8 +813,8 @@
 class tagDBCrossBillboard(Structure):
     _pack_ = 1
     _fields_ = [
-        ('GroupValue1', ctypes.c_ubyte),
-        ('GroupValue2', ctypes.c_ubyte),
+        ('GroupValue1', ctypes.c_ulong),
+        ('GroupValue2', ctypes.c_ulong),
         ('BillboardType', ctypes.c_ubyte),
         ('ID', ctypes.c_ulong),
         ('ID2', ctypes.c_ulong),
@@ -870,8 +870,8 @@
         if len(buf) < pos + self.getLength():
             return -1
         self.clear()
-        self.GroupValue1, pos = CommFunc.ReadBYTE(buf, pos)
-        self.GroupValue2, pos = CommFunc.ReadBYTE(buf, pos)
+        self.GroupValue1, pos = CommFunc.ReadDWORD(buf, pos)
+        self.GroupValue2, pos = CommFunc.ReadDWORD(buf, pos)
         self.BillboardType, pos = CommFunc.ReadBYTE(buf, pos)
         self.ID, pos = CommFunc.ReadDWORD(buf, pos)
         self.ID2, pos = CommFunc.ReadDWORD(buf, pos)
@@ -896,8 +896,8 @@
 
     def getBuffer(self):
         buf = ''
-        buf = CommFunc.WriteBYTE(buf, self.GroupValue1)
-        buf = CommFunc.WriteBYTE(buf, self.GroupValue2)
+        buf = CommFunc.WriteDWORD(buf, self.GroupValue1)
+        buf = CommFunc.WriteDWORD(buf, self.GroupValue2)
         buf = CommFunc.WriteBYTE(buf, self.BillboardType)
         buf = CommFunc.WriteDWORD(buf, self.ID)
         buf = CommFunc.WriteDWORD(buf, self.ID2)
@@ -921,8 +921,8 @@
 
     def getLength(self):
         length = 0
-        length += sizeof(ctypes.c_ubyte)
-        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_ulong)
+        length += sizeof(ctypes.c_ulong)
         length += sizeof(ctypes.c_ubyte)
         length += sizeof(ctypes.c_ulong)
         length += sizeof(ctypes.c_ulong)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
index 25a3322..39d2b8e 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ShareDefine.py
@@ -364,10 +364,11 @@
 CrossActName_Gubao = "CrossActGubao" # 古宝养成 - 跨服
 CrossActName_HorsePetTrain = "CrossActHorsePetTrain" # 骑宠养成 - 跨服
 CrossActName_Lianqi = "CrossActLianqi" # 炼器 - 跨服
+CrossActName_FamilyGCZ = "CrossActFamilyGCZ" # 仙盟攻城战
 
 #跨服运营活动列表
 CrossActNameList = [CrossActName_CTGBillboard, CrossActName_AllRecharge, CrossActName_LuckyCloudBuy, CrossActName_BossTrial, 
-                    CrossActName_XianXiaMJ, CrossActName_Gubao, CrossActName_HorsePetTrain, CrossActName_Lianqi]
+                    CrossActName_XianXiaMJ, CrossActName_Gubao, CrossActName_HorsePetTrain, CrossActName_Lianqi, CrossActName_FamilyGCZ]
 #需要锁定活动分区分配直到活动结束的跨服运营活动,即使热更分区配置,也不会改变正在活动中的分区设定,直到活动结束
 CrossActLockServerGroupIDList = [CrossActName_CTGBillboard, CrossActName_AllRecharge]
 
@@ -375,6 +376,7 @@
 ActKey_ID = "ID" # 活动ID,唯一标识的ID,一般是活动开启的time值
 ActKey_State = "State" # 活动状态 0-未开启, >0开启中,也代表当日的第几个时间段
 ActKey_StateJoin = "StateJoin" # 活动某些功能可参与状态 0-还不可参与, >0可参与,一般可参与时该状态等于state
+ActKey_StateError = "StateError" # 按流程走的活动状态是否已异常
 ActKey_CfgID = "CfgID" # 活动表配置ID
 ActKey_ActNum = "ActNum" # 活动分组编号
 ActKey_DayIndex = "DayIndex" # 当前活动天索引,0开始,代表第1天
@@ -917,7 +919,10 @@
 Def_CBT_HorsePetTrainScore, # 骑宠养成积分 - 个人榜  164
 Def_CBT_CrossRealmPK, # 跨服PK竞技场  165
 Def_CBT_LianqiScore, # 炼器积分 - 个人榜  166
-) = range(150, 166 + 1)
+Def_CBT_FamilyGCZScore, # 仙盟攻城战 - 仙盟积分总榜  167 (zoneID, 0)
+Def_CBT_FamilyGCZPlayerHurt, # 仙盟攻城战 - 玩家伤害总榜  168 (zoneID, 0)
+Def_CBT_FamilyGCZRoundHurt, # 仙盟攻城战 - 本轮分组仙盟伤害榜  169 (zoneID, batType*100+groupNum)
+) = range(150, 169 + 1)
 
 #职业对应战力排行榜类型
 JobFightPowerBillboardDict = {
@@ -1435,7 +1440,12 @@
 #通用信息记录类型 - 新 从 300 开始,原通用记录类型最大到255
 Def_GameRecTypeList = (
                        Def_GameRecType_Xiangong, # 仙宫记录 300
-                       ) = range(300, 1 + 300)
+                       Def_GameRecType_FamilyDelSyncCross, # 仙盟删除同步跨服状态本服记录, familyID 301
+                       Def_GameRecType_FamilyGCZMgr, # 仙盟攻城战公共管理信息记录, zoneID 302
+                       Def_GameRecType_FamilyGCZJoinFamily, # 仙盟攻城战参与仙盟信息, zoneID 303
+                       Def_GameRecType_FamilyGCZJoinMember, # 仙盟攻城战参与成员信息, zoneID 304
+                       Def_GameRecType_FamilyGCZCityWall, # 仙盟攻城战城池信息, zoneID 305
+                       ) = range(300, 1 + 305)
 #通用信息记录新 - 字典key配置,如果有配置,则可额外按对应记录Value值存储字典,方便快速取值,可配置Value编号 1~8,配空默认 Value1
 Def_GameRecValueKeyDict = {
                            Def_GameRecType_Xiangong:[1],
@@ -1640,6 +1650,7 @@
 CrossServerMsg_CrossServerState = "CrossServerState"    # 跨服服务器状态变更
 CrossServerMsg_PlayerLoginout = "PlayerLoginout"        # 玩家上下线状态同步
 CrossServerMsg_ExitCrossServer = "ExitCrossServer"      # 退出跨服服务器
+CrossServerMsg_SendFakePack = "SendFakePack"            # 给子服玩家发送封包
 CrossServerMsg_Notify = "Notify"                        # 提示信息
 CrossServerMsg_ChatCrossWorld = "ChatCrossWorld"        # 跨服世界聊天
 CrossServerMsg_ViewPlayerCacheRet = "ViewPlayerCacheRet"# 查看跨服玩家信息结果
@@ -1693,6 +1704,8 @@
 CrossServerMsg_FuncTeamInfo = "FuncTeamInfo"  # 功能队伍信息同步
 CrossServerMsg_FuncTeamDel = "FuncTeamDel"  # 功能队伍删除同步
 CrossServerMsg_FuncTeamList = "FuncTeamList"  # 功能队伍列表同步
+CrossServerMsg_FamilyDelRet = "FamilyDelRet"  # 仙盟删除结果
+CrossServerMsg_FamilyGCZ = "FamilyGCZ"  # 仙盟攻城战
 
 # 子服发送跨服信息定义
 ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1742,6 +1755,8 @@
 ClientServerMsg_HorsePetTrainScore = "HorsePetTrainScore" # 骑宠养成积分
 ClientServerMsg_QueryXiangong = "QueryXiangong" # 查看仙宫仙名录
 ClientServerMsg_LianqiScore = "LianqiScore" # 炼器积分
+ClientServerMsg_SyncFamilyInfo = "SyncFamilyInfo" # 仙盟信息同步
+ClientServerMsg_FamilyGCZ = "FamilyGCZ" # 仙盟攻城战
 
 #跨服广播类型定义
 CrossNotify_CrossAct = "CrossAct"
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index 4296654..99d11ac 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -2012,3 +2012,23 @@
 PacketCMD_4=0xB0
 PacketSubCMD_4=0x35
 PacketCallFunc_4=OnMineHouseKeeperFreeUse
+
+;仙盟攻城战
+[PlayerActFamilyGCZ]
+ScriptName = Player\PlayerActFamilyGCZ.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 3
+
+PacketCMD_1=0xC1
+PacketSubCMD_1=0x24
+PacketCallFunc_1=OnFamilyGCZContribution
+
+PacketCMD_2=0xC1
+PacketSubCMD_2=0x25
+PacketCallFunc_2=OnFamilyGCZAtk
+
+PacketCMD_3=0xC1
+PacketSubCMD_3=0x26
+PacketCallFunc_3=OnFamilyGCZGuess
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 4d0d5d4..5687fe6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -2327,6 +2327,7 @@
                          1000 * 20,                        # 仙盟充值互助
                          1000 * 20,                        # 仙盟珍宝阁
                          300,                              # 砍树
+                         1000 * 20,                        # 仙盟攻城战
                          ]
 TYPE_Player_Tick_Count = len(TYPE_Player_Tick_Time) 
 
@@ -2411,6 +2412,7 @@
 TYPE_Player_Tick_FamilyCTGAssist,        #仙盟充值互助
 TYPE_Player_Tick_FamilyZhenbaoge,        #仙盟珍宝阁
 TYPE_Player_Tick_CutTree,        #砍树
+TYPE_Player_Tick_FamilyGCZ,        #仙盟攻城战
 ) = range(0, TYPE_Player_Tick_Count)
 
 #---------------------------------------------------------------------
@@ -4245,6 +4247,16 @@
 #购买次数礼包活动
 Def_PDict_BuyCountGiftID = "BuyCountGiftID_%s"  # 玩家身上的活动ID,唯一标识,取活动开始日期time,参数(活动编号)
 Def_PDict_BuyCountGiftAward = "BuyCountGiftAward_%s"  # 礼包奖励记录,按位记录是否已领取,参数(活动编号)
+
+#仙盟攻城战
+Def_PDict_FamilyGCZID = "FamilyGCZID"  # 玩家身上的活动ID,唯一标识,取活动开始日期time值
+Def_PDict_FamilyGCZZoneID = "FamilyGCZZoneID"  # 参与的分区ID
+Def_PDict_FamilyGCZFamilyID = "FamilyGCZFamilyID"  # 参与的仙盟ID
+Def_PDict_FamilyGCZRoundNum = "FamilyGCZRoundNum"  # 记录的轮次
+Def_PDict_FamilyGCZContributionCnt = "FamilyGCZContributionCnt"  # 轮次低级捐献次数
+Def_PDict_FamilyGCZEnergy = "FamilyGCZEnergy"  # 进攻剩余体力
+Def_PDict_FamilyGCZEnergyTime = "FamilyGCZEnergyTime"  # 上次恢复体力时间戳,为0时不再恢复
+Def_PDict_FamilyGCZAwardState = "FamilyGCZAwardState"  # 活动奖励领取状态,按二进制位判断是否已领取,0-竞猜奖励;1-个人排行奖励;2-仙盟排名奖励;
 #-------------------------------------------------------------------------------
 
 #开服活动,Def_PDictType_OpenServerCampaign
@@ -6178,7 +6190,9 @@
 Def_RewardType_OpenServerDailyAward, # 开服每日奖励 76
 Def_RewardType_TreasureCntAward, # 寻宝累计次数奖励 77
 Def_RewardType_LunhuidianAward, # 轮回殿奖励 78
-)= range(79)
+Def_RewardType_RechargeDayAward, # 累充每日奖励 79
+Def_RewardType_FamilyGCZ, # 仙盟攻城战 80
+)= range(81)
 
 #boss复活相关活动定义
 BossRebornActIDList = (
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index 2163321..dec4787 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -5089,6 +5089,70 @@
 
 
 #------------------------------------------------------
+# C0 26 仙盟攻城战查询 #tagCGFamilyGCZQuery
+
+class  tagCGFamilyGCZQuery(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("QueryType", c_ubyte),    #查询类型:1-成员捐献值;2-进入城池场景;3-退出城池场景;4-进入城池;5-退出城池;6-战报;7-分组仙盟成员伤害;
+                  ("BatType", c_ubyte),    #指定战场类型,需要发送的查询类型: 2、4
+                  ("GroupNum", c_ubyte),    #指定分组编号,需要发送的查询类型: 2、4
+                  ("FamilyID", c_int),    #指定仙盟ID或城池ID,查自己盟的可不发,需要发的类型:2、4、6、7
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC0
+        self.SubCmd = 0x26
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC0
+        self.SubCmd = 0x26
+        self.QueryType = 0
+        self.BatType = 0
+        self.GroupNum = 0
+        self.FamilyID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCGFamilyGCZQuery)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 26 仙盟攻城战查询 //tagCGFamilyGCZQuery:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                QueryType:%d,
+                                BatType:%d,
+                                GroupNum:%d,
+                                FamilyID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.QueryType,
+                                self.BatType,
+                                self.GroupNum,
+                                self.FamilyID
+                                )
+        return DumpString
+
+
+m_NAtagCGFamilyGCZQuery=tagCGFamilyGCZQuery()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCGFamilyGCZQuery.Cmd,m_NAtagCGFamilyGCZQuery.SubCmd))] = m_NAtagCGFamilyGCZQuery
+
+
+#------------------------------------------------------
 # C0 03 强制退出跨服状态 #tagCGForceQuitCrossState
 
 class  tagCGForceQuitCrossState(Structure):
@@ -5245,8 +5309,8 @@
                   ("Cmd", c_ubyte),
                   ("SubCmd", c_ubyte),
                   ("Type", c_ubyte),    #榜单类型
-                  ("GroupValue1", c_ubyte),    # 分组值1
-                  ("GroupValue2", c_ubyte),    # 分组值2,与分组值1组合归为同组榜单数据
+                  ("GroupValue1", c_int),    # 分组值1
+                  ("GroupValue2", c_int),    # 分组值2,与分组值1组合归为同组榜单数据
                   ("StartIndex", c_int),    #查看的起始名次索引, 默认0
                   ("WatchCnt", c_ubyte),    #查看条数,默认20,最大不超过100
                   ("WatchID", c_int),    #查看指定ID名次前后,如玩家ID、家族ID等
@@ -23599,6 +23663,189 @@
 
 
 #------------------------------------------------------
+# C1 25 仙盟攻城战攻击 #tagCMFamilyGCZAtk
+
+class  tagCMFamilyGCZAtk(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("AtkType", c_ubyte),    #攻击类型: 1-普通单攻;2-技能单攻;3-技能群攻;
+                  ("TagCityID", c_int),    #目标城池ID,一般是仙盟ID或者特殊城池ID如修罗城城池,普攻单攻需指定目标,群攻技能发0
+                  ("TagGuardID", c_int),    #目标守卫ID,一般是玩家ID或者特殊守卫ID如修罗城守卫,普攻单攻需指定目标,技能攻击发0
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x25
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x25
+        self.AtkType = 0
+        self.TagCityID = 0
+        self.TagGuardID = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMFamilyGCZAtk)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 25 仙盟攻城战攻击 //tagCMFamilyGCZAtk:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                AtkType:%d,
+                                TagCityID:%d,
+                                TagGuardID:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.AtkType,
+                                self.TagCityID,
+                                self.TagGuardID
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZAtk=tagCMFamilyGCZAtk()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZAtk.Cmd,m_NAtagCMFamilyGCZAtk.SubCmd))] = m_NAtagCMFamilyGCZAtk
+
+
+#------------------------------------------------------
+# C1 24 仙盟攻城战捐献 #tagCMFamilyGCZContribution
+
+class  tagCMFamilyGCZContribution(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ContributionType", c_ubyte),    #捐献类型: 0-低级;1-高级
+                  ("UseCount", c_int),    #物品捐献时使用个数
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x24
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x24
+        self.ContributionType = 0
+        self.UseCount = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagCMFamilyGCZContribution)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 24 仙盟攻城战捐献 //tagCMFamilyGCZContribution:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ContributionType:%d,
+                                UseCount:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ContributionType,
+                                self.UseCount
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZContribution=tagCMFamilyGCZContribution()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZContribution.Cmd,m_NAtagCMFamilyGCZContribution.SubCmd))] = m_NAtagCMFamilyGCZContribution
+
+
+#------------------------------------------------------
+# C1 26 仙盟攻城战竞猜 #tagCMFamilyGCZGuess
+
+class  tagCMFamilyGCZGuess(Structure):
+    Head = tagHead()
+    SelectCnt = 0    #(BYTE SelectCnt)
+    SelectFamilyIDList = list()    #(vector<DWORD> SelectFamilyIDList)// 竞猜选择的仙盟ID排名顺序
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC1
+        self.Head.SubCmd = 0x26
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.SelectCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.SelectCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.SelectFamilyIDList.append(value)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC1
+        self.Head.SubCmd = 0x26
+        self.SelectCnt = 0
+        self.SelectFamilyIDList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 4 * self.SelectCnt
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.SelectCnt)
+        for i in range(self.SelectCnt):
+            data = CommFunc.WriteDWORD(data, self.SelectFamilyIDList[i])
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                SelectCnt:%d,
+                                SelectFamilyIDList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.SelectCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagCMFamilyGCZGuess=tagCMFamilyGCZGuess()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMFamilyGCZGuess.Head.Cmd,m_NAtagCMFamilyGCZGuess.Head.SubCmd))] = m_NAtagCMFamilyGCZGuess
+
+
+#------------------------------------------------------
 # C1 10 幸运云购购买 #tagCMLuckyCloudBuy
 
 class  tagCMLuckyCloudBuy(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
index 9e7d65d..58ed11c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -17027,8 +17027,8 @@
 class  tagGCCrossBillboardInfo(Structure):
     Head = tagHead()
     Type = 0    #(BYTE Type)//榜单类型
-    GroupValue1 = 0    #(BYTE GroupValue1)// 分组值1
-    GroupValue2 = 0    #(BYTE GroupValue2)// 分组值2,与分组值1组合归为同组榜单数据
+    GroupValue1 = 0    #(DWORD GroupValue1)// 分组值1
+    GroupValue2 = 0    #(DWORD GroupValue2)// 分组值2,与分组值1组合归为同组榜单数据
     WatchID = 0    #(DWORD WatchID)//查看指定ID名次前后,如玩家ID、家族ID等
     BillboardCount = 0    #(BYTE BillboardCount)
     CrossBillboardDataList = list()    #(vector<tagGCCrossBillboardData> CrossBillboardDataList)
@@ -17044,8 +17044,8 @@
         self.Clear()
         _pos = self.Head.ReadData(_lpData, _pos)
         self.Type,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.GroupValue1,_pos = CommFunc.ReadBYTE(_lpData, _pos)
-        self.GroupValue2,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupValue1,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.GroupValue2,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.WatchID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
         self.BillboardCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
         for i in range(self.BillboardCount):
@@ -17071,8 +17071,8 @@
         length = 0
         length += self.Head.GetLength()
         length += 1
-        length += 1
-        length += 1
+        length += 4
+        length += 4
         length += 4
         length += 1
         for i in range(self.BillboardCount):
@@ -17084,8 +17084,8 @@
         data = ''
         data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
         data = CommFunc.WriteBYTE(data, self.Type)
-        data = CommFunc.WriteBYTE(data, self.GroupValue1)
-        data = CommFunc.WriteBYTE(data, self.GroupValue2)
+        data = CommFunc.WriteDWORD(data, self.GroupValue1)
+        data = CommFunc.WriteDWORD(data, self.GroupValue2)
         data = CommFunc.WriteDWORD(data, self.WatchID)
         data = CommFunc.WriteBYTE(data, self.BillboardCount)
         for i in range(self.BillboardCount):
@@ -18518,6 +18518,1750 @@
 
 m_NAtagGCCrossZoneInfo=tagGCCrossZoneInfo()
 ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCCrossZoneInfo.Cmd,m_NAtagGCCrossZoneInfo.SubCmd))] = m_NAtagGCCrossZoneInfo
+
+
+#------------------------------------------------------
+# C0 24 仙盟攻城战活动信息 #tagGCFamilyGCZActInfo
+
+class  tagGCFamilyGCZActFamily(Structure):
+    FamilyID = 0    #(DWORD FamilyID)
+    Name = ""    #(char Name[33])//参与仙盟名字
+    LV = 0    #(BYTE LV)//仙盟等级
+    ServerID = 0    #(DWORD ServerID)//仙盟所属区服ID
+    EmblemID = 0    #(DWORD EmblemID)//徽章ID
+    FightPower = 0    #(DWORD FightPower)//仙盟总战力,求余亿部分
+    FightPowerEx = 0    #(DWORD FightPowerEx)//仙盟总战力,整除亿部分
+    LeaderID = 0    #(DWORD LeaderID)//盟主ID
+    LeaderName = ""    #(char LeaderName[33])//盟主名
+    LeaderFace = 0    #(DWORD LeaderFace)
+    LeaderFacePic = 0    #(DWORD LeaderFacePic)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.LV,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ServerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.EmblemID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderName,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.LeaderFace,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.LeaderFacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.FamilyID = 0
+        self.Name = ""
+        self.LV = 0
+        self.ServerID = 0
+        self.EmblemID = 0
+        self.FightPower = 0
+        self.FightPowerEx = 0
+        self.LeaderID = 0
+        self.LeaderName = ""
+        self.LeaderFace = 0
+        self.LeaderFacePic = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteBYTE(data, self.LV)
+        data = CommFunc.WriteDWORD(data, self.ServerID)
+        data = CommFunc.WriteDWORD(data, self.EmblemID)
+        data = CommFunc.WriteDWORD(data, self.FightPower)
+        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
+        data = CommFunc.WriteDWORD(data, self.LeaderID)
+        data = CommFunc.WriteString(data, 33, self.LeaderName)
+        data = CommFunc.WriteDWORD(data, self.LeaderFace)
+        data = CommFunc.WriteDWORD(data, self.LeaderFacePic)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                FamilyID:%d,
+                                Name:%s,
+                                LV:%d,
+                                ServerID:%d,
+                                EmblemID:%d,
+                                FightPower:%d,
+                                FightPowerEx:%d,
+                                LeaderID:%d,
+                                LeaderName:%s,
+                                LeaderFace:%d,
+                                LeaderFacePic:%d
+                                '''\
+                                %(
+                                self.FamilyID,
+                                self.Name,
+                                self.LV,
+                                self.ServerID,
+                                self.EmblemID,
+                                self.FightPower,
+                                self.FightPowerEx,
+                                self.LeaderID,
+                                self.LeaderName,
+                                self.LeaderFace,
+                                self.LeaderFacePic
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZActInfo(Structure):
+    Head = tagHead()
+    ServerInfoLen = 0    #(BYTE ServerInfoLen)
+    ServerIDRangeInfo = ""    #(String ServerIDRangeInfo)//开放该活动的服务器ID范围列表,json格式 [[IDA, IDB], ...], [] 为全服
+    ZoneID = 0    #(BYTE ZoneID)// 活动分区ID,公示期为0
+    ActID = 0    #(DWORD ActID)// 活动ID,代表某一次活动的唯一ID,前端如果有活动相关的本地记录可以通过验证此ID变更进行重置
+    StartDate = ""    #(char StartDate[10])// 开始日期 y-m-d
+    EndtDate = ""    #(char EndtDate[10])// 结束日期 y-m-d
+    JoinFamilyCnt = 0    #(BYTE JoinFamilyCnt)// 参与仙盟数
+    ActFlowID = 0    #(WORD ActFlowID)// 活动流程ID,对应H.活动时间流程表中的编号,前端根据跨服时间自行计算当前所处流程状态
+    GuessTemplateID = 0    #(WORD GuessTemplateID)// 竞猜奖励模版,对应H.活动竞猜表,前端自行读表展示
+    PersonalTemplateID = 0    #(WORD PersonalTemplateID)// 个人伤害排行奖励模版,对应H.活动榜单奖励模版表,前端自行读表展示
+    FamilyTemplateID = 0    #(WORD FamilyTemplateID)// 仙盟积分排行奖励模版,对应H.活动榜单奖励模版表,前端自行读表展示
+    StateError = 0    #(BYTE StateError)// 活动流程状态是否异常,如果不为0代表活动已异常,前端自行决定是不显示活动还是活动页面做提示
+    FamilyCount = 0    #(BYTE FamilyCount)
+    ActFamilyList = list()    #(vector<tagGCFamilyGCZActFamily> ActFamilyList)//本分区参与的仙盟名单
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x24
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.ServerInfoLen,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ServerIDRangeInfo,_pos = CommFunc.ReadString(_lpData, _pos,self.ServerInfoLen)
+        self.ZoneID,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ActID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.StartDate,_pos = CommFunc.ReadString(_lpData, _pos,10)
+        self.EndtDate,_pos = CommFunc.ReadString(_lpData, _pos,10)
+        self.JoinFamilyCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.ActFlowID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.GuessTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.PersonalTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.FamilyTemplateID,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.StateError,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.FamilyCount,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyCount):
+            temActFamilyList = tagGCFamilyGCZActFamily()
+            _pos = temActFamilyList.ReadData(_lpData, _pos)
+            self.ActFamilyList.append(temActFamilyList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x24
+        self.ServerInfoLen = 0
+        self.ServerIDRangeInfo = ""
+        self.ZoneID = 0
+        self.ActID = 0
+        self.StartDate = ""
+        self.EndtDate = ""
+        self.JoinFamilyCnt = 0
+        self.ActFlowID = 0
+        self.GuessTemplateID = 0
+        self.PersonalTemplateID = 0
+        self.FamilyTemplateID = 0
+        self.StateError = 0
+        self.FamilyCount = 0
+        self.ActFamilyList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += len(self.ServerIDRangeInfo)
+        length += 1
+        length += 4
+        length += 10
+        length += 10
+        length += 1
+        length += 2
+        length += 2
+        length += 2
+        length += 2
+        length += 1
+        length += 1
+        for i in range(self.FamilyCount):
+            length += self.ActFamilyList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.ServerInfoLen)
+        data = CommFunc.WriteString(data, self.ServerInfoLen, self.ServerIDRangeInfo)
+        data = CommFunc.WriteBYTE(data, self.ZoneID)
+        data = CommFunc.WriteDWORD(data, self.ActID)
+        data = CommFunc.WriteString(data, 10, self.StartDate)
+        data = CommFunc.WriteString(data, 10, self.EndtDate)
+        data = CommFunc.WriteBYTE(data, self.JoinFamilyCnt)
+        data = CommFunc.WriteWORD(data, self.ActFlowID)
+        data = CommFunc.WriteWORD(data, self.GuessTemplateID)
+        data = CommFunc.WriteWORD(data, self.PersonalTemplateID)
+        data = CommFunc.WriteWORD(data, self.FamilyTemplateID)
+        data = CommFunc.WriteBYTE(data, self.StateError)
+        data = CommFunc.WriteBYTE(data, self.FamilyCount)
+        for i in range(self.FamilyCount):
+            data = CommFunc.WriteString(data, self.ActFamilyList[i].GetLength(), self.ActFamilyList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                ServerInfoLen:%d,
+                                ServerIDRangeInfo:%s,
+                                ZoneID:%d,
+                                ActID:%d,
+                                StartDate:%s,
+                                EndtDate:%s,
+                                JoinFamilyCnt:%d,
+                                ActFlowID:%d,
+                                GuessTemplateID:%d,
+                                PersonalTemplateID:%d,
+                                FamilyTemplateID:%d,
+                                StateError:%d,
+                                FamilyCount:%d,
+                                ActFamilyList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.ServerInfoLen,
+                                self.ServerIDRangeInfo,
+                                self.ZoneID,
+                                self.ActID,
+                                self.StartDate,
+                                self.EndtDate,
+                                self.JoinFamilyCnt,
+                                self.ActFlowID,
+                                self.GuessTemplateID,
+                                self.PersonalTemplateID,
+                                self.FamilyTemplateID,
+                                self.StateError,
+                                self.FamilyCount,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZActInfo=tagGCFamilyGCZActInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZActInfo.Head.Cmd,m_NAtagGCFamilyGCZActInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZActInfo
+
+
+#------------------------------------------------------
+# C0 29 仙盟攻城战城池战斗信息 #tagGCFamilyGCZBatCityInfo
+
+class  tagGCFamilyGCZBatCityInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("CityID", c_int),    #城池ID
+                  ("CityLV", c_ubyte),    #城池等级
+                  ("FamilyID", c_int),    #所属仙盟ID,可能为0
+                  ("GuardID", c_int),    #当前防守成员ID,为0时表示没有防守成员了,城池已被击毁
+                  ("HP", c_int),    #防守成员剩余生命,求余亿部分,剩余生命为0时代表该防守成员被击败
+                  ("HPEx", c_int),    #防守成员剩余生命,整除亿部分
+                  ("HPMax", c_int),    #防守成员最大生命,求余亿部分
+                  ("HPMaxEx", c_int),    #防守成员最大生命,整除亿部分
+                  ("AtkPlayerID", c_int),    #发起攻击的玩家ID,可能为0,判断是否自己的ID进行相应的攻击表现
+                  ("AtkRet", c_ubyte),    #攻击结果,仅在攻击玩家ID不为0时有效,0--成功;1-目标已被击杀;2-城池已被摧毁;3-其他
+                  ("KillCnt", c_ubyte),    #本次攻击击杀防守人员数
+                  ("HurtValue", c_int),    #伤害飘血,求余亿部分
+                  ("HurtValueEx", c_int),    #伤害飘血,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC0
+        self.SubCmd = 0x29
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC0
+        self.SubCmd = 0x29
+        self.CityID = 0
+        self.CityLV = 0
+        self.FamilyID = 0
+        self.GuardID = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.AtkPlayerID = 0
+        self.AtkRet = 0
+        self.KillCnt = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatCityInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 29 仙盟攻城战城池战斗信息 //tagGCFamilyGCZBatCityInfo:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                CityID:%d,
+                                CityLV:%d,
+                                FamilyID:%d,
+                                GuardID:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                AtkPlayerID:%d,
+                                AtkRet:%d,
+                                KillCnt:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.CityID,
+                                self.CityLV,
+                                self.FamilyID,
+                                self.GuardID,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.AtkPlayerID,
+                                self.AtkRet,
+                                self.KillCnt,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatCityInfo=tagGCFamilyGCZBatCityInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatCityInfo.Cmd,m_NAtagGCFamilyGCZBatCityInfo.SubCmd))] = m_NAtagGCFamilyGCZBatCityInfo
+
+
+#------------------------------------------------------
+# C0 25 仙盟攻城战轮次分组信息 #tagGCFamilyGCZBatGroupInfo
+
+class  tagGCFamilyGCZBatGroup(Structure):
+    GroupNum = 0    #(BYTE GroupNum)// 分组编号,从1开始,对应A
+    FamilyIDCnt = 0    #(BYTE FamilyIDCnt)
+    FamilyIDList = list()    #(vector<DWORD> FamilyIDList)// 仙盟ID列表,前端可以通过判断参与的仙盟ID是否在某个分组里面验证有没有战场参赛资格
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.GroupNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.FamilyIDCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyIDCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.FamilyIDList.append(value)
+        return _pos
+
+    def Clear(self):
+        self.GroupNum = 0
+        self.FamilyIDCnt = 0
+        self.FamilyIDList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 1
+        length += 4 * self.FamilyIDCnt
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.GroupNum)
+        data = CommFunc.WriteBYTE(data, self.FamilyIDCnt)
+        for i in range(self.FamilyIDCnt):
+            data = CommFunc.WriteDWORD(data, self.FamilyIDList[i])
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                GroupNum:%d,
+                                FamilyIDCnt:%d,
+                                FamilyIDList:%s
+                                '''\
+                                %(
+                                self.GroupNum,
+                                self.FamilyIDCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBat(Structure):
+    BatType = 0    #(BYTE BatType)// 战场类型 1-初级;2-中级;3-高级;
+    GroupCnt = 0    #(BYTE GroupCnt)// 分组数
+    GroupList = list()    #(vector<tagGCFamilyGCZBatGroup> GroupList)// 分组列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.BatType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.GroupCnt):
+            temGroupList = tagGCFamilyGCZBatGroup()
+            _pos = temGroupList.ReadData(_lpData, _pos)
+            self.GroupList.append(temGroupList)
+        return _pos
+
+    def Clear(self):
+        self.BatType = 0
+        self.GroupCnt = 0
+        self.GroupList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 1
+        length += 1
+        for i in range(self.GroupCnt):
+            length += self.GroupList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteBYTE(data, self.BatType)
+        data = CommFunc.WriteBYTE(data, self.GroupCnt)
+        for i in range(self.GroupCnt):
+            data = CommFunc.WriteString(data, self.GroupList[i].GetLength(), self.GroupList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                BatType:%d,
+                                GroupCnt:%d,
+                                GroupList:%s
+                                '''\
+                                %(
+                                self.BatType,
+                                self.GroupCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatGroupInfo(Structure):
+    Head = tagHead()
+    RoundNum = 0    #(BYTE RoundNum)// 第x轮的分组,从1开始,没有在对应轮次战场分组里的视为没有该轮次的参赛资格
+    BatTypeCnt = 0    #(BYTE BatTypeCnt)// 战场类型数
+    BatList = list()    #(vector<tagGCFamilyGCZBat> BatList)//战场列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x25
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.RoundNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.BatTypeCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.BatTypeCnt):
+            temBatList = tagGCFamilyGCZBat()
+            _pos = temBatList.ReadData(_lpData, _pos)
+            self.BatList.append(temBatList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x25
+        self.RoundNum = 0
+        self.BatTypeCnt = 0
+        self.BatList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 1
+        for i in range(self.BatTypeCnt):
+            length += self.BatList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.RoundNum)
+        data = CommFunc.WriteBYTE(data, self.BatTypeCnt)
+        for i in range(self.BatTypeCnt):
+            data = CommFunc.WriteString(data, self.BatList[i].GetLength(), self.BatList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                RoundNum:%d,
+                                BatTypeCnt:%d,
+                                BatList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.RoundNum,
+                                self.BatTypeCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatGroupInfo=tagGCFamilyGCZBatGroupInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatGroupInfo.Head.Cmd,m_NAtagGCFamilyGCZBatGroupInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZBatGroupInfo
+
+
+#------------------------------------------------------
+# C0 30 仙盟攻城战战斗战报 #tagGCFamilyGCZBatReport
+
+class  tagGCFamilyGCZBatRepHurt(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("TagFamilyID", c_int),    #目标仙盟ID
+                  ("HurtValue", c_int),    #输出伤害,求余亿部分
+                  ("HurtValueEx", c_int),    #输出伤害,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.TagFamilyID = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatRepHurt)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 30 仙盟攻城战战斗战报 //tagGCFamilyGCZBatReport:
+                                TagFamilyID:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.TagFamilyID,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatReport(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)// 战报仙盟ID
+    DefRepCnt = 0    #(BYTE DefRepCnt)
+    DefRepList = list()    #(vector<tagGCFamilyGCZBatRepHurt> DefRepList)// 防守战报
+    AtkRepCnt = 0    #(BYTE AtkRepCnt)
+    AtkRepList = list()    #(vector<tagGCFamilyGCZBatRepHurt> AtkRepList)// 进攻战报
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x30
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.DefRepCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.DefRepCnt):
+            temDefRepList = tagGCFamilyGCZBatRepHurt()
+            _pos = temDefRepList.ReadData(_lpData, _pos)
+            self.DefRepList.append(temDefRepList)
+        self.AtkRepCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.AtkRepCnt):
+            temAtkRepList = tagGCFamilyGCZBatRepHurt()
+            _pos = temAtkRepList.ReadData(_lpData, _pos)
+            self.AtkRepList.append(temAtkRepList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x30
+        self.FamilyID = 0
+        self.DefRepCnt = 0
+        self.DefRepList = list()
+        self.AtkRepCnt = 0
+        self.AtkRepList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        for i in range(self.DefRepCnt):
+            length += self.DefRepList[i].GetLength()
+        length += 1
+        for i in range(self.AtkRepCnt):
+            length += self.AtkRepList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteBYTE(data, self.DefRepCnt)
+        for i in range(self.DefRepCnt):
+            data = CommFunc.WriteString(data, self.DefRepList[i].GetLength(), self.DefRepList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.AtkRepCnt)
+        for i in range(self.AtkRepCnt):
+            data = CommFunc.WriteString(data, self.AtkRepList[i].GetLength(), self.AtkRepList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                DefRepCnt:%d,
+                                DefRepList:%s,
+                                AtkRepCnt:%d,
+                                AtkRepList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.DefRepCnt,
+                                "...",
+                                self.AtkRepCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatReport=tagGCFamilyGCZBatReport()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatReport.Head.Cmd,m_NAtagGCFamilyGCZBatReport.Head.SubCmd))] = m_NAtagGCFamilyGCZBatReport
+
+
+#------------------------------------------------------
+# C0 28 仙盟攻城战城池场景信息 #tagGCFamilyGCZBatSceneInfo
+
+class  tagGCFamilyGCZBatScenePlayer(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])
+    Face = 0    #(DWORD Face)
+    FacePic = 0    #(DWORD FacePic)
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.Face,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.Face = 0
+        self.FacePic = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteDWORD(data, self.Face)
+        data = CommFunc.WriteDWORD(data, self.FacePic)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                Face:%d,
+                                FacePic:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.Face,
+                                self.FacePic
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneHurt(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("CityID", c_int),    #被攻击的城池ID
+                  ("HurtValue", c_int),    #伤害飘血,求余亿部分
+                  ("HurtValueEx", c_int),    #伤害飘血,整除亿部分
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.CityID = 0
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatSceneHurt)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 28 仙盟攻城战城池场景信息 //tagGCFamilyGCZBatSceneInfo:
+                                CityID:%d,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.CityID,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneCity(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("CityID", c_int),    #城池ID
+                  ("CityLV", c_ubyte),    #城池等级
+                  ("FamilyID", c_int),    #所属仙盟ID,可能为0
+                  ("Rank", c_ubyte),    #当前名次,从1开始
+                  ("HP", c_int),    #剩余生命,求余亿部分,剩余生命为0时代表被摧毁
+                  ("HPEx", c_int),    #剩余生命,整除亿部分
+                  ("HPMax", c_int),    #最大生命,求余亿部分
+                  ("HPMaxEx", c_int),    #最大生命,整除亿部分
+                  ("LastAtkedTime", c_int),    #最后一次被攻击时间戳,可能为0
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.CityID = 0
+        self.CityLV = 0
+        self.FamilyID = 0
+        self.Rank = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.LastAtkedTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZBatSceneCity)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 28 仙盟攻城战城池场景信息 //tagGCFamilyGCZBatSceneInfo:
+                                CityID:%d,
+                                CityLV:%d,
+                                FamilyID:%d,
+                                Rank:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                LastAtkedTime:%d
+                                '''\
+                                %(
+                                self.CityID,
+                                self.CityLV,
+                                self.FamilyID,
+                                self.Rank,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.LastAtkedTime
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZBatSceneInfo(Structure):
+    Head = tagHead()
+    BatType = 0    #(BYTE BatType)// 战场类型 1-初级;2-中级;3-高级;
+    GroupNum = 0    #(BYTE GroupNum)// 分组编号,从1开始,对应A
+    TopFamilyID = 0    #(DWORD TopFamilyID)// 伤害第一仙盟ID
+    TopLeaderID = 0    #(DWORD TopLeaderID)// 伤害第一盟主ID
+    TopPlayerID = 0    #(DWORD TopPlayerID)// 伤害第一玩家ID
+    TopPlayerFamilyID = 0    #(DWORD TopPlayerFamilyID)// 伤害第一玩家仙盟ID
+    AtkPlayerID = 0    #(DWORD AtkPlayerID)// 发起攻击的玩家,可能为0,仅技能攻击时通知,普攻时仅通知血量等信息
+    AtkType = 0    #(BYTE AtkType)// 攻击类型,有发起攻击的玩家时有效
+    KillCnt = 0    #(BYTE KillCnt)// 本次攻击累计击杀数,有发起攻击的玩家时有效
+    HurtCnt = 0    #(BYTE HurtCnt)
+    HurtList = list()    #(vector<tagGCFamilyGCZBatSceneHurt> HurtList)//本次攻击伤血信息,有发起攻击的玩家时有效
+    CityCnt = 0    #(BYTE CityCnt)
+    CityList = list()    #(vector<tagGCFamilyGCZBatSceneCity> CityList)// 城池信息,仅通知变化的城池
+    PlayerCnt = 0    #(BYTE PlayerCnt)
+    PlayerInfoList = list()    #(vector<tagGCFamilyGCZBatScenePlayer> PlayerInfoList)// 场景展示所需要用到的玩家信息,如第一玩家、使用技能玩家
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x28
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.BatType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.GroupNum,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.TopFamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopLeaderID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TopPlayerFamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.AtkPlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.AtkType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.KillCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.HurtCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.HurtCnt):
+            temHurtList = tagGCFamilyGCZBatSceneHurt()
+            _pos = temHurtList.ReadData(_lpData, _pos)
+            self.HurtList.append(temHurtList)
+        self.CityCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.CityCnt):
+            temCityList = tagGCFamilyGCZBatSceneCity()
+            _pos = temCityList.ReadData(_lpData, _pos)
+            self.CityList.append(temCityList)
+        self.PlayerCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.PlayerCnt):
+            temPlayerInfoList = tagGCFamilyGCZBatScenePlayer()
+            _pos = temPlayerInfoList.ReadData(_lpData, _pos)
+            self.PlayerInfoList.append(temPlayerInfoList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x28
+        self.BatType = 0
+        self.GroupNum = 0
+        self.TopFamilyID = 0
+        self.TopLeaderID = 0
+        self.TopPlayerID = 0
+        self.TopPlayerFamilyID = 0
+        self.AtkPlayerID = 0
+        self.AtkType = 0
+        self.KillCnt = 0
+        self.HurtCnt = 0
+        self.HurtList = list()
+        self.CityCnt = 0
+        self.CityList = list()
+        self.PlayerCnt = 0
+        self.PlayerInfoList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 1
+        length += 1
+        length += 1
+        for i in range(self.HurtCnt):
+            length += self.HurtList[i].GetLength()
+        length += 1
+        for i in range(self.CityCnt):
+            length += self.CityList[i].GetLength()
+        length += 1
+        for i in range(self.PlayerCnt):
+            length += self.PlayerInfoList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.BatType)
+        data = CommFunc.WriteBYTE(data, self.GroupNum)
+        data = CommFunc.WriteDWORD(data, self.TopFamilyID)
+        data = CommFunc.WriteDWORD(data, self.TopLeaderID)
+        data = CommFunc.WriteDWORD(data, self.TopPlayerID)
+        data = CommFunc.WriteDWORD(data, self.TopPlayerFamilyID)
+        data = CommFunc.WriteDWORD(data, self.AtkPlayerID)
+        data = CommFunc.WriteBYTE(data, self.AtkType)
+        data = CommFunc.WriteBYTE(data, self.KillCnt)
+        data = CommFunc.WriteBYTE(data, self.HurtCnt)
+        for i in range(self.HurtCnt):
+            data = CommFunc.WriteString(data, self.HurtList[i].GetLength(), self.HurtList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.CityCnt)
+        for i in range(self.CityCnt):
+            data = CommFunc.WriteString(data, self.CityList[i].GetLength(), self.CityList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.PlayerCnt)
+        for i in range(self.PlayerCnt):
+            data = CommFunc.WriteString(data, self.PlayerInfoList[i].GetLength(), self.PlayerInfoList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                BatType:%d,
+                                GroupNum:%d,
+                                TopFamilyID:%d,
+                                TopLeaderID:%d,
+                                TopPlayerID:%d,
+                                TopPlayerFamilyID:%d,
+                                AtkPlayerID:%d,
+                                AtkType:%d,
+                                KillCnt:%d,
+                                HurtCnt:%d,
+                                HurtList:%s,
+                                CityCnt:%d,
+                                CityList:%s,
+                                PlayerCnt:%d,
+                                PlayerInfoList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.BatType,
+                                self.GroupNum,
+                                self.TopFamilyID,
+                                self.TopLeaderID,
+                                self.TopPlayerID,
+                                self.TopPlayerFamilyID,
+                                self.AtkPlayerID,
+                                self.AtkType,
+                                self.KillCnt,
+                                self.HurtCnt,
+                                "...",
+                                self.CityCnt,
+                                "...",
+                                self.PlayerCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZBatSceneInfo=tagGCFamilyGCZBatSceneInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZBatSceneInfo.Head.Cmd,m_NAtagGCFamilyGCZBatSceneInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZBatSceneInfo
+
+
+#------------------------------------------------------
+# C0 26 仙盟攻城战大本营信息 #tagGCFamilyGCZCampInfo
+
+class  tagGCFamilyGCZCampMem(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])//参与玩家名字
+    FamilyLV = 0    #(BYTE FamilyLV)//参与时的家族职位
+    Face = 0    #(DWORD Face)//基本脸型
+    FacePic = 0    #(DWORD FacePic)//头像框
+    FightPower = 0    #(DWORD FightPower)//战力,求余亿部分
+    FightPowerEx = 0    #(DWORD FightPowerEx)//战力,整除亿部分
+    HP = 0    #(DWORD HP)//剩余生命,求余亿部分
+    HPEx = 0    #(DWORD HPEx)//剩余生命,整除亿部分
+    HPMax = 0    #(DWORD HPMax)//最大生命,求余亿部分
+    HPMaxEx = 0    #(DWORD HPMaxEx)//最大生命,整除亿部分
+    TotalHurt = 0    #(DWORD TotalHurt)//活动总伤害,求余亿部分,如果不在榜上则读该值
+    TotalHurtEx = 0    #(DWORD TotalHurtEx)//活动总伤害,整除亿部分
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.FamilyLV,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Face,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FacePic,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPower,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.FightPowerEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMax,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMaxEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TotalHurt,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.TotalHurtEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.FamilyLV = 0
+        self.Face = 0
+        self.FacePic = 0
+        self.FightPower = 0
+        self.FightPowerEx = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.TotalHurt = 0
+        self.TotalHurtEx = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 1
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteBYTE(data, self.FamilyLV)
+        data = CommFunc.WriteDWORD(data, self.Face)
+        data = CommFunc.WriteDWORD(data, self.FacePic)
+        data = CommFunc.WriteDWORD(data, self.FightPower)
+        data = CommFunc.WriteDWORD(data, self.FightPowerEx)
+        data = CommFunc.WriteDWORD(data, self.HP)
+        data = CommFunc.WriteDWORD(data, self.HPEx)
+        data = CommFunc.WriteDWORD(data, self.HPMax)
+        data = CommFunc.WriteDWORD(data, self.HPMaxEx)
+        data = CommFunc.WriteDWORD(data, self.TotalHurt)
+        data = CommFunc.WriteDWORD(data, self.TotalHurtEx)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                FamilyLV:%d,
+                                Face:%d,
+                                FacePic:%d,
+                                FightPower:%d,
+                                FightPowerEx:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                TotalHurt:%d,
+                                TotalHurtEx:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.FamilyLV,
+                                self.Face,
+                                self.FacePic,
+                                self.FightPower,
+                                self.FightPowerEx,
+                                self.HP,
+                                self.HPEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.TotalHurt,
+                                self.TotalHurtEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZCampInfo(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)//所在活动仙盟ID,可能不是玩家当前的仙盟ID,活动以该ID为准
+    Score = 0    #(WORD Score)//活动总积分,如果不在榜上则读该值
+    CampLV = 0    #(WORD CampLV)//大本营当前等级
+    CampExp = 0    #(DWORD CampExp)//大本营当前经验
+    CityLV = 0    #(WORD CityLV)//城池属性等级,开战后可能与当前大本营等级不一样
+    HPBase = 0    #(DWORD HPBase)//基础生命,求余亿部分
+    HPBaseEx = 0    #(DWORD HPBaseEx)//基础生命,整除亿部分
+    HPMax = 0    #(DWORD HPMax)//总生命,求余亿部分
+    HPMaxEx = 0    #(DWORD HPMaxEx)//总大生命,整除亿部分
+    HP = 0    #(DWORD HP)//剩余生命,求余亿部分
+    HPEx = 0    #(DWORD HPEx)//剩余生命,整除亿部分
+    DefMemCnt = 0    #(BYTE DefMemCnt)
+    DefMemList = list()    #(vector<tagGCFamilyGCZCampMem> DefMemList)//防守成员列表,有同步则差异更新,没有在防守成员里的视为没有参与资格
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x26
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Score,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.CampLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.CampExp,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.CityLV,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.HPBase,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPBaseEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMax,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPMaxEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HP,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HPEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.DefMemCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.DefMemCnt):
+            temDefMemList = tagGCFamilyGCZCampMem()
+            _pos = temDefMemList.ReadData(_lpData, _pos)
+            self.DefMemList.append(temDefMemList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x26
+        self.FamilyID = 0
+        self.Score = 0
+        self.CampLV = 0
+        self.CampExp = 0
+        self.CityLV = 0
+        self.HPBase = 0
+        self.HPBaseEx = 0
+        self.HPMax = 0
+        self.HPMaxEx = 0
+        self.HP = 0
+        self.HPEx = 0
+        self.DefMemCnt = 0
+        self.DefMemList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 2
+        length += 2
+        length += 4
+        length += 2
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 4
+        length += 1
+        for i in range(self.DefMemCnt):
+            length += self.DefMemList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteWORD(data, self.Score)
+        data = CommFunc.WriteWORD(data, self.CampLV)
+        data = CommFunc.WriteDWORD(data, self.CampExp)
+        data = CommFunc.WriteWORD(data, self.CityLV)
+        data = CommFunc.WriteDWORD(data, self.HPBase)
+        data = CommFunc.WriteDWORD(data, self.HPBaseEx)
+        data = CommFunc.WriteDWORD(data, self.HPMax)
+        data = CommFunc.WriteDWORD(data, self.HPMaxEx)
+        data = CommFunc.WriteDWORD(data, self.HP)
+        data = CommFunc.WriteDWORD(data, self.HPEx)
+        data = CommFunc.WriteBYTE(data, self.DefMemCnt)
+        for i in range(self.DefMemCnt):
+            data = CommFunc.WriteString(data, self.DefMemList[i].GetLength(), self.DefMemList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                Score:%d,
+                                CampLV:%d,
+                                CampExp:%d,
+                                CityLV:%d,
+                                HPBase:%d,
+                                HPBaseEx:%d,
+                                HPMax:%d,
+                                HPMaxEx:%d,
+                                HP:%d,
+                                HPEx:%d,
+                                DefMemCnt:%d,
+                                DefMemList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.Score,
+                                self.CampLV,
+                                self.CampExp,
+                                self.CityLV,
+                                self.HPBase,
+                                self.HPBaseEx,
+                                self.HPMax,
+                                self.HPMaxEx,
+                                self.HP,
+                                self.HPEx,
+                                self.DefMemCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZCampInfo=tagGCFamilyGCZCampInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZCampInfo.Head.Cmd,m_NAtagGCFamilyGCZCampInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZCampInfo
+
+
+#------------------------------------------------------
+# C0 27 仙盟攻城战成员贡献值信息 #tagGCFamilyGCZContributionInfo
+
+class  tagGCFamilyGCZContribution(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("PlayerID", c_int),    
+                  ("ContributionValue", c_int),    #贡献值
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.PlayerID = 0
+        self.ContributionValue = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZContribution)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 27 仙盟攻城战成员贡献值信息 //tagGCFamilyGCZContributionInfo:
+                                PlayerID:%d,
+                                ContributionValue:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.ContributionValue
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZContributionInfo(Structure):
+    Head = tagHead()
+    ContriCnt = 0    #(BYTE ContriCnt)
+    ContriList = list()    #(vector<tagGCFamilyGCZContribution> ContriList)//成员贡献值列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x27
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.ContriCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.ContriCnt):
+            temContriList = tagGCFamilyGCZContribution()
+            _pos = temContriList.ReadData(_lpData, _pos)
+            self.ContriList.append(temContriList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x27
+        self.ContriCnt = 0
+        self.ContriList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.ContriCnt):
+            length += self.ContriList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.ContriCnt)
+        for i in range(self.ContriCnt):
+            data = CommFunc.WriteString(data, self.ContriList[i].GetLength(), self.ContriList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                ContriCnt:%d,
+                                ContriList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.ContriCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZContributionInfo=tagGCFamilyGCZContributionInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZContributionInfo.Head.Cmd,m_NAtagGCFamilyGCZContributionInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZContributionInfo
+
+
+#------------------------------------------------------
+# C0 31 仙盟攻城战分组仙盟成员伤害明细 #tagGCFamilyGCZGroupFamilyMemHurtInfo
+
+class  tagGCFamilyGCZGroupFamilyMemHurt(Structure):
+    PlayerID = 0    #(DWORD PlayerID)
+    Name = ""    #(char Name[33])//参与玩家名字
+    HurtValue = 0    #(DWORD HurtValue)//伤害,求余亿部分
+    HurtValueEx = 0    #(DWORD HurtValueEx)//伤害,整除亿部分
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Name,_pos = CommFunc.ReadString(_lpData, _pos,33)
+        self.HurtValue,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HurtValueEx,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.PlayerID = 0
+        self.Name = ""
+        self.HurtValue = 0
+        self.HurtValueEx = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 33
+        length += 4
+        length += 4
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteString(data, 33, self.Name)
+        data = CommFunc.WriteDWORD(data, self.HurtValue)
+        data = CommFunc.WriteDWORD(data, self.HurtValueEx)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                PlayerID:%d,
+                                Name:%s,
+                                HurtValue:%d,
+                                HurtValueEx:%d
+                                '''\
+                                %(
+                                self.PlayerID,
+                                self.Name,
+                                self.HurtValue,
+                                self.HurtValueEx
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGroupFamilyMemHurtInfo(Structure):
+    Head = tagHead()
+    FamilyID = 0    #(DWORD FamilyID)//查看的目标仙盟ID
+    HurtMemCnt = 0    #(BYTE HurtMemCnt)
+    HurtMemList = list()    #(vector<tagGCFamilyGCZGroupFamilyMemHurt> HurtMemList)//成员伤害明细列表,只算城池被摧毁前的伤害,未排序,前端自己排序
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x31
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.FamilyID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.HurtMemCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.HurtMemCnt):
+            temHurtMemList = tagGCFamilyGCZGroupFamilyMemHurt()
+            _pos = temHurtMemList.ReadData(_lpData, _pos)
+            self.HurtMemList.append(temHurtMemList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x31
+        self.FamilyID = 0
+        self.HurtMemCnt = 0
+        self.HurtMemList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        for i in range(self.HurtMemCnt):
+            length += self.HurtMemList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.FamilyID)
+        data = CommFunc.WriteBYTE(data, self.HurtMemCnt)
+        for i in range(self.HurtMemCnt):
+            data = CommFunc.WriteString(data, self.HurtMemList[i].GetLength(), self.HurtMemList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                FamilyID:%d,
+                                HurtMemCnt:%d,
+                                HurtMemList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.FamilyID,
+                                self.HurtMemCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo=tagGCFamilyGCZGroupFamilyMemHurtInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo.Head.Cmd,m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZGroupFamilyMemHurtInfo
+
+
+#------------------------------------------------------
+# C0 32 仙盟攻城战竞猜名单信息 #tagGCFamilyGCZGuessInfo
+
+class  tagGCFamilyGCZGuessFamily(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("FamilyID", c_int),    #备选仙盟ID
+                  ("GuessValue", c_ushort),    #竞猜热度值,玩家每次选择则加1,重复选择也算
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.FamilyID = 0
+        self.GuessValue = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZGuessFamily)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 32 仙盟攻城战竞猜名单信息 //tagGCFamilyGCZGuessInfo:
+                                FamilyID:%d,
+                                GuessValue:%d
+                                '''\
+                                %(
+                                self.FamilyID,
+                                self.GuessValue
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGuessRight(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("AwardID", c_ubyte),    #奖励ID
+                  ("RightPlayerCnt", c_ushort),    #猜中玩家个数
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        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.AwardID = 0
+        self.RightPlayerCnt = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagGCFamilyGCZGuessRight)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C0 32 仙盟攻城战竞猜名单信息 //tagGCFamilyGCZGuessInfo:
+                                AwardID:%d,
+                                RightPlayerCnt:%d
+                                '''\
+                                %(
+                                self.AwardID,
+                                self.RightPlayerCnt
+                                )
+        return DumpString
+
+
+class  tagGCFamilyGCZGuessInfo(Structure):
+    Head = tagHead()
+    PlayerID = 0    #(DWORD PlayerID)//竞猜玩家ID,有玩家修改竞猜时会附带该信息,如果是自己ID,则更新自己的竞猜选项,否则无视
+    SelectCnt = 0    #(BYTE SelectCnt)
+    SelectFamilyIDList = list()    #(vector<DWORD> SelectFamilyIDList)//竞猜玩家选择的仙盟ID顺序
+    FinalCnt = 0    #(BYTE FinalCnt)
+    FinalFamilyIDList = list()    #(vector<DWORD> FinalFamilyIDList)//最终排名顺序,仅活动排名出来后才有值
+    RightCnt = 0    #(BYTE RightCnt)
+    RightInfoList = list()    #(vector<tagGCFamilyGCZGuessRight> RightInfoList)//猜中个数明细列表
+    FamilyCnt = 0    #(BYTE FamilyCnt)
+    GuessFamilyList = list()    #(vector<tagGCFamilyGCZGuessFamily> GuessFamilyList)//备选仙盟名单列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x32
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.PlayerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.SelectCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.SelectCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.SelectFamilyIDList.append(value)
+        self.FinalCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FinalCnt):
+            value,_pos=CommFunc.ReadDWORD(_lpData,_pos)
+            self.FinalFamilyIDList.append(value)
+        self.RightCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.RightCnt):
+            temRightInfoList = tagGCFamilyGCZGuessRight()
+            _pos = temRightInfoList.ReadData(_lpData, _pos)
+            self.RightInfoList.append(temRightInfoList)
+        self.FamilyCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.FamilyCnt):
+            temGuessFamilyList = tagGCFamilyGCZGuessFamily()
+            _pos = temGuessFamilyList.ReadData(_lpData, _pos)
+            self.GuessFamilyList.append(temGuessFamilyList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xC0
+        self.Head.SubCmd = 0x32
+        self.PlayerID = 0
+        self.SelectCnt = 0
+        self.SelectFamilyIDList = list()
+        self.FinalCnt = 0
+        self.FinalFamilyIDList = list()
+        self.RightCnt = 0
+        self.RightInfoList = list()
+        self.FamilyCnt = 0
+        self.GuessFamilyList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 4
+        length += 1
+        length += 4 * self.SelectCnt
+        length += 1
+        length += 4 * self.FinalCnt
+        length += 1
+        for i in range(self.RightCnt):
+            length += self.RightInfoList[i].GetLength()
+        length += 1
+        for i in range(self.FamilyCnt):
+            length += self.GuessFamilyList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteDWORD(data, self.PlayerID)
+        data = CommFunc.WriteBYTE(data, self.SelectCnt)
+        for i in range(self.SelectCnt):
+            data = CommFunc.WriteDWORD(data, self.SelectFamilyIDList[i])
+        data = CommFunc.WriteBYTE(data, self.FinalCnt)
+        for i in range(self.FinalCnt):
+            data = CommFunc.WriteDWORD(data, self.FinalFamilyIDList[i])
+        data = CommFunc.WriteBYTE(data, self.RightCnt)
+        for i in range(self.RightCnt):
+            data = CommFunc.WriteString(data, self.RightInfoList[i].GetLength(), self.RightInfoList[i].GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.FamilyCnt)
+        for i in range(self.FamilyCnt):
+            data = CommFunc.WriteString(data, self.GuessFamilyList[i].GetLength(), self.GuessFamilyList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                PlayerID:%d,
+                                SelectCnt:%d,
+                                SelectFamilyIDList:%s,
+                                FinalCnt:%d,
+                                FinalFamilyIDList:%s,
+                                RightCnt:%d,
+                                RightInfoList:%s,
+                                FamilyCnt:%d,
+                                GuessFamilyList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.PlayerID,
+                                self.SelectCnt,
+                                "...",
+                                self.FinalCnt,
+                                "...",
+                                self.RightCnt,
+                                "...",
+                                self.FamilyCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagGCFamilyGCZGuessInfo=tagGCFamilyGCZGuessInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagGCFamilyGCZGuessInfo.Head.Cmd,m_NAtagGCFamilyGCZGuessInfo.Head.SubCmd))] = m_NAtagGCFamilyGCZGuessInfo
 
 
 #------------------------------------------------------
@@ -57675,6 +59419,70 @@
 
 
 #------------------------------------------------------
+# C1 10 仙盟攻城战玩家信息 #tagMCFamilyGCZPlayerInfo
+
+class  tagMCFamilyGCZPlayerInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("Cmd", c_ubyte),
+                  ("SubCmd", c_ubyte),
+                  ("ContributionCnt", c_ubyte),    # 轮次低级捐献已捐献次数,轮次变更时会重置
+                  ("Energy", c_ubyte),    # 当前可用免费体力
+                  ("EnergyTime", c_int),    # 上次恢复免费体力时间戳,为0时不再恢复
+                  ("AwardState", c_int),    # 活动奖励领取状态,按二进制位判断是否已领取,0-竞猜奖励;1-个人排行奖励;2-仙盟排名奖励;
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        self.Cmd = 0xC1
+        self.SubCmd = 0x10
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.Cmd = 0xC1
+        self.SubCmd = 0x10
+        self.ContributionCnt = 0
+        self.Energy = 0
+        self.EnergyTime = 0
+        self.AwardState = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCFamilyGCZPlayerInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// C1 10 仙盟攻城战玩家信息 //tagMCFamilyGCZPlayerInfo:
+                                Cmd:%s,
+                                SubCmd:%s,
+                                ContributionCnt:%d,
+                                Energy:%d,
+                                EnergyTime:%d,
+                                AwardState:%d
+                                '''\
+                                %(
+                                self.Cmd,
+                                self.SubCmd,
+                                self.ContributionCnt,
+                                self.Energy,
+                                self.EnergyTime,
+                                self.AwardState
+                                )
+        return DumpString
+
+
+m_NAtagMCFamilyGCZPlayerInfo=tagMCFamilyGCZPlayerInfo()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCFamilyGCZPlayerInfo.Cmd,m_NAtagMCFamilyGCZPlayerInfo.SubCmd))] = m_NAtagMCFamilyGCZPlayerInfo
+
+
+#------------------------------------------------------
 # C1 08 幸运云购玩家信息 #tagMCLuckyCloudBuyPlayerInfo
 
 class  tagMCLuckyCloudBuyPlayerInfo(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/FamilyGCZ.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/FamilyGCZ.py
new file mode 100644
index 0000000..fd4d6a0
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/FamilyGCZ.py
@@ -0,0 +1,75 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.FamilyGCZ
+#
+# @todo:仙盟攻城战
+# @author hxp
+# @date 2025-04-09
+# @version 1.0
+#
+# 详细描述: 仙盟攻城战
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-04-09 16:00"""
+#-------------------------------------------------------------------------------
+
+import ChConfig
+import GameWorld
+import PlayerControl
+import PlayerActFamilyGCZ
+import ShareDefine
+
+#---------------------------------------------------------------------
+#逻辑实现
+
+## GM命令执行入口
+#  @param curPlayer 当前玩家
+#  @param msgList 参数列表
+#  @return None
+#  @remarks 函数详细说明.
+def OnExec(curPlayer, msgList):
+    
+    if not msgList:
+        GameWorld.DebugAnswer(curPlayer, "捐献次数: FamilyGCZ j 低级捐献次数")
+        GameWorld.DebugAnswer(curPlayer, "设置体力: FamilyGCZ e 体力")
+        GameWorld.DebugAnswer(curPlayer, "成员捐献: FamilyGCZ x 经验 [成员数]")
+        GameWorld.DebugAnswer(curPlayer, "设大本营: FamilyGCZ c 等级 [经验]")
+        GameWorld.DebugAnswer(curPlayer, "攻击城池: FamilyGCZ a 城池ID [攻击次数 仙盟ID]")
+        GameWorld.DebugAnswer(curPlayer, "攻击城池: FamilyGCZ h 城池ID 剩余血量 [攻击方人数 仙盟ID]")
+        GameWorld.DebugAnswer(curPlayer, "击毁城池: FamilyGCZ k [城池ID 攻击方仙盟ID]")
+        GameWorld.DebugAnswer(curPlayer, "输出城池: FamilyGCZ p [战场类型 分组编号]")
+        GameWorld.DebugAnswer(curPlayer, "山寨竞猜: FamilyGCZ g 人数 [竞猜ID1 ID2 ID3]")
+        GameWorld.DebugAnswer(curPlayer, "城池ID一般等同于仙盟ID,修罗城ID=20亿")
+        GameWorld.DebugAnswer(curPlayer, "攻击次数默认1自己攻击,大于1仙盟成员轮流攻击")
+        GameWorld.DebugAnswer(curPlayer, "剩余血量 :由指定的攻击方人数平摊输出,1个时默认自己")
+        GameWorld.DebugAnswer(curPlayer, "击毁城池没有指定ID时则随机击毁一个")
+        GameWorld.DebugAnswer(curPlayer, "攻击方仙盟ID有值可指定A盟攻击B盟")
+        GameWorld.DebugAnswer(curPlayer, "攻击方仙盟ID没填则A盟默认自己盟")
+        return
+    
+    playerID = curPlayer.GetPlayerID()
+    value = msgList[0]
+    
+    # 捐献次数
+    if value == "j":
+        contriCnt = msgList[1] if len(msgList) > 1 else 0
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZContributionCnt, contriCnt)
+        GameWorld.DebugAnswer(curPlayer, "设置低级捐献次数: %s" % contriCnt)
+        PlayerActFamilyGCZ.Sync_FamilyGCZPlayerInfo(curPlayer)
+        return
+    
+    # 设置体力
+    if value == "e":
+        setEnergy = msgList[1] if len(msgList) > 1 else 0
+        updEnergy = PlayerActFamilyGCZ.SetEnergy(curPlayer, setEnergy)
+        GameWorld.DebugAnswer(curPlayer, "设置体力: %s" % updEnergy)
+        return
+    
+    if value in ["x", "c", "a", "h", "k", "p", "g"]:
+        dataMsg = {"ActMsgType":"GMCMD", "playerID":playerID, "msgList":msgList}
+        GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+        return
+    
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
index 1dfdf39..fd0fd8c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Item/ItemControler.py
@@ -224,6 +224,47 @@
 def IsEventItem(item):
     return (item.GetType() == ChConfig.Def_ItemType_MissionItem)
 
+def DelPlayerItemByPacks(curPlayer, itemID, delCount, eventName="", saveDataDict={}, 
+                         packTypeList=[IPY_GameWorld.rptItem, IPY_GameWorld.rptWarehouse]):
+    '''扣除玩家物品,从多个背包检查,一般是用于不是立马扣除的逻辑,防止玩家快速把背包物品放入仓库导致扣除失败
+    如跨服功能,有些逻辑需要跨服验证或处理后才进行扣除
+    '''           
+    remainDelCnt = delCount # 剩余需要扣除数量
+    for packType in packTypeList:
+        curPack = curPlayer.GetItemManager().GetPack(packType)
+        for i in range(0, curPack.GetCount()):
+            curItem = curPack.GetAt(i)
+            if curItem.IsEmpty():
+                continue
+            if curItem.GetItemTypeID() != itemID:
+                continue
+            if curItem.GetIsLocked():
+                continue
+            
+            itemCount = GetItemCount(curItem)
+            
+            #身上物品比要删除的物品多
+            if itemCount > remainDelCnt:
+                updItemCount = itemCount - remainDelCnt
+                SetItemCount(curItem, updItemCount)
+                if ItemNeedRecord(curItem):
+                    itemNoteDict = ItemCommon.GetItemNoteDict(curItem, remainDelCnt, itemCount, updItemCount)
+                    ItemCommon.DR_DelItem(curPlayer, packType, eventName, itemNoteDict, saveDataDict)
+                remainDelCnt = 0
+            else:
+                if ItemNeedRecord(curItem):
+                    itemNoteDict = ItemCommon.GetItemNoteDict(curItem, itemCount, itemCount, 0)
+                    ItemCommon.DR_DelItem(curPlayer, packType, eventName, itemNoteDict, saveDataDict)
+                curItem.Clear()
+                remainDelCnt -= itemCount
+                
+            if remainDelCnt <= 0:
+                return True
+            
+    GameWorld.ErrLog("扣除物品失败,物品不足! itemID=%s,delCount=%s,remainDelCnt=%s,eventName=%s" 
+                     % (itemID, delCount, remainDelCnt, eventName), curPlayer.GetPlayerID())
+    return False
+
 ## 删除物品 
 #  @param curPlayer 当前玩家
 #  @param packindex 背包索引
@@ -2534,7 +2575,7 @@
         
     return True
 
-def GivePlayerItemOrMail(curPlayer, itemList, mailKey=None, event=["", False, {}], isNotifyAward=True):
+def GivePlayerItemOrMail(curPlayer, itemList, mailKey=None, event=["", False, {}], isNotifyAward=True, notifyDataEx=None):
     ##给物品,背包满则发邮件
     if not itemList:
         return
@@ -2570,7 +2611,7 @@
             
         if isNotifyAward:
             eventName = event[0] if event else ""
-            NotifyGiveAwardInfo(curPlayer, giveItemList, eventName)
+            NotifyGiveAwardInfo(curPlayer, giveItemList, eventName, dataEx=notifyDataEx)
     return
 
 def NotifyGiveAwardInfo(curPlayer, giveItemInfo, eventName="", exp=0, moneyInfo=None, dataEx=None):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
index 6df29d6..4ed2bea 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -170,6 +170,7 @@
 import PlayerActLoginNew
 import PlayerActBuyCountGift
 import PlayerActLunhuidian
+import PlayerActFamilyGCZ
 import PlayerActYunshi
 import PlayerActTask
 
@@ -1006,6 +1007,7 @@
         PlayerCutTree.OnPlayerLogin(curPlayer)
         PlayerMineArea.OnPlayerLogin(curPlayer)
         PlayerGuaji.OnPlayerLogin(curPlayer)
+        PlayerActFamilyGCZ.OnPlayerLogin(curPlayer)
         
         # 上线查询一次充值订单
         curPlayer.SendDBQueryRecharge()
@@ -5706,6 +5708,9 @@
     # 仙盟充值互助活动奖励
     elif rewardType == ChConfig.Def_RewardType_FamilyCTGAssist:
         PlayerActFamilyCTGAssist.GetFamilyCTGAssistAward(curPlayer, dataEx, dataExStr)
+    # 仙盟攻城战活动奖励
+    elif rewardType == ChConfig.Def_RewardType_FamilyGCZ:
+        PlayerActFamilyGCZ.GetFamilyGCZAward(curPlayer, dataEx, tick)
     # 天道树奖励
     elif rewardType == ChConfig.Def_RewardType_TiandaoTree:
         PlayerXiangong.GetTiandaoTreeAward(curPlayer, dataEx)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py
index 0245418..3e623b8 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/CrossRealmPlayer.py
@@ -206,6 +206,19 @@
                 return actInfo
     return {}
 
+def GetCrossActInfoByZoneID(actName, zoneID):
+    ## 根据分区获取跨服对应的跨服活动信息
+    actInfoDict = PyGameData.g_crossActInfoDict.get(actName, {})
+    if not actInfoDict:
+        return {}
+    for actInfo in actInfoDict.values():
+        if not actInfo.get(ShareDefine.ActKey_State, 0):
+            continue
+        ipyDataDict = actInfo.get(ShareDefine.ActKey_IpyDataInfo, {})
+        if zoneID == ipyDataDict.get("ZoneID", 0):
+            return actInfo
+    return {}
+
 def NotifyCrossActEnd(curPlayer, actName):
     '''通知跨服运营活动结束
                     防止跨服服务器与子服时间不一致导致可能出现活动实际已关闭
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyGCZ.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyGCZ.py
new file mode 100644
index 0000000..4c3d753
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerActFamilyGCZ.py
@@ -0,0 +1,525 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.PlayerActFamilyGCZ
+#
+# @todo:仙盟攻城战
+# @author hxp
+# @date 2025-04-09
+# @version 1.0
+#
+# 详细描述: 仙盟攻城战
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-04-09 16:00"""
+#-------------------------------------------------------------------------------
+
+import ChConfig
+import GameWorld
+import ShareDefine
+import IpyGameDataPY
+import NetPackCommon
+import CrossRealmPlayer
+import ChPyNetSendPack
+import IPY_GameWorld
+import PlayerControl
+import ItemControler
+import ItemCommon
+
+import time
+
+## 1-公示期;99-领奖期;轮次状态=轮次*10+轮次阶段;轮次阶段:1-分组战备;2-战斗;3-休战
+FamilyGCZState_Publicity = 1 # 公示期
+FamilyGCZState_Award = 99 # 结束领奖期
+
+# 轮次状态信息
+FamilyGCZRoundState_Group = 1 # 分组+战备
+FamilyGCZRoundState_Fight = 2 # 战斗
+FamilyGCZRoundState_Over = 3 # 休战结算
+
+# 攻击类型
+(
+AtkType_Normal, # 普通单攻 1
+AtkType_SkillSingle, # 技能单攻 2
+AtkType_SkillArea, # 技能群攻 3
+) = range(1, 1 + 3)
+
+def GetRoundState(state):
+    ## 获取轮次、状态信息
+    if state < 10 or state == FamilyGCZState_Award:
+        return 0, state
+    return state / 10, state % 10
+
+def SendToGameServer_FamilyGCZ(curPlayer, msgType, dataMsg=""):
+    playerID = curPlayer.GetPlayerID()
+    msgList = str([msgType, dataMsg])
+    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "FamilyGCZ", msgList, len(msgList))
+    GameWorld.DebugLog("仙盟攻城战发送GameServer: %s, %s" % (msgType, dataMsg), playerID)
+    return
+
+def GameServer_FamilyGCZ(curPlayer, msgData, tick):
+    ## 收到GameServer攻城战信息
+    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_FamilyGCZ, 0)
+    
+    msgType, dataMsg, ret = msgData
+    
+    if not ret:
+        return
+    
+    if msgType == "FamilyGCZPlayerActInfo":
+        __CheckPlayerFamilyGCZByCrossGameServer(curPlayer, dataMsg)
+        
+    elif msgType == "FamilyGCZAtkResult":
+        __OnFamilyGCZAtkResult(curPlayer, dataMsg)
+        
+    elif msgType == "FamilyGCZAward":
+        __OnFamilyGCZAward(curPlayer, dataMsg)
+        
+    return
+
+def OnPlayerLogin(curPlayer):
+    __CheckPlayerFamilyGCZByMapServer(curPlayer)
+    return
+
+def RefreshActFamilyGCZInfo():
+    ## 收到GameServer同步的活动信息,刷新活动信息
+    playerManager = GameWorld.GetPlayerManager()
+    for index in xrange(playerManager.GetPlayerCount()):
+        curPlayer = playerManager.GetPlayerByIndex(index)
+        if not GameWorld.IsNormalPlayer(curPlayer):
+            continue
+        
+        __CheckPlayerFamilyGCZByMapServer(curPlayer)
+        
+    return
+
+def __CheckPlayerFamilyGCZByMapServer(curPlayer):
+    ## 本服地图仅检查结束重置即可
+    
+    playerID = curPlayer.GetPlayerID()
+    playerActID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZID)
+    playerZoneID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZZoneID)
+    playerRoundNum = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZRoundNum)
+        
+    actInfo = CrossRealmPlayer.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, playerZoneID)
+    actID = actInfo.get(ShareDefine.ActKey_ID, 0)
+    state = actInfo.get(ShareDefine.ActKey_State, 0)
+    if not playerActID and not playerZoneID:
+        GameWorld.DebugLog("仙盟攻城战活动玩家没有活动数据不需要重置! playerZoneID=%s,playerActID=%s" % (playerZoneID, playerActID), playerID)
+        if state:
+            roundNum, _ = GetRoundState(state)
+            if roundNum and roundNum != playerRoundNum:
+                __doPlayerRoundChange(curPlayer, playerRoundNum, roundNum)
+        return
+    
+    ipyDataDict = actInfo.get(ShareDefine.ActKey_IpyDataInfo, {})
+    zoneID = ipyDataDict.get("ZoneID", 0)
+    
+    if state and playerActID == actID and playerZoneID == zoneID:
+        GameWorld.DebugLog("仙盟攻城战活动玩家正常活动中不需要重置! playerZoneID=%s,playerActID=%s,state=%s" % (playerZoneID, playerActID, state), playerID)
+        return
+    GameWorld.DebugLog("仙盟攻城战活动玩家重置End! zoneID=%s,actID=%s,playerZoneID=%s,playerActID=%s,state=%s" 
+                       % (zoneID, actID, playerZoneID, playerActID, state), playerID)
+    
+    __doPlayerFamilyGCZReset(curPlayer)
+    return
+
+def __CheckPlayerFamilyGCZByCrossGameServer(curPlayer, joinInfo):
+    '''检查玩家参与的活动信息,以锁定盟为准的活动统一根据跨服GameServer同步的参与信息处理
+        锁定名单信息都在跨服GameServer,所以跨服GameServer统一管理
+    注:结束重置的在本服地图直接处理即可
+    '''
+    
+    playerID = curPlayer.GetPlayerID()
+    zoneID, familyID = joinInfo
+    
+    actInfo = CrossRealmPlayer.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    actID = actInfo.get(ShareDefine.ActKey_ID, 0)
+    state = actInfo.get(ShareDefine.ActKey_State, 0)
+    
+    playerActID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZID)
+    playerZoneID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZZoneID)
+    if playerActID == actID and playerZoneID == zoneID:
+        GameWorld.DebugLog("仙盟攻城战活动ID不变,不处理! zoneID=%s,actID=%s,state=%s,familyID=%s" % (zoneID, actID, state, familyID), playerID)
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZFamilyID, familyID)
+        if state:
+            Sync_FamilyGCZPlayerInfo(curPlayer)
+        return
+    
+    GameWorld.DebugLog("仙盟攻城战活动玩家重置! zoneID=%s,actID=%s,playerZoneID=%s,playerActID=%s" 
+                       % (zoneID, actID, playerZoneID, playerActID), playerID)
+    __doPlayerFamilyGCZReset(curPlayer, state, actID, zoneID, familyID)
+    return
+
+def __doPlayerRoundChange(curPlayer, playerRoundNum, roundNum):
+    ## 轮次变更
+    playerID = curPlayer.GetPlayerID()
+    GameWorld.DebugLog("    仙盟攻城战活动玩家轮次变更! playerRoundNum=%s,roundNum=%s" % (playerRoundNum, roundNum), playerID)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZRoundNum, roundNum)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZContributionCnt, 0)
+    addEnergy = IpyGameDataPY.GetFuncCfg("FamilyGCZEnergy", 3)
+    if addEnergy > 0:
+        SetEnergy(curPlayer, curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy) + addEnergy, False)
+    Sync_FamilyGCZPlayerInfo(curPlayer)
+    return
+
+def __doPlayerFamilyGCZReset(curPlayer, state=0, actID=0, zoneID=0, familyID=0):
+    ## 活动变更重置
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZID, actID)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZZoneID, zoneID)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZFamilyID, familyID)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZRoundNum, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZContributionCnt, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergy, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergyTime, 0)
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZAwardState, 0)
+    
+#    # 回收道具
+#    for itemID in IpyGameDataPY.GetFuncEvalCfg("LianqiUseItem", 5):
+#        ItemControler.RecycleItem(curPlayer, itemID, "ActLianqiRecycleItem")
+
+    if state:
+        maxEnergy = IpyGameDataPY.GetFuncCfg("FamilyGCZEnergy", 1)
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergy, maxEnergy)
+        Sync_FamilyGCZPlayerInfo(curPlayer)
+        
+    return True
+
+def OnProcess(curPlayer):
+    if not curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZID):
+        return
+    curEnergy = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy)
+    maxEnergy = IpyGameDataPY.GetFuncCfg("FamilyGCZEnergy", 1)
+    if curEnergy >= maxEnergy:
+        #GameWorld.DebugLog("仙盟攻城战体力已满,无需恢复! curEnergy=%s >= %s" % (curEnergy, maxEnergy), curPlayer.GetPlayerID())
+        return
+    needSeconds = IpyGameDataPY.GetFuncCfg("FamilyGCZEnergy", 2) * 60
+    if not needSeconds:
+        return
+    curTime = int(time.time())
+    lastTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergyTime)
+    if not lastTime:
+        lastTime = curTime
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergyTime, lastTime)
+        return
+    passTime = curTime - lastTime
+    addEnergy = passTime / needSeconds
+    if addEnergy <= 0:
+        return
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergyTime, curTime)
+    updEnergy = SetEnergy(curPlayer, min(curEnergy + addEnergy, maxEnergy))
+    GameWorld.DebugLog("时间恢复仙盟攻城战体力: passTime=%s(%s-%s),addEnergy=%s,updEnergy=%s" 
+                       % (passTime, curTime, lastTime, addEnergy, updEnergy), curPlayer.GetPlayerID())
+    return
+
+def SetEnergy(curPlayer, setEnergy, isNotify=True):
+    ## 设置体力
+    maxEnergy = IpyGameDataPY.GetFuncCfg("FamilyGCZEnergy", 1)
+    curEnergy = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy)
+    updEnergy = min(maxEnergy, max(setEnergy, 0))
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergy, updEnergy)
+    
+    if updEnergy >= maxEnergy:
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergyTime, 0)
+    elif curEnergy >= maxEnergy and updEnergy < maxEnergy:
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZEnergyTime, int(time.time()))
+        
+    if isNotify:
+        Sync_FamilyGCZPlayerInfo(curPlayer)
+    return updEnergy
+
+#// C1 24 仙盟攻城战捐献 #tagCMFamilyGCZContribution
+#
+#struct    tagCMFamilyGCZContribution
+#{
+#    tagHead        Head;
+#    BYTE        ContributionType;        //捐献类型: 0-低级;1-高级
+#    DWORD        UseCount;        //物品捐献时使用个数
+#};
+def OnFamilyGCZContribution(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    contributionType = clientData.ContributionType
+    useCount = clientData.UseCount
+    
+    # 大本营被摧毁无法再捐献,前端自己判断,后端不限制
+    
+    if contributionType == 1:
+        __doContiributionHigh(curPlayer, useCount)
+    else:
+        __doContiributionLow(curPlayer)        
+    return
+
+def __doContiributionLow(curPlayer):
+    ## 低级捐献 - 货币
+    playerID = curPlayer.GetPlayerID()
+    moneyType = IpyGameDataPY.GetFuncCfg("FamilyGCZContributionLow", 1)
+    costMoneyList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZContributionLow", 2)
+    if not moneyType or not costMoneyList:
+        return
+    maxCnt = len(costMoneyList)
+    contriCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZContributionCnt)
+    if contriCnt >= maxCnt:
+        GameWorld.DebugLog("仙盟攻城战已达本轮最大低级捐献次数! contriCnt=%s >= %s" % (contriCnt, maxCnt), playerID)
+        return
+    costMoney = costMoneyList[contriCnt]
+    if not costMoney or not PlayerControl.PayMoney(curPlayer, moneyType, costMoney, "FamilyGCZ"):
+        return
+    contriCnt += 1
+    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZContributionCnt, contriCnt)
+    Sync_FamilyGCZPlayerInfo(curPlayer)
+    
+    addCampExp = IpyGameDataPY.GetFuncCfg("FamilyGCZContributionLow", 3)
+    randItemWeightList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZContributionLow", 4)
+    randItemInfo = GameWorld.GetResultByWeightList(randItemWeightList)
+    if randItemInfo and len(randItemInfo) >= 2:
+        ItemControler.GivePlayerItemOrMail(curPlayer, [randItemInfo], event=["FamilyGCZContiribution", False, {}])
+        
+    GameWorld.DebugLog("仙盟攻城战低级捐献! contriCnt=%s,moneyType=%s,costMoney=%s,addCampExp=%s,randItemInfo=%s" 
+                       % (contriCnt, moneyType, costMoney, addCampExp, randItemInfo), playerID)
+    dataMsg = {"ActMsgType":"AddCampExp", "playerID":playerID, "addCampExp":addCampExp}
+    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+
+def __doContiributionHigh(curPlayer, useCount):
+    ## 高级捐献 - 物品
+    playerID = curPlayer.GetPlayerID()
+    costItemID = IpyGameDataPY.GetFuncCfg("FamilyGCZContributionHigh", 1)
+    
+    costItemIndexList, bindCnt, unBindCnt = ItemCommon.GetPackItemBindStateIndexInfo(curPlayer, costItemID, useCount)
+    lackCnt = useCount - bindCnt - unBindCnt
+    if lackCnt > 0:
+        GameWorld.DebugLog("仙盟攻城战高级捐献消耗道具不足! costItemID=%s,useCount=%s,bindCnt=%s,unBindCnt=%s,lackCnt=%s" 
+                           % (costItemID, useCount, bindCnt, unBindCnt, lackCnt))
+        return
+    # 扣除消耗
+    ItemCommon.DelCostItemByBind(curPlayer, costItemIndexList, bindCnt, unBindCnt, useCount, "FamilyGCZContiribution")
+    
+    addCampExp = IpyGameDataPY.GetFuncCfg("FamilyGCZContributionHigh", 2) * useCount
+    randItemWeightList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZContributionHigh", 3)
+    awardItemList = []
+    for _ in range(useCount):
+        randItemInfo = GameWorld.GetResultByWeightList(randItemWeightList)
+        if randItemInfo and len(randItemInfo) >= 2:
+            awardItemList.append(randItemInfo)
+    if awardItemList:
+        ItemControler.GivePlayerItemOrMail(curPlayer, GameWorld.MergeItemList(awardItemList), event=["FamilyGCZContiribution", False, {}])
+        
+    GameWorld.DebugLog("仙盟攻城战高级捐献! costItemID=%s,useCount=%s,addCampExp=%s,awardItemList=%s" 
+                       % (costItemID, useCount, addCampExp, awardItemList), playerID)
+    dataMsg = {"ActMsgType":"AddCampExp", "playerID":playerID, "addCampExp":addCampExp}
+    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+
+#// C1 25 仙盟攻城战攻击 #tagCMFamilyGCZAtk
+#
+#struct    tagCMFamilyGCZAtk
+#{
+#    tagHead        Head;
+#    BYTE        AtkType;        //攻击类型: 1-普通单攻;2-技能单攻;3-技能群攻;
+#    DWORD        TagCityID;    //目标城池ID,一般是仙盟ID或者特殊城池ID如修罗城城池,普攻单攻需指定目标,群攻技能发0
+#    DWORD        TagGuardID;    //目标守卫ID,一般是玩家ID或者特殊守卫ID如修罗城守卫,普攻单攻需指定目标,技能攻击发0
+#};
+def OnFamilyGCZAtk(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    atkType = clientData.AtkType
+    tagCityID = clientData.TagCityID
+    tagGuardID = clientData.TagGuardID
+    
+    playerID = curPlayer.GetPlayerID()
+    
+    zoneID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZZoneID)
+    actInfo = CrossRealmPlayer.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State, 0)
+    roundNum, roundState = GetRoundState(state)
+    if not roundNum or roundState != FamilyGCZRoundState_Fight:
+        GameWorld.ErrLog("仙盟攻城战非攻击阶段: state=%s,roundNum=%s,roundState=%s" % (state, roundNum, roundState), playerID)
+        return
+    
+    hurtFamilyCnt = 1
+    useItemID = 0
+    # 普攻
+    if atkType == AtkType_Normal:
+        curEnergy = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy)
+        if not curEnergy:
+            useItemID = IpyGameDataPY.GetFuncCfg("FamilyGCZAtk", 2)
+            if not useItemID:
+                return
+            if not ItemCommon.FindItemInPackByItemID(curPlayer, useItemID, IPY_GameWorld.rptItem):
+                GameWorld.DebugLog("仙盟攻城战普攻道具不足! useItemID=%s" % useItemID, playerID)
+                return
+        hurtMulti = IpyGameDataPY.GetFuncCfg("FamilyGCZAtk", 1)
+        
+    # 技能
+    else:
+        useItemID = IpyGameDataPY.GetFuncCfg("FamilyGCZAtk", 4)
+        if not useItemID:
+            return
+        if not ItemCommon.FindItemInPackByItemID(curPlayer, useItemID, IPY_GameWorld.rptItem):
+            GameWorld.DebugLog("仙盟攻城战技能道具不足! useItemID=%s" % useItemID, playerID)
+            return    
+        hurtMulti = IpyGameDataPY.GetFuncCfg("FamilyGCZAtk", 3)
+        
+        # 群攻
+        if atkType == AtkType_SkillArea:
+            hurtFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyGCZAtk", 5)
+            
+    tick = GameWorld.GetGameWorld().GetTick()
+    if not GameWorld.SetPlayerTickTime(curPlayer, ChConfig.TYPE_Player_Tick_FamilyGCZ, tick):
+        GameWorld.DebugLog("攻城战请求CD中...", playerID)
+        PlayerControl.NotifyCode(curPlayer, "RequestLater")
+        return
+    
+    dataMsg = {"ActMsgType":"GCZAtk", "zoneID":zoneID, "playerID":playerID, "atkType":atkType,
+               "tagCityID":tagCityID, "tagGuardID":tagGuardID, "hurtMulti":hurtMulti, 
+               "useItemID":useItemID, "hurtFamilyCnt":hurtFamilyCnt}
+    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+
+def __OnFamilyGCZAtkResult(curPlayer, retInfo):
+    ## 跨服同步回来的攻击结果
+    #"atkRet":atkRet, "errMsg":errMsg, "reqMsg":msgData, "hurtDict":hurtDict, "killCntTotal":killCntTotal
+    
+    playerID = curPlayer.GetPlayerID()
+    atkRet = retInfo["atkRet"]
+    errMsg = retInfo["errMsg"]
+    hurtDict = retInfo["hurtDict"]
+    killCntTotal = retInfo["killCntTotal"]
+    reqMsg = retInfo["reqMsg"]
+    
+    atkType = reqMsg["atkType"]
+    useItemID = reqMsg["useItemID"]
+    
+    GameWorld.DebugLog("仙盟攻城战攻击结果! atkType=%s,takRet=%s:%s" % (atkType, atkRet, errMsg), playerID)
+    if atkRet != 0 or errMsg != "OK":
+        # 攻击失败可暂时不处理
+        return
+    
+    # 攻击成功处理
+    
+    # 扣除物品
+    if useItemID:
+        delCount = 1
+        if not ItemControler.DelPlayerItemByPacks(curPlayer, useItemID, delCount, "FamilyGCZAtk"):
+            return
+        GameWorld.DebugLog("    扣除道具: useItemID=%s" % (useItemID), playerID)
+        
+    fixAwardItemList = [] # 固定奖励
+    # 普通攻击
+    if atkType == AtkType_Normal:
+        if useItemID:
+            fixAwardItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZAtkAward", 2) 
+        else:
+            fixAwardItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZAtkAward", 1) 
+            # 非道具普攻,扣体力
+            curEnergy = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy)
+            updEnergy = SetEnergy(curPlayer, curEnergy - 1)
+            GameWorld.DebugLog("    扣除体力: curEnergy=%s,updEnergy=%s" % (curEnergy, updEnergy), playerID)
+            
+    # 技能攻击
+    elif atkType in [AtkType_SkillSingle, AtkType_SkillArea]:
+        fixAwardItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZAtkAward", 3) 
+        
+    killAwardItemList = []
+    killAwardRandCnt = IpyGameDataPY.GetFuncCfg("FamilyGCZAtkAward", 5) * killCntTotal
+    killAwardWeightList = IpyGameDataPY.GetFuncEvalCfg("FamilyGCZAtkAward", 4) 
+    for _ in range(killAwardRandCnt):
+        randItem = GameWorld.GetResultByWeightList(killAwardWeightList)
+        if randItem and isinstance(randItem, list) and len(randItem) == 3:
+            killAwardItemList.append(randItem)
+            
+    hurtCnt = len(hurtDict)
+    hurtValueTotal = sum(hurtDict.values())
+    GameWorld.DebugLog("    hurtCnt=%s,hurtValueTotal=%s,killCntTotal=%s" % (hurtCnt, hurtValueTotal, killCntTotal), playerID)
+    
+    notifyDataEx = {"atkType":atkType, "killCntTotal":killCntTotal, "hurtCnt":hurtCnt, "hurtValueTotal":hurtValueTotal}
+    giveAwardItemList = GameWorld.MergeItemList(fixAwardItemList + killAwardItemList)
+    GameWorld.DebugLog("    giveAwardItemList=%s,fixAwardItemList=%s,killAwardItemList=%s" % (giveAwardItemList, fixAwardItemList, killAwardItemList), playerID)
+    ItemControler.GivePlayerItemOrMail(curPlayer, giveAwardItemList, event=["FamilyGCZAtk", False, {}], notifyDataEx=notifyDataEx)
+    return
+
+#// C1 26 仙盟攻城战竞猜 #tagCMFamilyGCZGuess
+#
+#struct    tagCMFamilyGCZGuess
+#{
+#    tagHead        Head;
+#    BYTE        SelectCnt;
+#    DWORD        SelectFamilyIDList[SelectCnt];    // 竞猜选择的仙盟ID排名顺序
+#};
+def OnFamilyGCZGuess(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    selectFamilyIDList = clientData.SelectFamilyIDList
+    playerID = curPlayer.GetPlayerID()
+    zoneID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZZoneID)
+    dataMsg = {"ActMsgType":"Guess", "zoneID":zoneID, "playerID":playerID, "selectFamilyIDList":selectFamilyIDList}
+    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+
+def GetFamilyGCZAward(curPlayer, awardType, tick):
+    ## 领奖 0-竞猜奖励;1-个人排行奖励;2-仙盟排名奖励;
+    playerID = curPlayer.GetPlayerID()
+    awardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZAwardState)
+    if awardState&pow(2, awardType):
+        GameWorld.DebugLog("仙盟攻城战已领取该奖励! awardType=%s,awardState=%s" % (awardType, awardState), playerID)
+        return
+    
+    zoneID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZZoneID)
+    actInfo = CrossRealmPlayer.GetCrossActInfoByZoneID(ShareDefine.CrossActName_FamilyGCZ, zoneID)
+    if not actInfo:
+        return
+    state = actInfo.get(ShareDefine.ActKey_State, 0)
+    if state != FamilyGCZState_Award:
+        GameWorld.ErrLog("仙盟攻城战非领奖阶段: state=%s" % state, playerID)
+        return
+    
+    if not GameWorld.SetPlayerTickTime(curPlayer, ChConfig.TYPE_Player_Tick_FamilyGCZ, tick):
+        GameWorld.DebugLog("攻城战请求CD中...", playerID)
+        PlayerControl.NotifyCode(curPlayer, "RequestLater")
+        return
+    
+    dataMsg = {"ActMsgType":"GetAward", "zoneID":zoneID, "playerID":playerID, "awardType":awardType}
+    GameWorld.SendMsgToCrossServer(ShareDefine.ClientServerMsg_FamilyGCZ, dataMsg)
+    return
+
+def __OnFamilyGCZAward(curPlayer, dataMsg):
+    ## 跨服同步回来的领奖结果
+    #"zoneID":zoneID, "playerID":playerID, "actID":actID, "awardType":awardType, "awardItemList":awardItemList
+    zoneID = dataMsg["zoneID"]
+    playerID = dataMsg["playerID"]
+    actID = dataMsg["actID"]
+    awardType = dataMsg["awardType"]
+    awardItemList = dataMsg["awardItemList"]
+    awardValue = dataMsg.get("awardValue", 0)
+    fmLV = dataMsg.get("fmLV", 0)
+    playerActID = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZID)
+    awardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZAwardState)
+    if awardState&pow(2, awardType):
+        GameWorld.Log("仙盟攻城战发放奖励时已领取该奖励! awardType=%s,awardState=%s,zoneID=%s,actID=%s" 
+                      % (awardType, awardState, zoneID, actID), playerID)
+        return
+    
+    GameWorld.DebugLog("仙盟攻城战发放奖励! awardType=%s,awardValue=%s,fmLV=%s,awardItemList=%s,awardState=%s" 
+                  % (awardType, awardValue, fmLV, awardItemList, awardState), playerID)
+    
+    # 同一个活动,更新领奖记录
+    if playerActID == actID:
+        updAwardState = awardState|pow(2, awardType)
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyGCZAwardState, updAwardState)
+        GameWorld.DebugLog("    更新领奖记录! awardType=%s,awardState=%s,updAwardState=%s" % (awardType, awardState, updAwardState), playerID)
+        Sync_FamilyGCZPlayerInfo(curPlayer)
+        
+    drDict = {"zoneID":zoneID, "actID":actID, "awardType":awardType, "awardValue":awardValue, "fmLV":fmLV}
+    ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["FamilyGCZAward", True, drDict])
+    return
+
+def Sync_FamilyGCZPlayerInfo(curPlayer):
+    clientPack = ChPyNetSendPack.tagMCFamilyGCZPlayerInfo()
+    clientPack.ContributionCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZContributionCnt)
+    clientPack.Energy = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergy)
+    clientPack.EnergyTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZEnergyTime)
+    clientPack.AwardState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyGCZAwardState)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    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 0b8de83..0218108 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -86,6 +86,7 @@
 import PlayerActHorsePetTrain
 import PlayerActLianqi
 import PlayerActGodGift
+import PlayerActFamilyGCZ
 import PlayerActFamilyCTGAssist
 import PlayerActRechargeRebateGold
 import PlayerActManyDayRecharge
@@ -1576,6 +1577,9 @@
             elif actionName == ShareDefine.CrossActName_Lianqi:
                 PlayerActLianqi.RefreshCrossActLianqiInfo()
                 
+            elif actionName == ShareDefine.CrossActName_FamilyGCZ:
+                PlayerActFamilyGCZ.RefreshActFamilyGCZInfo()
+                
             return
         
         if key == ShareDefine.Def_Notify_WorldKey_CrossZoneName:
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 13e0de7..f4b95b2 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerState.py
@@ -55,6 +55,7 @@
 import FormulaControl
 import PlayerGoldGift
 import PlayerActLianqi
+import PlayerActFamilyGCZ
 import PlayerFlashSale
 import PlayerChatBox
 import PlayerFace
@@ -1300,6 +1301,8 @@
     FBLogic.OnCustomSceneProcess(curPlayer, tick)
     #炼器
     PlayerActLianqi.OnProcess(curPlayer)
+    #仙盟攻城战
+    PlayerActFamilyGCZ.OnProcess(curPlayer)
     #跨服数据同步,放最后
     CrossPlayerData.ProcessCrossPlayer(curPlayer, tick)
     return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FamilyGCZ.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FamilyGCZ.py
new file mode 100644
index 0000000..645f194
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/RemoteQuery/GY_Query_FamilyGCZ.py
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.RemoteQuery.GY_Query_FamilyGCZ
+#
+# @todo:仙盟攻城战
+# @author hxp
+# @date 2025-04-09
+# @version 1.0
+#
+# 详细描述: 仙盟攻城战
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-04-09 16:00"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import PlayerActFamilyGCZ
+
+#---------------------------------------------------------------------
+#逻辑实现
+## 请求逻辑
+#  @param query_Type 请求类型
+#  @param query_ID 请求的玩家ID
+#  @param packCMDList 发包命令 [ ]
+#  @param tick 当前时间
+#  @return "True" or "False" or ""
+#  @remarks 函数详细说明.
+def DoLogic(query_Type, query_ID, packCMDList, tick):
+    return ""
+
+#---------------------------------------------------------------------
+#执行结果
+## 执行结果
+#  @param curPlayer 发出请求的玩家
+#  @param callFunName 功能名称
+#  @param funResult 查询的结果
+#  @param tick 当前时间
+#  @return None
+#  @remarks 函数详细说明.
+def DoResult(curPlayer, callFunName, funResult, tick):
+    GameWorld.DebugLog("GY_Query_FamilyGCZ DoResult %s" % str(funResult), curPlayer.GetPlayerID())
+    if funResult != "":
+        PlayerActFamilyGCZ.GameServer_FamilyGCZ(curPlayer, eval(funResult), tick)
+    return
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index 25a3322..39d2b8e 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -364,10 +364,11 @@
 CrossActName_Gubao = "CrossActGubao" # 古宝养成 - 跨服
 CrossActName_HorsePetTrain = "CrossActHorsePetTrain" # 骑宠养成 - 跨服
 CrossActName_Lianqi = "CrossActLianqi" # 炼器 - 跨服
+CrossActName_FamilyGCZ = "CrossActFamilyGCZ" # 仙盟攻城战
 
 #跨服运营活动列表
 CrossActNameList = [CrossActName_CTGBillboard, CrossActName_AllRecharge, CrossActName_LuckyCloudBuy, CrossActName_BossTrial, 
-                    CrossActName_XianXiaMJ, CrossActName_Gubao, CrossActName_HorsePetTrain, CrossActName_Lianqi]
+                    CrossActName_XianXiaMJ, CrossActName_Gubao, CrossActName_HorsePetTrain, CrossActName_Lianqi, CrossActName_FamilyGCZ]
 #需要锁定活动分区分配直到活动结束的跨服运营活动,即使热更分区配置,也不会改变正在活动中的分区设定,直到活动结束
 CrossActLockServerGroupIDList = [CrossActName_CTGBillboard, CrossActName_AllRecharge]
 
@@ -375,6 +376,7 @@
 ActKey_ID = "ID" # 活动ID,唯一标识的ID,一般是活动开启的time值
 ActKey_State = "State" # 活动状态 0-未开启, >0开启中,也代表当日的第几个时间段
 ActKey_StateJoin = "StateJoin" # 活动某些功能可参与状态 0-还不可参与, >0可参与,一般可参与时该状态等于state
+ActKey_StateError = "StateError" # 按流程走的活动状态是否已异常
 ActKey_CfgID = "CfgID" # 活动表配置ID
 ActKey_ActNum = "ActNum" # 活动分组编号
 ActKey_DayIndex = "DayIndex" # 当前活动天索引,0开始,代表第1天
@@ -917,7 +919,10 @@
 Def_CBT_HorsePetTrainScore, # 骑宠养成积分 - 个人榜  164
 Def_CBT_CrossRealmPK, # 跨服PK竞技场  165
 Def_CBT_LianqiScore, # 炼器积分 - 个人榜  166
-) = range(150, 166 + 1)
+Def_CBT_FamilyGCZScore, # 仙盟攻城战 - 仙盟积分总榜  167 (zoneID, 0)
+Def_CBT_FamilyGCZPlayerHurt, # 仙盟攻城战 - 玩家伤害总榜  168 (zoneID, 0)
+Def_CBT_FamilyGCZRoundHurt, # 仙盟攻城战 - 本轮分组仙盟伤害榜  169 (zoneID, batType*100+groupNum)
+) = range(150, 169 + 1)
 
 #职业对应战力排行榜类型
 JobFightPowerBillboardDict = {
@@ -1435,7 +1440,12 @@
 #通用信息记录类型 - 新 从 300 开始,原通用记录类型最大到255
 Def_GameRecTypeList = (
                        Def_GameRecType_Xiangong, # 仙宫记录 300
-                       ) = range(300, 1 + 300)
+                       Def_GameRecType_FamilyDelSyncCross, # 仙盟删除同步跨服状态本服记录, familyID 301
+                       Def_GameRecType_FamilyGCZMgr, # 仙盟攻城战公共管理信息记录, zoneID 302
+                       Def_GameRecType_FamilyGCZJoinFamily, # 仙盟攻城战参与仙盟信息, zoneID 303
+                       Def_GameRecType_FamilyGCZJoinMember, # 仙盟攻城战参与成员信息, zoneID 304
+                       Def_GameRecType_FamilyGCZCityWall, # 仙盟攻城战城池信息, zoneID 305
+                       ) = range(300, 1 + 305)
 #通用信息记录新 - 字典key配置,如果有配置,则可额外按对应记录Value值存储字典,方便快速取值,可配置Value编号 1~8,配空默认 Value1
 Def_GameRecValueKeyDict = {
                            Def_GameRecType_Xiangong:[1],
@@ -1640,6 +1650,7 @@
 CrossServerMsg_CrossServerState = "CrossServerState"    # 跨服服务器状态变更
 CrossServerMsg_PlayerLoginout = "PlayerLoginout"        # 玩家上下线状态同步
 CrossServerMsg_ExitCrossServer = "ExitCrossServer"      # 退出跨服服务器
+CrossServerMsg_SendFakePack = "SendFakePack"            # 给子服玩家发送封包
 CrossServerMsg_Notify = "Notify"                        # 提示信息
 CrossServerMsg_ChatCrossWorld = "ChatCrossWorld"        # 跨服世界聊天
 CrossServerMsg_ViewPlayerCacheRet = "ViewPlayerCacheRet"# 查看跨服玩家信息结果
@@ -1693,6 +1704,8 @@
 CrossServerMsg_FuncTeamInfo = "FuncTeamInfo"  # 功能队伍信息同步
 CrossServerMsg_FuncTeamDel = "FuncTeamDel"  # 功能队伍删除同步
 CrossServerMsg_FuncTeamList = "FuncTeamList"  # 功能队伍列表同步
+CrossServerMsg_FamilyDelRet = "FamilyDelRet"  # 仙盟删除结果
+CrossServerMsg_FamilyGCZ = "FamilyGCZ"  # 仙盟攻城战
 
 # 子服发送跨服信息定义
 ClientServerMsg_ServerInitOK = "ServerInitOK"           # 子服启动成功
@@ -1742,6 +1755,8 @@
 ClientServerMsg_HorsePetTrainScore = "HorsePetTrainScore" # 骑宠养成积分
 ClientServerMsg_QueryXiangong = "QueryXiangong" # 查看仙宫仙名录
 ClientServerMsg_LianqiScore = "LianqiScore" # 炼器积分
+ClientServerMsg_SyncFamilyInfo = "SyncFamilyInfo" # 仙盟信息同步
+ClientServerMsg_FamilyGCZ = "FamilyGCZ" # 仙盟攻城战
 
 #跨服广播类型定义
 CrossNotify_CrossAct = "CrossAct"

--
Gitblit v1.8.0