From f2cf64ffabcd15d5497aee8914b8d37d8b9a88b0 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期六, 07 二月 2026 15:39:43 +0800
Subject: [PATCH] 66 【公会】基础主体-服务端(公会GM命令支持跨服 CreateFamily、Family、Zhenbaoge;跨服公会聊天支持,包含公会聊天缓存;查看玩家A705回包增加公会所在服务器ID信息;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py | 3263 ++++++++++++++++++++++++++++++++++++----------------------
 1 files changed, 2,020 insertions(+), 1,243 deletions(-)

diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
index e7a0ee6..5c1faa5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
@@ -13,788 +13,1529 @@
 #---------------------------------------------------------------------
 #"""Version = 2017-07-12 21:00""" 
 #---------------------------------------------------------------------
-import IPY_GameWorld
-import GameWorld
-import EventShell
+
 import FBLogic
 import ChConfig
-import PlayerControl
+import GameWorld
 import ShareDefine
-import ReadChConfig
-import PlayerTruck
+import PlayerControl
 import NetPackCommon
+import PlayerViewCache
 import ChPyNetSendPack
-import DataRecordPack
-import GameLogic_FamilyWar
-import GameLogic_FamilyBoss
-import ChMapToGamePyPack
-import PlayerFamilyTech
-import PlayerFamilyRedPacket
-import PlayerFamilyZhenfa
-import PlayerTongTianLing
-import PlayerWeekParty
-import PlayerActivity
-import ItemControler
-import SkillCommon
-import BuffSkill
-import ItemCommon
-import PlayerSuccess
-import GameFuncComm
-import PlayerTJG
+import PlayerFamilyEmblem
+import PlayerFamilyZhenbaoge
+import PlayerFamilyTaofa
+import IPY_PlayerDefine
 import IpyGameDataPY
-import cPickle
+import IPY_GameWorld
+import PlayerMail
+import PlayerTask
+import CrossPlayer
+import PlayerTalk
+import DirtyList
+import DBDataMgr
+import DBFamily
+import CrossMsg
+import CrossMgr
 
-import time
 import random
-#---------------------------------------------------------------------
-#家族刷新
-#class   IPY_MFamilyRefresh
-#{
-#public:
-#
-#    int      GetFamilyID();
-#
-#    char *      GetFamilyName();
-#};
-## 家族刷新
-#  @param index 玩家索引
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def GameServer_FamilyRefresh(index, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    refreshPack = IPY_GameWorld.IPY_MFamilyRefresh()
-    
-    if curPlayer.GetID() != refreshPack.GetPlayerID():
-        #不是自己
-        return
-    
-    lastFamilyID = curPlayer.GetFamilyID()
-    
-#************族长变更删除发布任务*************************************************
-    if (curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader 
-        and refreshPack.GetFamilyMemberLV()!= IPY_GameWorld.fmlLeader):
-        EventShell.EventResponse_OnMemberChange(curPlayer)
-#*******************************************************************
+import time
 
-    #---根据封包修改玩家家族属性---
-    if lastFamilyID != refreshPack.GetFamilyID():
-        curPlayer.SetFamilyID(refreshPack.GetFamilyID())
-        familyIDChangeTime = int(time.time())
-        #PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyIDChangeTime, familyIDChangeTime)
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_LastFamilyID, lastFamilyID)
-        GameWorld.Log("记录家族变更时间...familyIDChangeTime=%s,lastFamilyID=%s,nextFamilyID=%s" 
-                           % (familyIDChangeTime, lastFamilyID, refreshPack.GetFamilyID()), curPlayer.GetPlayerID())
-        
-        #触发玩家加入家族事件
-        EventShell.EventResponse_OnFamilyAdd(curPlayer)
-        
-    if curPlayer.GetFamilyName() != refreshPack.GetFamilyName():
-        curPlayer.SetFamilyName(refreshPack.GetFamilyName())
-        #通知周围玩家家族名称刷新
-        curPlayer.Notify_FamilyNameRefresh()
-    
-    refreshFamilyLV = refreshPack.GetFamilyLV()
-    lastFamilyLV = curPlayer.GetFamilyLV()
-    if lastFamilyLV != refreshFamilyLV:
-        #任务需求,记录先前的家族等级
-        curPlayer.SetDict(ChConfig.Def_PlayerKey_FamilyLVLeave, lastFamilyLV)
-        
-        isLVUP = False
-        if lastFamilyLV and refreshFamilyLV > 0:
-            #触发家族升级事件
-            isLVUP = True
-        
-        curPlayer.SetFamilyLV(refreshFamilyLV)
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
-        
-        __OnFamilyLVBuffChange(curPlayer, tick)
-        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
-        if isLVUP:
-            EventShell.EventResponse_OnFamilyLVUp(curPlayer)
-        
-    if curPlayer.GetFamilyMemberLV() != refreshPack.GetFamilyMemberLV():
-        # 因为仙盟职位没有存DBPlayer,而跨服后又没有Family信息,所以这里做个存储,用于跨服用
-        curPlayer.SetReceivedSalary(refreshPack.GetFamilyMemberLV())
-        curPlayer.SetFamilyMemberLV(refreshPack.GetFamilyMemberLV())
-        #通知周围玩家家族职位刷新
-        #curPlayer.Notify_FamilyMemberLVRefresh()
-        GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
-    
-    if curPlayer.GetFamilyMoney() != refreshPack.GetFamilyMoney():
-        curPlayer.SetFamilyMoney(refreshPack.GetFamilyMoney())
-    
-    if curPlayer.GetFamilyLastWeekActiveValue() != refreshPack.GetLastWeekFamilyActiveValue():
-        curPlayer.SetFamilyLastWeekActiveValue(refreshPack.GetLastWeekFamilyActiveValue())
-        
-    if PlayerControl.GetFamilyEmblemID(curPlayer) != refreshPack.GetExtra6():
-        PlayerControl.SetFamilyEmblemID(curPlayer, refreshPack.GetExtra6())
-        
-    #---处理特殊逻辑, 进入退出家族---
+#申请、审核、退出、踢人、捐献、升级
 
-    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
-        #玩家离开家族
-        __OnLeaveFamily(curPlayer, tick)
-        
-        #改变镖车身上记录的主人信息
-        PlayerTruck.ChangeTruckNoteInfo(curPlayer)
+Def_CreatFamily_MaxStr = 33
 
-    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
-        #刚进家族并为族长,触发建家族事件
-        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
-            EventShell.EventResponse_OnCreateFamily(curPlayer)
-        
-        #进入家族触发事件
-        __OnEnterFamily(curPlayer, tick)
-        
-        #改变镖车身上记录的主人信息
-        PlayerTruck.ChangeTruckNoteInfo(curPlayer)
-        
-    #---通知客户端刷新属性---
-    curPlayer.View_FamilyInfoRefresh()
-    return
+#公会权限
+(
+FamilyPowerID_Call,         #招人 1
+FamilyPowerID_ChangeFmlv,   #变更职位 2
+FamilyPowerID_Broadcast,    #发布公告 3
+FamilyPowerID_Kick,         #踢人 4
+) = range(1, 1 + 4)
 
-## 进入家族触发事件
-#  @param curPlayer 当前玩家
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def __OnEnterFamily(curPlayer, tick):
-    EventShell.EventResponse_OnFamily(curPlayer)
-    __OnFamilyLVBuffChange(curPlayer, tick)
-    __FamilyAffair_CheckReset(curPlayer)
-    PlayerFamilyTech.Sync_PlayerFamilyTechLV(curPlayer)
-    DelAddFamilyRecord(curPlayer)
-    GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
-    GameLogic_FamilyBoss.OnEnterFamily(curPlayer)
-    PlayerFamilyRedPacket.CreatCacheRedPacktet(curPlayer)
-    PlayerFamilyZhenfa.OnEnterFamily(curPlayer)
-    return
+#公会职位对应人数设置属性名
+Def_FmlSetAttrName = {
+                      IPY_PlayerDefine.fmlMember:"MemberMax",
+                      IPY_PlayerDefine.fmlCounsellor:"EliteMax",
+                      IPY_PlayerDefine.fmlViceLeader:"DeputyLeaderMax",
+                      }
 
-## 退出家族触发事件
-#  @param curPlayer 当前玩家
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def __OnLeaveFamily(curPlayer, tick):
-    #---清空家族相关信息---
-    curPlayer.SetPerExp(0)
-    curPlayer.SetFamilyHornor(0)
-    curPlayer.SetFamilyActiveValue(0)
-    curPlayer.SetLastWeekFamilyActiveValue(0)
-    curPlayer.SetFamilyLV(0)
-    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
-    
-    #触发玩家离开家族的任务
-    EventShell.EventResponse_OnLeaveFamily(curPlayer)
-    
-    #设置领奖限制
-    __SetForbidFamilyAward(curPlayer)
-    FBLogic.OnLeaveFamily(curPlayer, tick)
-    
-    __OnFamilyLVBuffChange(curPlayer, tick)
-    #清空仓库积分
-    PlayerControl.SetPlayerCurrency(curPlayer, ShareDefine.TYPE_Price_FamilyStoreScore, 0)
-    GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
-    PlayerFamilyZhenfa.OnLeaveFamily(curPlayer)
-    return
+def GetRenameTime(family): return family.GetExtra1()
+def SetRenameTime(family, renameTime): return family.SetExtra1(renameTime)
 
-## 家族等级加持buff变更处理(进、退家族时 及 家族等级变更时)
-def __OnFamilyLVBuffChange(curPlayer, tick):
-    return
-    # 跨服服务器不处理,防止登录跨服服务器后无战盟导致战力下降
+def OnMinute():
+    #战力刷新在DBFamily.OnMinute
     if GameWorld.IsCrossServer():
+        pass
+    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
+        return
+    PlayerFamilyEmblem.CheckExpireEmblem()
+    return
+
+def FamilyCrossCenterOnHour():
+    if GameWorld.IsCrossServer():
+        __doFamilyOnHour()
+    return
+
+def FamilyOnHour():
+    if not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("非游戏服或本服已跨服互通了不处理 FamilyOnHour")
+        return
+    __doFamilyOnHour()
+    return
+
+def __doFamilyOnHour():
+    familyManager = DBDataMgr.GetFamilyMgr()
+    for zoneID in familyManager.GetZoneIDListThisServer():
+        zoneMgr = familyManager.GetZoneFamilyMgr(zoneID)
+        for i in range(0, zoneMgr.GetCount()):
+            family = zoneMgr.GetAt(i)
+            #自动传位
+            __AutoChangeLeader(family)
+    return
+
+def FamilyCrossCenterOnDay():
+    if GameWorld.IsCrossServer():
+        __doFamilyOnDay()
+    return
+
+def FamilyOnDay():
+    ## 本服时间自己触发的onday逻辑
+    
+    if not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("非游戏服或本服已跨服互通了不处理 FamilyOnDay")
         return
     
-    familyLVBuffSkillTypeID = ReadChConfig.GetEvalChConfig("FamilyLVBuff")
-    if not familyLVBuffSkillTypeID:
+    __doFamilyOnDay()
+    DBFamily.CheckCrossFamilyTransData()
+    return
+
+def __doFamilyOnDay():
+    familyManager = DBDataMgr.GetFamilyMgr()
+    for zoneID in familyManager.GetZoneIDListThisServer():
+        zoneMgr = familyManager.GetZoneFamilyMgr(zoneID)
+        for i in range(0, zoneMgr.GetCount()):
+            family = zoneMgr.GetAt(i)
+            familyID = family.GetID()
+            
+            PlayerFamilyZhenbaoge.OnDay(family)
+            PlayerFamilyTaofa.OnDay(family)
+            
+            for index in xrange(family.GetCount()):
+                member = family.GetAt(index)
+                # 重置成员日信息
+                member.SetContribDay(0)
+                member.SetDonateCntDay(0)
+                
+            Broadcast_FamilyInfo(familyID) # onday
+    return
+
+def PlayerCrossCenterOnDay(curPlayer):
+    ## 处理跨服中心同步的onday玩家事件
+    if not DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会还属于本服,不处理成员跨服过天", curPlayer.GetPlayerID())
         return
-    
-    # 先删后加
-    playerSkillManager = curPlayer.GetSkillManager()
-    BuffSkill.DelBuffBySkillID(curPlayer, familyLVBuffSkillTypeID, tick)
-    playerSkillManager.DeleteSkillBySkillTypeID(familyLVBuffSkillTypeID)
-    familyLV = curPlayer.GetFamilyLV()
-    GameWorld.DebugLog("__OnFamilyLVBuffChange familyLV=%s,familyLVBuffSkillTypeID=%s" % (familyLV, familyLVBuffSkillTypeID))
-    if familyLV > 0:
-        curSkill = GameWorld.GetGameData().FindSkillByType(familyLVBuffSkillTypeID , familyLV)
-        if not curSkill:
-            return
-        skillID = curSkill.GetSkillID()
-        playerSkillManager.LVUPSkillByID(skillID)
-        SkillCommon.AddBuffBySkillType(curPlayer, familyLVBuffSkillTypeID, tick, familyLV)
-    
-    curControl = PlayerControl.PlayerControl(curPlayer)
-    #curControl.CalcPassiveBuffAttr()
-    curControl.RefreshPlayerAttrByBuff()
+    __doPlayerOnDay(curPlayer)
     return
 
-def GetPlayerChangeFamilyPastHour(curPlayer):
-    ## 获取玩家家族变更已经经过了多长时间
-    curTime = int(time.time())
-    changeTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyIDChangeTime)
-    return (curTime - changeTime) / 3600
-
-#
-#//////////////////////////////////////////////////////////////
-#//08 04 申请家族战#tagMRequestFamilyWar
-#tagMRequestFamilyWar       *   GettagMRequestFamilyWar();
-#
-#class   IPY_MRequestFamilyWar
-#{
-#public:
-#
-#    int      GetFamilyID();
-#
-#    char *      GetFamilyName();
-#
-#    int      GetVSFamilyID();
-#
-#    char *      GetVSFamilyName();
-#};
-## 08 04 申请家族战#
-#  @param index 玩家索引
-#  @param tick 当前时间
-#  @return None
-#  @remarks 函数详细说明.
-def GameServer_RequestFamilyWar(index, tick):
-#===============================================================================
-#    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-#    pack = IPY_GameWorld.IPY_MRequestFamilyWar()
-#    familyID = pack.GetFamilyID()
-#    familyName = pack.GetFamilyName()
-#    vsFamilyID = pack.GetVSFamilyID()
-#    vsFamilyName = pack.GetVSFamilyName()
-#    FBLogic.OnRequestFamilyWar(curPlayer, familyID, familyName, vsFamilyID, vsFamilyName, tick)
-#===============================================================================
-    GameWorld.ErrLog('GameServer_RequestFamilyWar, 此接口废弃')
-    return
-#---------------------------------------------------------------------
-##玩家金钱转换为家族金钱
-# @param playerMoney 玩家金钱
-# @return 家族金钱
-# @remarks 玩家金钱转换为家族金钱
-def GetPlayerMoney_Change_FamilyMoney(playerMoney):
-    return int(playerMoney * ShareDefine.Def_PlayerMoney_Change_FamilyMoney_Rate)
-#---------------------------------------------------------------------
-##玩家活跃度转换为家族活跃度
-# @param activeValue 玩家活跃度
-# @return 家族活跃度
-# @remarks 玩家活跃度转换为家族活跃度
-def GetPlayerActiveValue_Change_ActiveValue(activeValue):
-    return activeValue
-
-#---------------------------------------------------------------------
-##累加玩家的家族活跃度
-# @param curPlayer 玩家实例
-# @param addValue 添加的数值
-# @param sendPackGameServer 是否通知GameServer累加家族活跃度
-# @param reason 来源, 原因
-# @return None
-# @remarks 累加玩家的家族活跃度
-def AddPlayerFamilyActiveValue(curPlayer, addValue, sendPackGameServer = False, reason = 0, isSysMsg=False):
-    if addValue <= 0:
-        #GameWorld.ErrLog("AddPlayerFamilyActiveValue Err = %s"%(addValue))
+def PlayerOnDay(curPlayer):
+    ## 本服时间的onday玩家事件
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已经跨服了,不处理成员本服过天", curPlayer.GetPlayerID())
         return
-    
-    curActiveValue = curPlayer.GetFamilyActiveValue()
-    totalActiveValue = curPlayer.GetLastWeekFamilyActiveValue()
+    __doPlayerOnDay(curPlayer)
+    return
 
-    #---累加玩家家族活跃度---
-    if curActiveValue < ChConfig.Def_UpperLimit_DWord:
-        #累计活跃度
-        curPlayer.SetFamilyActiveValue(min(curActiveValue + addValue, ChConfig.Def_UpperLimit_DWord))
-        curPlayer.SetLastWeekFamilyActiveValue(min(totalActiveValue + addValue, ChConfig.Def_UpperLimit_DWord))
-        
-    #增加贡献度
-    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_Family_Contribution, addValue, isSysHint=isSysMsg)
-    
-    #流向记录:
-    DataRecordPack.DR_AddPlayerFamilyActiveValue(curPlayer, addValue, curPlayer.GetFamilyActiveValue(), reason)
+def __doPlayerOnDay(curPlayer):
+    PlayerFamilyZhenbaoge.PlayerOnDay(curPlayer)
+    PlayerFamilyTaofa.PlayerOnDay(curPlayer)
+    Do_MapServer_PlayerOnDay(curPlayer)
+    return
 
-    #---通知世界服务器累加家族活跃度---
-    #@warning: 刀剑笑项目, 玩家家族活跃度累加同时累加家族活跃度
-    #          如果这边没有加上去在外面要处理一次累加家族活跃度
-    if not sendPackGameServer:
-        return True
-      
-    #刀剑笑要求, 玩家活跃度累加的同时累加家族活跃度 1活跃=1资金
-    SendPack_GameServer_AddFamilyDetailEx(curPlayer, addValue, addValue, resion=reason)
-    return True
-
-#---------------------------------------------------------------------
-##通知世界服务器累加家族属性
-# @param curPlayer 玩家实例
-# @param addFamilyHornor 添加家族兽粮
-# @param addFamilyMoney 添加家族资金
-# @param addFamilyActiveValue 增加家族活跃度
-# @return 返回值无意义
-# @remarks 通知世界服务器累加家族属性
-def SendPack_GameServer_AddFamilyDetail(curPlayer, addFamilyHornor = 0, addFamilyMoney = 0, addFamilyActiveValue = 0, resion=0):
-#    //增加家族荣誉
-#    int      GetAddFamilyHornor();
-#    //增加家族资金
-#    int      GetAddFamilyMoney();
-#    //增加家族活跃度
-#    int      GetFamilyActiveValue();
-#    GameWorld.Log('addFamilyHornor = %s, addFamilyMoney = %s, addFamilyActiveValue = %s'%
-#                        (addFamilyHornor, addFamilyMoney, addFamilyActiveValue))
-
-    #curPlayer.GameServer_AddFamilyDetail(addFamilyHornor, addFamilyMoney, addFamilyActiveValue)
-    #改为PY包
-    if not curPlayer.GetFamilyID():
+def OnPlayerLogin(curPlayer, tick):
+    Do_MapServer_PlayerLogin(curPlayer)
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员登录逻辑 OnPlayerLogin", curPlayer.GetPlayerID())
         return
-    sendPack = ChMapToGamePyPack.tagMGAddFamilyDetail()
-    sendPack.PlayerID = curPlayer.GetPlayerID()
-    sendPack.AddFamilyHornor = addFamilyHornor
-    sendPack.AddFamilyMoney = addFamilyMoney
-    sendPack.FamilyActiveValue = addFamilyActiveValue
-    sendPack.AddResion = resion
-    NetPackCommon.SendPyPackToGameServer(sendPack)
-    GameWorld.DebugLog("AddFamilyDetail addFamilyHornor=%s,addFamilyMoney=%s,addFamilyActiveValue=%s, resion=%s" 
-                       % (addFamilyHornor, addFamilyMoney, addFamilyActiveValue, resion), curPlayer.GetPlayerID())
-    return
-#---------------------------------------------------------------------
-##通知世界服务器累加家族属性
-# @param curPlayer 玩家实例
-# @param addFamilyActiveValue 增加玩家家族活跃度
-# @param addFamilyHornor 添加家族荣誉
-# @param addFamilyMoney 添加家族资金
-# @return 返回值无意义
-# @remarks 通知世界服务器累加家族属性
-def SendPack_GameServer_AddFamilyDetailEx(curPlayer, addPlayerActiveValue, addFamilyMoney = 0, addFamilyHornor = 0, resion=0):
-    
-    #刀剑笑增加玩家活跃度默认新增家族活跃度
-    addFamilyActiveValue = GetPlayerActiveValue_Change_ActiveValue(addPlayerActiveValue)
-        
-    SendPack_GameServer_AddFamilyDetail(curPlayer, addFamilyHornor, addFamilyMoney, addFamilyActiveValue, resion)
-    
-    return
-#---------------------------------------------------------------------
-##通知GameServer, 客户端开始创建家族
-# @param curPlayer 玩家实例
-# @return 返回值无意义
-# @remarks 通知GameServer, 客户端开始创建家族
-def SendPack_GameServer_InputFamilyName(curPlayer):
-    #通知GameServer设置查看状态, 防外挂
-    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_CreateFamily)
-    #通知客户端打开输入家族名界面
-    curPlayer.Frm_InputFamilyName()
+    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
+    OnCrossPlayerLogin(crossPlayer)
     return
 
-##通知GameServer, 客户端开始浏览家族信息
-# @param curPlayer 玩家实例
-# @return 返回值无意义
-# @remarks 通知GameServer, 客户端开始浏览家族信息
-def SendPack_GameServer_ViewAllFamily(curPlayer):
-    #通知GameServer设置查看状态, 防外挂
-    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_FamilyWar)
-    #通知GameServer给客户端家族信息
-    curPlayer.GameServer_ViewAllFamily()
+def OnCrossPlayerLogin(crossPlayer):
+    ## 玩家上线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
+    PlayerLoginRefreshFamily(crossPlayer)
+    Sync_RequestAddFamilyInfo(crossPlayer, False)
+    PlayerTalk.NotifyTalkCache(crossPlayer, [IPY_GameWorld.tcFamily]) # 公会聊天缓存
+    #PlayerFamilyTaofa.OnPlayerLogin(curPlayer) 讨伐待修改
     return
 
-##通知GameServer, 清除客户端浏览家族状态
-# @param curPlayer 玩家实例
-# @return 返回值无意义
-# @remarks 通知GameServer, 通知GameServer, 清除客户端浏览家族状态
-def SendPack_GameServer_ClearViewFamilyState(curPlayer):
-    #MapServer告诉GameServer自己是否在创建家族状态
-    #因为家族封包是在GameServer处理的, 防止被骗
-    if curPlayer.GetViewFamilyState() in [ShareDefine.TViewFamilyType_None, 
-                                          ShareDefine.TViewFamilyType_CreateFamily]:
-        #TViewFamilyType_CreateFamily状态在GameServer DoCreateFamily结束后解除
+def OnPlayerLogout(curPlayer):
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员离线逻辑 OnPlayerLogout", curPlayer.GetPlayerID())
         return
-    
-    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_None)
+    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
+    OnCrossPlayerLogout(crossPlayer)
     return
 
-def __SetForbidFamilyAward(curPlayer):
-    ''' 设置家族相关活动领奖限制 
-     如果退出时今日已开始过对应活动,则设置领奖限制
+def OnCrossPlayerLogout(crossPlayer):
+    ## 玩家下线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if not familyID:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    curMember.SetOffTime(int(time.time()))
+    
+    # 通知成员下线
+    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID]) # 成员下线
+    return
+
+def SendToFamilyMapPlayer(crossPlayer, doType, doData):
+    playerID = crossPlayer.GetPlayerID()
+    dataMsg = {"doType":doType, "doData":doData}
+    if GameWorld.IsCrossServer():
+        CrossMsg.SendToClientServer(ShareDefine.C2S_FamilyMapPlayer, dataMsg, [crossPlayer.GetMainServerID()], playerID)
+    else:
+        C2S_FamilyMapPlayer(dataMsg, playerID)
+    return
+
+def MapServer_FamilyRefresh(crossPlayer, familyID, isVoluntarily=0):
+    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
     '''
+    playerID = crossPlayer.GetPlayerID()
+    FmLV = 0 # 职位
+    FamilyLV = 0 # 公会等级
+    FamilyName = ""
+    EmblemID, EmblemWord = 0, ""
+    if familyID:
+        familyMgr = DBDataMgr.GetFamilyMgr()
+        curFamily = familyMgr.FindFamily(familyID)
+        if curFamily:
+            FamilyLV = curFamily.GetLV()
+            FamilyName = curFamily.GetName()
+            EmblemID = curFamily.GetEmblemID()
+            EmblemWord = curFamily.GetEmblemWord()
+            member = curFamily.FindMember(playerID)
+            if member:
+                FmLV = member.GetFmLV()
+        else:
+            familyID = 0
+                    
+    crossPlayer.SetFamilyID(familyID)
+    # 同步更新查看缓存
+    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, familyID, FamilyName, EmblemID, EmblemWord)
+    
+    doData = {"FamilyID":familyID}
+    if familyID:
+        doData.update({"FmLV":FmLV, "FamilyLV":FamilyLV, "FamilyName":FamilyName, "EmblemID":EmblemID, "EmblemWord":EmblemWord})
+    if isVoluntarily:
+        doData["isVoluntarily"] = 1
+    SendToFamilyMapPlayer(crossPlayer, "FamilyRefresh", doData)
+    return
+
+def PlayerLoginRefreshFamily(crossPlayer):
+    ## 玩家登录时刷新家族
+    
+    playerID = crossPlayer.GetPlayerID()
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    refreshFamilyID = familyMgr.GetPlayerFamilyID(playerID)
+    GameWorld.DebugLog("PlayerLoginRefreshFamily playerID=%s,refreshFamilyID=%s" % (playerID, refreshFamilyID))
+    crossPlayer.SetFamilyID(refreshFamilyID)
+    MapServer_FamilyRefresh(crossPlayer, refreshFamilyID) # 登录
+    familyID = refreshFamilyID
+    if not familyID:
+        return
+    
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    curMember.SetOffTime(0) # 在线0,脱机1,>1离线时间
+    curMember.RefreshMemberByID(playerID)
+    
+    Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
+    # 广播成员在线
+    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员登录
+    
+    #通知招人
+    if GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
+        CrossPlayer.SendFakePack(crossPlayer, GetPack_FamilyReqJoinInfo(familyID))
+        
+    # 盟主上线处理
+    #if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
+    #    OnFamilyLeaderLogin(curPlayer)
+    return
+
+def RefreshFamilyMember(crossPlayer):
+    ## 玩家成员相关属性变更时同步更新家族成员信息
+    if GameWorld.IsCrossServer():
+        pass
+    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
+        return
+    familyID = crossPlayer.GetFamilyID()
+    if not familyID:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    playerID = crossPlayer.GetPlayerID()
+    member = family.FindMember(playerID)
+    if not member:
+        return
+    member.RefreshMemberByID(playerID)
+    return
+
+def FamilyPyPackForwarding(curPlayer, clientData, tick, funcName, needResult=False, reqCD=0.5, reqDataEx=None):
+    ## 玩家请求公会功能包转发处理
+    # @needResult: 转发跨服处理完毕是否需要回复状态,一般有消耗货币、物品相关的都要回复,且设置一个较长请求cd,防止重复处理
+    # @param reqCD: 转发跨服请求cd,秒,支持小数
     
     playerID = curPlayer.GetPlayerID()
-    openState = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_FamilyActivityDayState)
-    forbidState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAward)
-    GameWorld.DebugLog("退出家族处理家族活动奖励领取限制! openState=%s,forbidState=%s" % (openState, forbidState), playerID)
     
-    updForbidState = forbidState
-    for activityType in ShareDefine.Def_FamActivityList:
-        
-        if not openState&pow(2, activityType):
-            GameWorld.DebugLog("    该家族活动今日未开启过! 不设置限制领奖! activityType=%s" % activityType, playerID)
-            continue
-        updForbidState = updForbidState|pow(2, activityType)
-        GameWorld.DebugLog("    家族活动领奖限制 activityType=%s,updForbidState=%s" % (activityType, updForbidState), playerID)
-       
-    if updForbidState != forbidState:
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_ForbidFamilyAward, updForbidState)
-        GameWorld.DebugLog("    更新家族活动领奖限制: forbidState=%s,updForbidState=%s" % (forbidState, updForbidState), playerID)
-        
-    return
-
-## 设置玩家是否可以参加家族活动
-#  @param curPlayer 玩家实例
-#  @param isForbid 是否限制
-#  @return 返回值无意义
-def SetForbidFamilyAction(curPlayer, isForbid):
-    #===============================================================================================
-    # if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAction) != isForbid:
-    #    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_ForbidFamilyAction, isForbid)
-    # 
-    #    # 通知玩家是否可以参加家族活动
-    #    SendClientForbidFamilyAction(curPlayer)
-    #===============================================================================================
-    return
-
-
-## 通知玩家是否可以参加家族活动
-#  @param curPlayer 玩家实例
-#  @return 返回值无意义
-def SendClientForbidFamilyAction(curPlayer):
-#    isForbid = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAction)
-#    
-#    outCardsCDTime = ChPyNetSendPack.tagPyFamilyActionForbid()
-#    outCardsCDTime.Clear()
-#    
-#    outCardsCDTime.IsForbid = isForbid
-#    NetPackCommon.SendFakePack(curPlayer, outCardsCDTime)
-    return
-
-    
-#===============================================================================
-# //A6 01  向玩家申请加入家族 #tagCGRequestJoinFamilyByPlayer
-# 
-# struct    tagCGRequestJoinFamilyByPlayer
-# 
-# {
-#    tagHead        Head;
-#    DWORD        AddPlayerID;    //申请加入的玩家ID
-# };
-#===============================================================================
-## 通过申请加入家族
-#  @param index: 玩家索引
-#  @param clientData: 封包结构体
-#  @param tick: 时间戳
-#  @return: None
-def RequestAddFamilyByID(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    addPlayerID = clientData.AddPlayerID    #申请进入的家族ID
-    
-    addPlayer = GameWorld.GetPlayerManager().FindPlayerByID(addPlayerID)
-    #对方下线
-    if not addPlayer:
+    crossServerID = DBDataMgr.GetFamilyMgr().GetCurCrossServerID()
+    if crossServerID == -2:
+        PlayerControl.NotifyCode(curPlayer, "FamilyInTransData")
         return
     
-    addFamilyID = addPlayer.GetFamilyID()
-    #对方没有家族
-    if addFamilyID <= 0:
+    if crossServerID < 0:
+        GameWorld.ErrLog("公会功能异常! crossServerID=%s" % crossServerID)
         return
     
-    #申请加入
-    AddFamily(curPlayer, addFamilyID)
-    
-    #通知当前申请加入的哪些家族
-    #Sync_RequestAddFamilyInfo(curPlayer) 
-    return
-    
-    
-#===============================================================================
-# //A6 02  申请加入家族#tagCGRequesJoinFamily
-# 
-# struct    tagCGRequesJoinFamily
-# 
-# {
-#    tagHead        Head;
-#    BYTE        Type;        //申请类型
-#    DWORD        AddFamilyID;    //申请加入的家族
-# };
-#===============================================================================
-## 申请加入家族
-#  @param index: 玩家索引
-#  @param clientData: 封包结构体
-#  @param tick: 时间戳
-#  @return: None
-def RequestAddFamily(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    addFamilyID = clientData.AddFamilyID  # 申请进入的家族ID
-    requestType = clientData.Type   # 申请类型(申请/撤销)
-
-    if requestType == 0:
-        #申请加入
-        AddFamily(curPlayer, addFamilyID)
-        
-    elif requestType == 1:
-        DelAddFamilyRecord()
-        
-    
-    #通知当前申请加入的哪些家族
-    Sync_RequestAddFamilyInfo(curPlayer) 
-    return
-
-def DelAddFamilyRecord(curPlayer):
-    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
-            
-        #申请加入的家族ID
-        requestAddFamilyID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index) 
-        if not requestAddFamilyID:
-            continue
-        #本版本因为客户端没有单个取消申请的操作,此处发一次包一次性全部取消
-#            if addFamilyID != requestAddFamilyID:
-#                continue
-        
-        #申请数量-1
-        requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyCnt, requestAddFamilyCnt - 1)
-        
-        #清除撤销申请的家族ID记录
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyID%index, 0)
-        
-        #向GameServer发送请求删除该玩家的申请家族信息情况
-        sendMsg = str(requestAddFamilyID)
-        curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_DelFamilyAction, 
-                                     ShareDefine.Def_ActionType_FamilyAdd, '', sendMsg, len(sendMsg))
-    
-    Sync_RequestAddFamilyInfo(curPlayer)
-    return
-
-## 通知当前申请加入的哪些家族
-#  @param curPlayer:玩家实例
-#  @return:  None
-def Sync_RequestAddFamilyInfo(curPlayer):
-    requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
-    
-    packList = []
-    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
-        familyID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index)
-        if familyID == 0:
-            continue
-        
-        requestAddFamilyInfo = ChPyNetSendPack.tagRequestJoinFamily()
-        requestAddFamilyInfo.RequestFamilyID = familyID
-        packList.append(requestAddFamilyInfo)
-    
-    requestAddFamily = ChPyNetSendPack.tagMCNotifyRequestJoinFamilyInfo()
-    requestAddFamily.Clear()
-    requestAddFamily.RequestCount = len(packList)
-    requestAddFamily.RequestAddFamilyInfo = packList
-    
-    NetPackCommon.SendFakePack(curPlayer, requestAddFamily)
-    return
-
-
-## 申请加入帮会
-#@param curPlayer 家族实例
-#@param familyID 家族ID
-#@return 返回值无意义
-def AddFamily(curPlayer, familyID):
-    
-    requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
-    if requestAddFamilyCnt >= ChConfig.Def_Player_RequestAddFamilyMaxCnt:
-        # jiazu_pan_21675 改为 jiazu_lhs_31379 最多同时向%s个帮会发出申请
-        PlayerControl.NotifyCode(curPlayer, "jiazu_lhs_31379", [ChConfig.Def_Player_RequestAddFamilyMaxCnt])
+    # 本服处理
+    if crossServerID == 0 or crossServerID == GameWorld.GetGameWorld().GetServerID():
+        isOK = CallPyPackFunc(playerID, clientData, funcName, reqDataEx=reqDataEx)
+        if needResult:
+            __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
         return
-        
-    #是否已经申请了
-    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
-        if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index) == familyID:
-            GameWorld.DebugLog('已经在申请列表中')
-            #已经在申请列表中
+    
+    ssServer = CrossMgr.GetSSServerMgr().GetSSServer(crossServerID)
+    if not ssServer or not ssServer.IsServerOpen():
+        PlayerControl.NotifyCode(curPlayer, "ServerNoOpen")
+        return
+    
+    # 转发请求CD验证
+    if reqCD:
+        reqTick = curPlayer.GetDictByKey(funcName) # 根据函数名单独处理CD,防止相互影响
+        if reqTick and (tick - reqTick) < reqCD * 1000:
+            PlayerControl.NotifyCode(curPlayer, "RequestLater")
             return
-
-    #请求记录申请进入家族信息
-    sendMsg = cPickle.dumps([familyID, PlayerControl.GetFightPower(curPlayer)], 2)
-    curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_AddFamilyAction, 
-                                        ShareDefine.Def_ActionType_FamilyAdd, '', sendMsg, len(sendMsg))
-    
-    #等GameServer反馈结果再处理
-    return
-    
-    #记录个人申请加入的家族ID
-    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
-        if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index) == 0:
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyID%index, familyID)
-            break
+        curPlayer.SetDict(funcName, tick)
         
-    #记录个人申请加入家族的个数
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyCnt, requestAddFamilyCnt + 1)
-    
+    # 剩下的就是大于0
+    dataMsg = {"funcName":funcName}
+    if clientData:
+        dataMsg["packBuff"] = clientData.GetBuffer()       
+    if reqDataEx:
+        dataMsg["reqDataEx"] = reqDataEx
+    if needResult:
+        dataMsg["needResult"] = 1
+    CrossMsg.SendToCrossServer(ShareDefine.S2C_FamilyPyPack, dataMsg, [crossServerID], playerID)
     return
 
+def S2C_FamilyPyPack(dataMsg, fromServerID, playerID):
+    funcName = dataMsg["funcName"]
+    packBuff = dataMsg.get("packBuff")
+    reqDataEx = dataMsg.get("reqDataEx")
+    clientData = None
+    if packBuff:
+        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
+        if not clientData:
+            return
+    isOK = CallPyPackFunc(playerID, clientData, funcName, fromServerID, reqDataEx)
+    # 处理结束回复,无论成功与否
+    if "needResult" in dataMsg:
+        dataMsg.pop("needResult")
+        dataMsg["isOK"] = isOK
+        CrossMsg.SendToClientServer(ShareDefine.C2S_FamilyPyPackRet, dataMsg, [fromServerID], playerID)
+    return
 
-
-
-    
-## 战盟玩家OnDay
-#  @param curPlayer
-#  @return None
-def FamilyPlayerOnDay(curPlayer):
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
+def C2S_FamilyPyPackRet(dataMsg, playerID):
+    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+    if not curPlayer:
         return
-    #每日福利奖励
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDayAward, 0)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyMoneyDonateCount, 0)
-    Sync_FamilyDayRewardState(curPlayer)
-    __FamilyAffair_Refresh(curPlayer, True)
+    funcName = dataMsg["funcName"]
+    packBuff = dataMsg.get("packBuff")
     
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeCut, 0)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy, 0)
-    Sync_ZhenbaogeInfo(curPlayer)
+    curPlayer.SetDict(funcName, 0) # 重置CD
+    
+    clientData = None
+    if packBuff:
+        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
+        if not clientData:
+            return
+    isOK = dataMsg.get("isOK")
+    __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
     return
 
-
-## 战盟玩家OnLogin
-#  @param curPlayer
-#  @return None
-def FamilyPlayerOnLogin(curPlayer, tick):
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
+def CallPyPackFunc(playerID, clientData, funcName, fromServerID=0, reqDataEx=None):
+    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(playerID)
+    if not crossPlayer:
+        GameWorld.ErrLog("找不到该CrossPlayer! playerID=%s" % playerID, playerID)
         return
+    
+    callFunc = GetCallFunc(funcName)
+    if not callFunc:
+        GameWorld.ErrLog("公会封包功能函数名不存在! %s" % funcName)
+        return
+    
+    # 执行函数
+    tick = GameWorld.GetGameWorld().GetTick()
+    isOK = callFunc(crossPlayer, clientData, tick, fromServerID, reqDataEx)
+    return isOK
 
-    PlayerFamilyTech.Sync_PlayerFamilyTechLV(curPlayer)
-    SyncFamilyActivityInfo(curPlayer)
-    Sync_FamilyDayRewardState(curPlayer)
-    __FamilyAffair_CheckReset(curPlayer)
-    PlayerFamilyZhenfa.OnPlayerLogin(curPlayer)
-    Sync_ZhenbaogeInfo(curPlayer)
+def __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK):
+    ## 处理后额外处理,如成就、任务等
+    funcName = "%s_Ret" % funcName
+    callFunc = GetCallFunc(funcName)
+    if not callFunc:
+        # 结果额外处理函数允许不一定需要,根据功能自行决定
+        return
+    callFunc(curPlayer, clientData, isOK)
     return
 
-def FamilyPlayerOnLoginCross(curPlayer):
-    ## 登录跨服服务器
-    crossFamilyMemberLV = curPlayer.GetReceivedSalary()
-    if crossFamilyMemberLV:
-        curPlayer.SetFamilyMemberLV(crossFamilyMemberLV)
-        GameWorld.DebugLog("跨服登录设置仙盟职位等级: %s" % crossFamilyMemberLV, curPlayer.GetPlayerID())
+def GetCallFunc(funcName):
+    callFunc = None
+    if "." in funcName:
+        # 分割字符串,最后一个部分是函数名
+        parts = funcName.split('.')
+        if len(parts) == 2:
+            moduleName, func_name = parts
+            # 导入模块
+            module = __import__(moduleName)
+            # 获取函数
+            if hasattr(module, func_name):
+                func = getattr(module, func_name)
+                if callable(func):
+                    callFunc = func
+    else:
+        gDict = globals()
+        if funcName in gDict and callable(gDict[funcName]):
+            # 获取函数对象
+            callFunc = gDict[funcName]
+    return callFunc
+
+#// A6 04 创建家族 #tagCMCreateFamily
+#
+#struct    tagCMCreateFamily
+#{
+#    tagHead        Head;
+#    char        Name[33];
+#    WORD        EmblemID; //选择徽章ID,解锁公会等级为1级的均为可选ID
+#    char        EmblemWord[3];    //徽章文字
+#};
+def OnCreateFamily(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    inputName = clientData.Name
+    
+    GameWorld.DebugLog("OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
+    
+    # 通用的前置验证,直接游戏服处理即可
+    if not CheckInputFamilyName(curPlayer, inputName):
+        return
+    
+    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
+    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
+    if moneyType and needMoney:
+        if not PlayerControl.HaveMoney(curPlayer, moneyType, needMoney):
+            return
+        
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnCreateFamily", True, 20)
+    return
+
+def __OnCreateFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    inputName = clientData.Name
+    emblemID = clientData.EmblemID
+    emblemWord = clientData.EmblemWord
+    
+    GameWorld.DebugLog("__OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
+    
+    fullFamilyName = inputName
+    
+    playerID = crossPlayer.GetPlayerID()
+    playerFamilyID = crossPlayer.GetFamilyID()
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    if playerFamilyID:
+        curFamily = familyMgr.FindFamily(playerFamilyID)
+        if curFamily:
+            if curFamily.FindMember(playerID):
+                #玩家已经有家族, 创建失败
+                CrossPlayer.NotifyCode(crossPlayer, "GeRen_chenxin_85890")
+                return
+            
+    # 验证重名
+    if CheckFamilyNameExists(crossPlayer, fullFamilyName, fromServerID):
+        return
+    
+    serverID = crossPlayer.GetServerID()
+    curFamily = familyMgr.AddFamily(fullFamilyName, serverID)
+    if curFamily == None:
+        GameWorld.ErrLog("创建家族失败", playerID)
+        return
+    newFamilyID = curFamily.GetID()
+    zoneMgr = familyMgr.GetZoneFamilyMgrByFamilyID(newFamilyID)
+    zoneID = zoneMgr.GetZoneID() if zoneMgr else -1
+    curFamily.SetLV(1)
+    emblemIDList = PlayerFamilyEmblem.GetDefaultFamilyEmblemIDList()
+    if not emblemID or emblemID not in emblemIDList:
+        emblemID = random.choice(emblemIDList) # 从默认徽章中随机选择一个
+    GameWorld.Log("创建公会: familyID=%s,playerID=%s,emblemID=%s,serverID=%s,zoneID=%s" % (newFamilyID, playerID, emblemID, serverID, zoneID))
+    curFamily.SetEmblemID(emblemID)
+    curFamily.SetEmblemWord(emblemWord)
+    
+    # 扣除消耗
+    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
+    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
+    if moneyType and needMoney:
+        CrossPlayer.CostPlayerResources(crossPlayer, "CreateFamily", costMoneyDict={moneyType:needMoney})
+        
+    #-设置家族成员属性
+    DoPlayerJionFamily(curFamily, playerID, crossPlayer, IPY_PlayerDefine.fmlLeader)
+    if zoneMgr:
+        zoneMgr.Sort()
+        
+    #XW_JZ_EstablishSud <n color="255,255,0">恭喜您,家族建立成功!</n>    25  -   -
+    #CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_EstablishSud")
+    #PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
+    PlayerFamilyZhenbaoge.OnZhenbaogeReset(curFamily)
+    return True
+
+def CheckInputFamilyName(curPlayer, inputName):
+    '''检查玩家输入的公会名是否合法,建盟、改名通用
+    【注】该函数仅在游戏服验证名字的通用合法性,如长度、敏感词等,公会名重名请在公会所在数据服验证
+    @return: None-不合法;非空-合法的公会全名
+    '''
+    #C++过滤空格
+    familyName = GameWorld.GetGameWorld().GetCharTrim(inputName)
+    
+    serverID = GameWorld.GetPlayerServerID(curPlayer)
+    maxServerID = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 3)
+    if serverID > maxServerID or serverID <= 0:
+        GameWorld.ErrLog("公会全名 serverID=%s error! maxServerID=%s, check FamilyNameFormat.txt" % (serverID, maxServerID))
+        return
+    
+    maxLen = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 2)
+    if len(familyName) > maxLen:
+        GameWorld.ErrLog("公会全名 familyName=%s, len=%s > %s, check FamilyNameFormat.txt" % (familyName, len(familyName), maxLen))
+        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [maxLen / 3, maxLen])
+        return
+    
+    if DirtyList.IsWordForbidden(familyName):
+        #XW_JZ_Family_NameNoLegality 对不起,家族名称中含有非法字符
+        PlayerControl.NotifyCode(curPlayer, "NameSensitive")
+        return
+    
+    if len(familyName) <= 0 or len(familyName) > Def_CreatFamily_MaxStr:
+        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [Def_CreatFamily_MaxStr / 3, Def_CreatFamily_MaxStr])
+        return
+    
+    return True
+
+def CheckFamilyNameExists(crossPlayer, familyName, fromServerID=0):
+    ## 重名在数据所在服务器验证
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    zoneID = familyMgr.GetZoneIDInThisServer(fromServerID)
+    if zoneID < 0:
+        GameWorld.ErrLog("验证公会重名时分区异常也视为重名")
+        return True
+    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
+    # 不同分区暂时允许重名
+    if zoneMgr.FindFamilyByName(familyName):
+        #XW_JZ_EstablishErr_Name    <n color="255,255,0">对不起,您输入的家族名已存在,建立家族失败!</n> 25  -   -
+        CrossPlayer.NotifyCode(crossPlayer, "NameExists")
+        return True
+    return False
+
+def DoPlayerJionFamily(family, playerID, crossPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
+    '''加入家族,支持离线玩家加入
+    @param crossPlayer: 如果是离线玩家则为None
+    '''
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    if isinstance(family, int):
+        familyID = family
+        curFamily = familyMgr.FindFamily(familyID)
+    else:
+        curFamily = family
+        
+    if not curFamily:
+        return
+    
+    familyID = curFamily.GetID()
+    member = curFamily.AddMember(playerID)
+    member.SetFmLV(jionFamilySetLv)
+    member.RefreshMemberByID(playerID)
+    
+    if jionFamilySetLv == IPY_PlayerDefine.fmlLeader:
+        curFamily.SetLeaderID(playerID)
+        
+    if broadcastFamilyChange:
+        # 广播其他在线成员
+        Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员加入
+        
+    familyMgr.DelPlayerReqJoinFamilyIDAll(playerID)
+    #设置玩家身上保存的家族信息
+    if crossPlayer:
+        MapServer_FamilyRefresh(crossPlayer, familyID) # 加入
+        Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
+        Sync_RequestAddFamilyInfo(crossPlayer)
+        PlayerFamilyTaofa.OnCrossPlayerEnterFamily(crossPlayer)
+        
+    if jionFamilySetLv != IPY_PlayerDefine.fmlLeader:
+        #通知所有家族成员, 这个人加入了家族
+        CrossPlayer.FamilyNotify(familyID, "XW_JZ_EnterFamily", [member.GetPlayerName()], excludeIDList=[playerID])
+        #if jionPlayer:
+        #    PlayerControl.NotifyCode(jionPlayer, 'XW_JZ_EnterFamilyInfo', [family.GetName()])
+            
+    #记录加入事件
+    tick = GameWorld.GetGameWorld().GetTick()
+    AddFamilyActionNote(member.GetPlayerName(), familyID, ShareDefine.Def_ActionType_FamilyEvent, 
+                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_Join, jionFamilySetLv, 0], tick)
+    return
+
+def AddFamilyActionNote(curName, familyID, actionType, actionDataList, tick, isClearNone=True, useData=""):
+    '''统一添加家族Action数据
+    '''
+    if actionType not in ShareDefine.Def_ActionTypeList:
+        #记录行为类型错误
+        GameWorld.ErrLog("记录家族行为类型:%s 错误")
+        return False
+    
+    familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
+    actionData = familyAction.AddAction()
+    if not actionData:
+        return False
+    
+    #---设置行为对象数据---
+    actionData.SetName(curName)  # 设置名字
+    actionData.SetUserData(useData) # 设置自定义数据
+    for num, value in enumerate(actionDataList, 1):
+        setFunc = getattr(actionData, "SetValue%s" % num)
+        if setFunc:
+            setFunc(value)
+    return True
+
+def GetFamilyMemberHasPow(member, powerID):
+    ## 公会成员是否有该权限
+    powerDict = IpyGameDataPY.GetFuncEvalCfg("FamilyPower", 1, {})
+    if str(powerID) not in powerDict:
+        return False
+    needMemberLV = powerDict[str(powerID)]
+    return member.GetFmLV() >= needMemberLV
+
+def Sync_FamilyInfo(crossPlayer, syncMemIDList=None, isSyncMem=True):
+    ## 通知指定玩家 // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
+    familyID = crossPlayer.GetFamilyID()
+    if not familyID:
+        return
+    clientPack = GetPack_FamilyInfo(familyID, syncMemIDList, isSyncMem)
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
+    return
+
+def Broadcast_FamilyInfo(familyID, changeMemIDList=None, isSyncMem=True, excludeIDList=None):
+    ## 广播给公会成员 // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
+    # @param changeMemIDList: 指定仅通知哪些变化成员信息,差异更新通知
+    # @param isSyncMem: 是否需要通知成员信息
+    clientPack = GetPack_FamilyInfo(familyID, changeMemIDList, isSyncMem)
+    CrossPlayer.SendFakePackByFamily(familyID, clientPack, excludeIDList)
+    return
+
+def GetPack_FamilyInfo(familyID, changeMemIDList=None, isSyncMem=True):
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    curFamily = familyMgr.FindFamily(familyID)
+    if not curFamily:
+        return
+    
+    clientPack = ChPyNetSendPack.tagMCRoleFamilyInfo()
+    clientPack.FamilyID = familyID
+    clientPack.FamilyName = curFamily.GetName()
+    clientPack.FamilyLV = curFamily.GetLV()
+    clientPack.FamilyLVExp = curFamily.GetExp()
+    clientPack.JoinReview = curFamily.GetJoinReview()
+    clientPack.JoinLVMin = curFamily.GetJoinLVMin()
+    clientPack.ServerID = curFamily.GetServerID()
+    clientPack.EmblemID = curFamily.GetEmblemID()
+    clientPack.EmblemWord = curFamily.GetEmblemWord()
+    clientPack.FightPower = curFamily.GetFightPower()
+    clientPack.FightPowerEx = curFamily.GetFightPowerEx()
+    clientPack.Broadcast = curFamily.GetBroadcast()
+    clientPack.BroadcastLen = len(clientPack.Broadcast)
+    clientPack.LeaderID = curFamily.GetLeaderID()
+    clientPack.MemberList = []
+    # ---------------
+    # 测试用,同步全部,等前端同步修改后删除
+    #isSyncMem = True 
+    #changeMemIDList = []
+    # ---------------
+    if isSyncMem:
+        for index in xrange(curFamily.GetCount()):
+            member = curFamily.GetAt(index)
+            memID = member.GetPlayerID()
+            if changeMemIDList and memID not in changeMemIDList:
+                continue
+            memInfo = ChPyNetSendPack.tagMCRoleFamilyMember()
+            memInfo.PlayerID = member.GetPlayerID()
+            memInfo.JoinTime = member.GetJoinTime()
+            memInfo.Name = member.GetPlayerName()
+            memInfo.NameLen = len(memInfo.Name)
+            memInfo.LV = member.GetLV()
+            memInfo.Job = member.GetJob()
+            memInfo.RealmLV = member.GetRealmLV()
+            memInfo.Face = member.GetFace()
+            memInfo.FacePic = member.GetFacePic()
+            memInfo.TitleID = member.GetTitleID()
+            memInfo.FightPower = member.GetFightPower()
+            memInfo.FightPowerEx = member.GetFightPowerEx()
+            memInfo.FmLV = member.GetFmLV()
+            memInfo.ServerID = member.GetServerID()
+            memInfo.ContribTotal = member.GetContribTotal()
+            memInfo.ContribDay = member.GetContribDay()
+            memInfo.DonateCntTotal = member.GetDonateCntTotal()
+            memInfo.DonateCntDay = member.GetDonateCntDay()
+            memInfo.OffTime = member.GetOffTime()
+            clientPack.MemberList.append(memInfo)
+    clientPack.MemberCount = len(clientPack.MemberList)
+    clientPack.Extra1 = curFamily.GetExtra1()
+    return clientPack
+
+def GetPack_FamilyDel(delPlayerID, playerName, delType=0):
+    # @param delType: 0-踢出;1-主动退出
+    clientPack = ChPyNetSendPack.tagSCFamilyMemDel()
+    clientPack.Type = delType
+    clientPack.PlayerID = delPlayerID
+    clientPack.Name = playerName
+    clientPack.NameLen = len(clientPack.Name)
+    return clientPack
+    
+#// A6 01 向玩家申请加入家族 #tagCMRequestJoinFamilyByPlayer
+#
+#struct    tagCMRequestJoinFamilyByPlayer
+#{
+#    tagHead        Head;
+#    DWORD        TagPlayerID;    //目标家族玩家ID
+#};
+def OnRequestJoinFamilyByPlayer(index, clientData, tick):
+    #屏蔽,默认只使用 A6 02
+    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    #tagPlayerID = clientData.TagPlayerID
+    #tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagPlayerID)
+    #if not tagPlayer:
+    #    GameWorld.DebugLog("对方不在线! tagPlayerID=%s" % tagPlayerID)
+    #    return
+    #tagFamilyID = tagPlayer.GetFamilyID()
+    #if tagFamilyID <= 0:
+    #    GameWorld.DebugLog("对方没有家族! tagPlayerID=%s" % tagPlayerID)
+    #    return
+    #RequestJoinTagFamily(curPlayer, tagFamilyID)
+    return
+
+#// A6 02 申请加入家族#tagCMRequesJoinFamily
+#
+#struct    tagCMRequesJoinFamily
+#{
+#    tagHead        Head;
+#    BYTE        Type;        //申请类型,0-申请;1-撤销
+#    DWORD        TagFamilyID;    //目标家族ID,申请时为0代表一键申请家族任意家族
+#};
+def OnRequesJoinFamily(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    requestType = clientData.Type
+    if requestType == 0:
+        if CheckInJoinCD(curPlayer):
+            return
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnRequesJoinFamily", True)
+    return
+
+def __OnRequesJoinFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    tagFamilyID = clientData.TagFamilyID  # 申请进入的家族ID
+    requestType = clientData.Type   # 申请类型(申请/撤销)
+    GameWorld.DebugLog("__OnRequesJoinFamily: tagFamilyID=%s,requestType=%s" % (tagFamilyID, requestType))
+    
+    # 申请加入
+    if requestType == 0:
+        if not tagFamilyID:
+            AutoJoinFamily(crossPlayer)
+        else:
+            RequestJoinTagFamily(crossPlayer, tagFamilyID)
+            
+    # 撤销申请
+    elif requestType == 1:
+        CancelJoinTagFamily(crossPlayer, tagFamilyID)
+        
+    return True
+
+def __OnRequesJoinFamily_Ret(curPlayer, clientData, isOK):
+    requestType = clientData.Type   # 申请类型(申请/撤销)
+    # 申请加入
+    if requestType == 0:
+        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily, 1)
         
     return
 
+def CheckInJoinCD(curPlayer):
+    ## 检查是否加入公会CD中
+    leaveFamilyTime = PlayerControl.GetLeaveFamilyTimeEx(curPlayer)
+    if not leaveFamilyTime:
+        return False
+    
+    leaveCnt, kickedCnt, lastVoluntarily = PlayerControl.GetLeaveFamilyInfo(curPlayer)
+    joinCDMinute = 0
+    if lastVoluntarily:
+        if leaveCnt <= 0:
+            return False
+        joinCDMinuteList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 1)
+        if joinCDMinuteList:
+            joinCDMinute = joinCDMinuteList[leaveCnt - 1] if len(joinCDMinuteList) >= leaveCnt else joinCDMinuteList[-1]
+    else:
+        if kickedCnt <= 0:
+            return False
+        joinCDMinuteList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 2)
+        if joinCDMinuteList:
+            joinCDMinute = joinCDMinuteList[kickedCnt - 1] if len(joinCDMinuteList) >= kickedCnt else joinCDMinuteList[-1]
+    if joinCDMinute:
+        cdTimes = joinCDMinute * 60
+        passTimes = int(time.time()) - leaveFamilyTime
+        if passTimes < cdTimes:
+            GameWorld.DebugLog("加入公会CD中: leaveCnt=%s,kickedCnt=%s,lastVoluntarily=%s,leaveFamilyTime=%s(%s),passTimes=%s < %s" 
+                   % (leaveCnt, kickedCnt, lastVoluntarily, leaveFamilyTime, GameWorld.ChangeTimeNumToStr(leaveFamilyTime), passTimes, cdTimes))
+            return True
+    return False
 
-
-
-## 通知GameServer增加家族相关信息值
-# @param curPlayer 玩家实例
-# @param infoDict 信息字典
-# @return 
-def Send_GameServer_PyAddFamilyInfoValue(curPlayer, infoDict={}):
-    if not isinstance(infoDict, dict) or not infoDict:
-        GameWorld.DebugLog("Send_GameServer_PyAddFamilyInfoValue infoDict=%s err!" % str(infoDict))
+def AutoJoinFamily(crossPlayer):
+    if crossPlayer.GetFamilyID():
+        return
+    playerID = crossPlayer.GetPlayerID()
+    realmLV = crossPlayer.GetRealmLV()
+    GameWorld.DebugLog("玩家一键自动加入家族! realmLV=%s" % realmLV, playerID)
+    #if CheckInJoinCD(curPlayer):
+    #    return
+    
+    mainServerID = crossPlayer.GetMainServerID()
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    zoneID = familyMgr.GetZoneIDInThisServer(mainServerID)
+    if zoneID < 0:
+        return
+    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
+    indexList = range(zoneMgr.GetCount())
+    random.shuffle(indexList) #打乱顺序
+    for index in indexList:
+        family = zoneMgr.GetAt(index)
+        if not family:
+            continue
+        #familyID = family.GetID()
+        lvMin = family.GetJoinLVMin()
+        if lvMin and realmLV < lvMin:
+            #GameWorld.DebugLog("    官职不足的不处理! familyID=%s,lvMin=%s" % (familyID, lvMin), playerID)
+            continue
+        if family.GetJoinReview():
+            #GameWorld.DebugLog("    需要审核的不处理! familyID=%s" % familyID, playerID)
+            continue
+        MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
+        if family.GetCount() >= MemberMax:
+            #GameWorld.DebugLog("    成员已满的不处理! familyID=%s" % familyID, playerID)
+            continue
+        
+        #直接加入
+        DoPlayerJionFamily(family, playerID, crossPlayer)
         return
     
-    sendMsg = '%s'%(infoDict)
-    #GameWorld.DebugLog("Send_GameServer_PyAddFamilyInfoValue sendMsg=%s" % sendMsg)
-    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, 'PyAddFamilyInfoValue', sendMsg, len(sendMsg))
+    # 可再扩展自动请求,暂时不处理
+    GameWorld.DebugLog("没有可自动进入的公会!")
+    CrossPlayer.NotifyCode(crossPlayer, "QuickEnterFamilyFail")
     return
 
-#---------------------------------------------------------------------
-
-
-
-## 添加家族事件记录
-def AddFamilyEventNote(curPlayer, eventType, valueList):
-    ##同步记录到GameServer, eventType默认为value1
-    sendMsg = cPickle.dumps([eventType] + valueList, 2)
-    curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_AddFamilyAction, ShareDefine.Def_ActionType_FamilyEvent, '', sendMsg, len(sendMsg))
-    GameWorld.DebugLog("AddFamilyEventNote sendMsg=%s" % sendMsg)
-    return
-
-
-## --------------------------------- 仙盟活跃 -----------------------------------
-def OnWeekEx(curPlayer):
-    #重置
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
-    for actionid in ShareDefine.FamilyActiveIDList:
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
-    
-    SyncFamilyActivityInfo(curPlayer)
-    return
-
-## 领取仙盟活跃度奖励
-#  @param curPlayer
-#  @param awardIndex
-#  @return None
-def GetFamilyActivityAward(curPlayer, awardIndex):
-    return
-    familyActiveList = IpyGameDataPY.GetFuncEvalCfg('FamilyActive')
-    familyActiveAwardList = IpyGameDataPY.GetFuncEvalCfg('FamilyActive', 2)
-    if awardIndex < 0 or awardIndex >= len(familyActiveList):
-        GameWorld.ErrLog("活跃度奖励索引不合法,index=%s,Len=%s" % (awardIndex, len(familyActiveList)))
-        return
-    
-    # 判断是否已领取
-    getAwardRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
-    if getAwardRecord & pow(2, awardIndex):
-        GameWorld.Log("已经领取过该活跃度奖励,index=%s" % (awardIndex))
-        return
-    
-    
-    needActivity = familyActiveList[awardIndex]
-    totalActivity = __GetTotalFamilyActivity(curPlayer)#总活跃度
-    
-    # 判断活跃度
-    if totalActivity < needActivity:
-        GameWorld.Log("领奖活跃度不足,index=%s,needActivity=%s,totalActivity=%s" 
-                      % (awardIndex, needActivity, totalActivity))
-        return
-   
-    activityValue = familyActiveAwardList[awardIndex]
-    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, activityValue)
-    
-    # 更新已领取成功标记
-    updAwardRecord = getAwardRecord | (1 << awardIndex)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityAwardRecord, updAwardRecord)
-    
-    SyncFamilyActivityInfo(curPlayer)
-    GameWorld.DebugLog("领取仙盟活跃度奖励OK! index=%s,needActivity=%s,totalActivity=%s,awardRecord=%s" 
-                       % (awardIndex, needActivity, totalActivity, updAwardRecord))
-    return
-
-def AddFamilyActivity(curPlayer, actionid, addCnt=1):
-    return
-    ipyData = IpyGameDataPY.GetIpyGameData('FamilyActivity', actionid)
+def GetFamilySetting(familyLV, fieldName):
+    ## 获取公会等级表对应字段值
+    if not fieldName:
+        return 0
+    ipyData = IpyGameDataPY.GetIpyGameData("Family", familyLV)
     if not ipyData:
+        return 0
+    attrName = "Get%s" % fieldName
+    if not hasattr(ipyData, attrName):
+        return 0
+    return getattr(ipyData, attrName)()
+
+def RequestJoinTagFamily(crossPlayer, familyID):
+    ## 申请加入
+    #if CheckInJoinCD(curPlayer):
+    #    return
+    playerID = crossPlayer.GetPlayerID()
+    if crossPlayer.GetFamilyID():
+        GameWorld.DebugLog('已经有公会不能再申请加入! familyID=%s' % crossPlayer.GetFamilyID(), playerID)
         return
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ipyData.GetUnLockFuncID()):
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
+    if playerID in reqFamilyIDList:
+        GameWorld.DebugLog('已经在申请加入公会列表中! familyID=%s' % familyID, playerID)
         return
-    finishCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
-    maxCnt = ipyData.GetTotalActivityTime()
-    if finishCnt >= maxCnt:
+    
+    maxReqFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 2)
+    if len(reqFamilyIDList) >= maxReqFamilyCnt:
+        GameWorld.DebugLog('已经达到最大申请加入公会数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
         return
-    newCnt = min(maxCnt, finishCnt+addCnt)
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, newCnt)
-    SyncFamilyActivityInfo(curPlayer, actionid)
-    GameWorld.DebugLog('    仙盟活跃完成次数 actionid=%s,newCnt=%s'%(actionid, newCnt))
+    
+    tagFamily = familyMgr.FindFamily(familyID)
+    if not tagFamily:
+        return
+    
+    lvMin = tagFamily.GetJoinLVMin()
+    if crossPlayer.GetRealmLV() < lvMin:
+        GameWorld.DebugLog('官职未达到该公会加入最低限制! realmLV=%s < %s' % (crossPlayer.GetRealmLV(), lvMin), playerID)
+        return
+    
+    # 需要审核,满员后端不限制申请,由前端自行决定是否可申请
+    if tagFamily.GetJoinReview():
+        reqPlayerIDDict = tagFamily.GetReqJoinPlayerInfo()
+        if playerID not in reqPlayerIDDict:
+            maxReqPlayerCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 1)
+            if len(reqPlayerIDDict) >= maxReqPlayerCnt:
+                GameWorld.DebugLog('目标公会申请加入数已满! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
+                CrossPlayer.NotifyCode(crossPlayer, "jiazu_pan_141056")
+                return
+            
+        tagFamily.AddReqJoinPlayerID(playerID)
+        # 广播给有招人权限的
+        SendFamilyReqJoinInfo(familyID)
+        
+        #jiazu_pan_500807:申请入帮成功!请等待帮会管理人员审批! 
+        CrossPlayer.NotifyCode(crossPlayer, "jiazu_pan_500807")
+        Sync_RequestAddFamilyInfo(crossPlayer)
+        return
+    
+    # 不需要审核,自动加入
+    memberMax = GetFamilySetting(tagFamily.GetLV(), "MemberMax")
+    if tagFamily.GetCount() >= memberMax:
+        GameWorld.DebugLog('目标公会成员已满! familyLV=%s,memberMax=%s' % (tagFamily.GetLV(), memberMax), playerID)
+        return
+    
+    DoPlayerJionFamily(tagFamily, playerID, crossPlayer)
     return
 
-def __GetTotalFamilyActivity(curPlayer):
-    ##获取总仙盟活跃度
-    totalPoint = 0
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    for i in xrange(ipyDataMgr.GetFamilyActivityCount()):
-        ipyData = ipyDataMgr.GetFamilyActivityByIndex(i)
-        actionid = ipyData.GetID()
-        curTimes = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
-        totalPoint += curTimes/ipyData.GetSingleTimes()*ipyData.GetSingleActiveValue()
-    return totalPoint
-
-def SyncFamilyActivityInfo(curPlayer, syncActionid=-1):
-    return
-    #通知活跃领取情况
-    syncActionIDList = ShareDefine.FamilyActiveIDList if syncActionid==-1 else [syncActionid]
-    sendPack = ChPyNetSendPack.tagMCFamilyActivityInfo()
-    sendPack.Clear()
-    sendPack.AwardRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
-    sendPack.InfoList = []
-    for actionid in syncActionIDList:
-        activityInfo = ChPyNetSendPack.tagMCFamilyActionCnt()
-        activityInfo.ActionID = actionid
-        activityInfo.FinishCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
-        sendPack.InfoList.append(activityInfo)
-    sendPack.Count = len(sendPack.InfoList)
-    NetPackCommon.SendFakePack(curPlayer, sendPack)
+def CancelJoinTagFamily(crossPlayer, familyID):
+    # 撤销申请
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    playerID = crossPlayer.GetPlayerID()
+    tagFamily = familyMgr.FindFamily(familyID)
+    if tagFamily:
+        tagFamily.DelReqJoinPlayerID(playerID)
+    familyMgr.DelPlayerReqJoinFamilyID(playerID, familyID)
+    SendFamilyReqJoinInfo(familyID)
+    Sync_RequestAddFamilyInfo(crossPlayer)
     return
 
-#=================仙盟改名======================
+def Sync_RequestAddFamilyInfo(crossPlayer, isForce=True):
+    ## 通知当前申请加入的哪些家族
+    playerID = crossPlayer.GetPlayerID()
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
+    # 非强制通知时没有申请记录的可不通知,如登录
+    if not isForce and not reqFamilyIDList:
+        return
+    clientPack = ChPyNetSendPack.tagMCNotifyRequestJoinFamilyInfo()
+    clientPack.Clear()
+    clientPack.RequestJoinFamilyIDList = reqFamilyIDList
+    clientPack.RequestCount = len(clientPack.RequestJoinFamilyIDList)
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
+    return
+
+def IsFamilyNeedViewPlayer(playerID):
+    ## 公会功能中查看玩家是否需要用到的
+    
+    # 公会成员已存储成员信息,所以成员不用判断,仅判断其他即可
+    # 是否有请求加入某个公会
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    if familyMgr.GetPlayerReqJoinFamilyIDList(playerID):
+        return True
+    
+    return False
+
+def SendFamilyReqJoinInfo(familyID):
+    ## 广播给公会有招人权限的
+    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyReqJoinInfo(familyID), None, GetFamilyMemberHasPow, FamilyPowerID_Call)
+    return
+
+def GetPack_FamilyReqJoinInfo(familyID):
+    ## 获取 // A5 22 家族申请加入的玩家信息 #tagMCFamilyReqJoinInfo
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    curFamily = familyMgr.FindFamily(familyID)
+    if not curFamily:
+        return
+    reqPlayerIDDict = curFamily.GetReqJoinPlayerInfo()
+    #没人申请也要通知
+    #if not reqPlayerIDDict:
+    #    return
+    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
+    clientPack = ChPyNetSendPack.tagMCFamilyReqJoinInfo()
+    clientPack.ReqJoinList = []
+    for playerID, reqTime in reqPlayerIDDict.items():
+        crossPlayer = crossPlayerMgr.FindCrossPlayer(playerID)
+        reqInfo = ChPyNetSendPack.tagMCFamilyReqJoinPlayer()
+        reqInfo.PlayerID = playerID
+        reqInfo.ReqTime = reqTime
+        reqInfo.IsOnLine = 1 if crossPlayer else 0
+        viewCache = PlayerViewCache.FindViewCache(playerID)
+        if viewCache:
+            reqInfo.Name = viewCache.GetPlayerName()
+            reqInfo.NameLen = len(reqInfo.Name)
+            reqInfo.LV = viewCache.GetLV()
+            reqInfo.Job = viewCache.GetJob()
+            reqInfo.RealmLV = viewCache.GetRealmLV()
+            reqInfo.Face = viewCache.GetFace()
+            reqInfo.FacePic = viewCache.GetFacePic()
+            reqInfo.TitleID = viewCache.GetTitleID()
+            reqInfo.FightPower = viewCache.GetFightPower()
+            reqInfo.FightPowerEx = viewCache.GetFightPowerEx()
+            reqInfo.ServerID = viewCache.GetServerID()
+        clientPack.ReqJoinList.append(reqInfo)
+        if len(clientPack.ReqJoinList) >= 100:
+            break
+    clientPack.ReqCnt = len(clientPack.ReqJoinList)
+    return clientPack
+
+#// A6 21 审核请求加入家族 #tagCMJoinFamilyReply
+#
+#struct tagCMJoinFamilyReply
+#{
+#    tagHead    Head;
+#    DWORD    TagPlayerID;    //被审核玩家ID 0则代表全部
+#    BYTE    IsOK;        //是否同意其加入
+#};
+def OnJoinFamilyReply(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnJoinFamilyReply")
+    return
+
+def __OnJoinFamilyReply(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    tagPlayerID = clientData.TagPlayerID
+    isOK = clientData.IsOK
+    GameWorld.DebugLog("__OnJoinFamilyReply: tagPlayerID=%s,isOK=%s" % (tagPlayerID, isOK))
+    
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
+        GameWorld.DebugLog("没有招人权限,无法审核人员入盟!", playerID)
+        #PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Popedom")
+        return
+    
+    GameWorld.DebugLog("审核入盟申请: tagPlayerID=%s,familyID=%s,isOK=%s" % (tagPlayerID, familyID, isOK), playerID)
+    reqPlayerIDDict = family.GetReqJoinPlayerInfo()
+    tagPlayerIDList = reqPlayerIDDict.keys()
+    if tagPlayerID:
+        if tagPlayerID not in reqPlayerIDDict:
+            GameWorld.DebugLog("不存在该申请人员! tagPlayerID=%s" % tagPlayerID)
+            return
+        tagPlayerIDList = [tagPlayerID]
+        
+    if not tagPlayerIDList:
+        GameWorld.DebugLog("没有申请人员!")
+        return
+    
+    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
+    # 拒绝
+    if not isOK:
+        for tagPlayerID in tagPlayerIDList:
+            family.DelReqJoinPlayerID(tagPlayerID)
+            tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
+            if not tagCrossPlayer:
+                continue
+            Sync_RequestAddFamilyInfo(tagCrossPlayer)
+            #jiazu_pan_592934:{%S}拒绝了您的入帮申请
+            CrossPlayer.NotifyCode(tagCrossPlayer, "jiazu_pan_592934", [family.GetName()])
+            
+        SendFamilyReqJoinInfo(familyID)
+        return
+    
+    # 处理同意
+    offlinePlayerCanJoin = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 3)
+    MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
+    joinOKPlayerIDList = []
+    for tagPlayerID in tagPlayerIDList:
+        if family.GetCount() >= MemberMax:
+            CrossPlayer.NotifyCode(crossPlayer, "jiazu_lhs_202580")
+            break
+        tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
+        #申请目标不在线
+        if not tagCrossPlayer:
+            if not offlinePlayerCanJoin:
+                GameWorld.DebugLog("离线玩家无法加入公会! tagPlayerID=%s" % tagPlayerID, playerID)
+                CrossPlayer.NotifyCode(crossPlayer, "jiazu_hwj35_367906")
+                continue
+            
+        family.DelReqJoinPlayerID(tagPlayerID) # 以下只要操作的都删除
+        
+        if family.FindMember(tagPlayerID):
+            GameWorld.DebugLog("已经是本盟成员! tagPlayerID=%s" % tagPlayerID, playerID)
+            CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_InviteErr_Repeat")
+            continue
+        
+        tagFamilyID = familyMgr.GetPlayerFamilyID(tagPlayerID)
+        if tagFamilyID:
+            GameWorld.DebugLog("已经加入其他公会! tagPlayerID=%s,tagFamilyID=%s" % (tagPlayerID, tagFamilyID), playerID)
+            CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_InviteErr_Repeat")
+            continue
+        
+        DoPlayerJionFamily(family, tagPlayerID, tagCrossPlayer, broadcastFamilyChange=False)
+        joinOKPlayerIDList.append(tagPlayerID)
+        
+    #if not joinOKPlayerIDList:
+    #    return
+    SendFamilyReqJoinInfo(familyID)
+    if joinOKPlayerIDList:
+        Broadcast_FamilyInfo(familyID, changeMemIDList=joinOKPlayerIDList, excludeIDList=joinOKPlayerIDList) # 审核
+    return
+
+#// A6 22 修改收人方式 #tagCMChangeFamilyJoin
+#
+#struct    tagCMChangeFamilyJoin
+#{
+#    tagHead         Head;
+#    BYTE        JoinReview;    //成员加入是否需要审核,默认0自动加入
+#    WORD        JoinLVMin;    //限制最低可加入的玩家等级
+#};
+def OnChangeFamilyJoin(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyJoin")
+    return
+
+def __OnChangeFamilyJoin(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    joinReview = clientData.JoinReview
+    joinLVMin = clientData.JoinLVMin # 官职
+    GameWorld.DebugLog("__OnChangeFamilyJoin: joinReview=%s,joinLVMin=%s" % (joinReview, joinLVMin))
+    
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
+        GameWorld.DebugLog("没有招人权限", playerID)
+        return
+    GameWorld.DebugLog("修改招人设置: familyID=%s,joinReview=%s,joinLVMin=%s" % (familyID, joinReview, joinLVMin), playerID)
+    family.SetJoinReview(joinReview)
+    family.SetJoinLVMin(joinLVMin)
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改招人
+    return
+
+#// A6 23 修改家族公告 #tagCMChangeFamilyBroadcast
+#
+#struct    tagCMChangeFamilyBroadcast
+#{
+#    tagHead        Head;
+#    char        Msg[200];
+#};
+def OnChangeFamilyBroadcast(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyBroadcast")
+    return
+
+def __OnChangeFamilyBroadcast(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    broadcast = clientData.Msg
+    
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Broadcast):
+        GameWorld.DebugLog("没有修改公告权限", playerID)
+        return
+    family.SetBroadcast(broadcast)
+    GameWorld.DebugLog('更改公会公告: Family=%s,公告=%s' % (GameWorld.CodeToGbk(family.GetName()), GameWorld.CodeToGbk(broadcast)), playerID)
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改公告
+    return
+
+#// A6 24 修改家族徽章 #tagCMChangeFamilyEmblem
+#
+#struct    tagCMChangeFamilyEmblem
+#{
+#    tagHead        Head;
+#    BYTE        EmblemID;    // 更换的徽章ID
+#    char        EmblemWord[3];    // 徽章文字
+#};
+def OnChangeFamilyEmblem(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyEmblem")
+    return
+
+def __OnChangeFamilyEmblem(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    changeEmblemID = clientData.EmblemID
+    emblemWord = clientData.EmblemWord
+    PlayerFamilyEmblem.OnChangeFamilyEmblem(crossPlayer, changeEmblemID, emblemWord)
+    return
+
+#// A6 25 修改家族成员职位 #tagCMChangeFamilyMemLV
+#
+#struct    tagCMChangeFamilyMemLV
+#{
+#    tagHead        Head;
+#    DWORD        PlayerID; // 目标成员ID
+#    BYTE        FmLV;  // 变更为xx职位
+#};
+def OnChangeFamilyMemLV(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyMemLV")
+    return
+
+def __OnChangeFamilyMemLV(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    OnChangeFamilyMemberLV(crossPlayer, clientData.PlayerID, clientData.FmLV)
+    return
+
+def OnChangeFamilyMemberLV(crossPlayer, tagID, changeFmlv, isGMOP=False):
+    '''变更成员职位
+    @param curPlayer: 操作的玩家
+    @param tagID: 目标成员ID
+    @param changeFmlv: 修改为xx职位
+    @param isGMOP: 是否是GM后台发起的,如果是GM发起的,一般curPlayer传入的为目标成员ID实例
+    '''
+    if not crossPlayer:
+        return
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    if changeFmlv < 0 or changeFmlv > IPY_PlayerDefine.fmlLeader:
+        GameWorld.DebugLog("不存在该职位等级! changeFmlv=%s" % changeFmlv)
+        return
+    # 非GM操作的需检查权限
+    if not isGMOP:
+        if not GetFamilyMemberHasPow(curMember, FamilyPowerID_ChangeFmlv):
+            return
+        if playerID == tagID:
+            GameWorld.DebugLog("不能任免自己的家族职位", playerID)
+            return
+        
+    tagMember = family.FindMember(tagID)
+    if tagMember == None:
+        GameWorld.DebugLog("更改家族成员职位时目标成员不存在! tagID=%s" % tagID, playerID)
+        return 
+    
+    if not isGMOP:
+        if curMember.GetFmLV() != IPY_PlayerDefine.fmlLeader:
+            if changeFmlv >= curMember.GetFmLV():
+                GameWorld.DebugLog("修改的职位不能比自己高或平级! changeFmlv=%s" % changeFmlv, playerID)
+                return
+            if tagMember.GetFmLV() >= curMember.GetFmLV():
+                GameWorld.DebugLog("修改的目标成员职位不能比自己高或平级! tagFmlv=%s" % tagMember.GetFmLV(), playerID)
+                return
+            
+    changeMemIDList = [tagID]
+    if changeFmlv == IPY_PlayerDefine.fmlLeader:
+        ChangeFamilyLeader(family, tagMember)
+        changeMemIDList.append(playerID)
+        
+    else:
+        fmLVMemCnt = 0
+        for index in range(family.GetCount()):
+            familyMember = family.GetAt(index)
+            if familyMember.GetFmLV() != changeFmlv:
+                continue
+            fmLVMemCnt += 1
+            
+        maxCnt = GetFamilySetting(family.GetLV(), Def_FmlSetAttrName.get(changeFmlv, ""))
+        if fmLVMemCnt >= maxCnt:
+            # jiazu_hwj35_272921 改为 jiazu_chenxin_31379
+            CrossPlayer.NotifyCode(crossPlayer, "jiazu_chenxin_31379")
+            GameWorld.DebugLog("目前该职位的人数已经达到上限! changeFmlv=%s,fmLVMemCnt=%s >= %s" % (changeFmlv, fmLVMemCnt, maxCnt))
+            return
+        ChangeFamilyMemberLv(tagMember, changeFmlv)
+        
+    if isGMOP:
+        family.SetBroadcast("")
+    Broadcast_FamilyInfo(familyID, changeMemIDList=changeMemIDList) # 修改职位
+    return True
+
+def ChangeFamilyLeader(family, newLeaderMem):
+    ## 变更家族族长
+    
+    familyID = family.GetID()
+    befLeaderID = family.GetLeaderID()
+    newLeaderID = newLeaderMem.GetPlayerID()
+    if befLeaderID == newLeaderID:
+        return
+    
+    befLeaderMem = family.FindMember(befLeaderID)
+    if befLeaderMem:
+        #把原族长降为普通成员
+        ChangeFamilyMemberLv(befLeaderMem, IPY_PlayerDefine.fmlMember)
+        
+    family.SetLeaderID(newLeaderID)
+    ChangeFamilyMemberLv(newLeaderMem, IPY_PlayerDefine.fmlLeader)
+    GameWorld.Log("家族设置新族长! familyID=%s,newLeaderID=%s,befLeaderID=%s" % (familyID, newLeaderID, befLeaderID))
+    return
+
+def ChangeFamilyMemberLv(tagMember, changeFamilyLV):
+    ## 修改成员职位,只做修改逻辑,不做验证,验证由各调用入口自行验证
+    familyID = tagMember.GetFamilyID()
+    tagID = tagMember.GetPlayerID()
+    memName = tagMember.GetPlayerName()
+    befFamilyLV = tagMember.GetFmLV()
+    
+    tagMember.SetFmLV(changeFamilyLV)
+    
+    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagID)
+    if tagCrossPlayer:
+        MapServer_FamilyRefresh(tagCrossPlayer, familyID) # 修改职位
+        if GetFamilyMemberHasPow(tagMember, FamilyPowerID_Call):
+            CrossPlayer.SendFakePack(tagCrossPlayer, GetPack_FamilyReqJoinInfo(familyID))
+        
+    # 记录家族事件记录信息
+    tick = GameWorld.GetGameWorld().GetTick()
+    AddFamilyActionNote(memName, familyID, ShareDefine.Def_ActionType_FamilyEvent, 
+                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_FMLV, changeFamilyLV, befFamilyLV], tick)
+    
+    #xx被任命为xx
+    CrossPlayer.FamilyNotify(familyID, "XW_JZ_AppointFamily", [memName, changeFamilyLV])
+    return
+
+def __AutoChangeLeader(curFamily):
+    ## 自动传位
+    leaderID = curFamily.GetLeaderID()
+    leaderMem = curFamily.FindMember(leaderID)
+    if not leaderMem:
+        return
+    offTime = leaderMem.GetOffTime()
+    if not offTime:
+        return
+    familyID = curFamily.GetID()
+    curTime = int(time.time())
+    passTime = curTime - offTime
+    passHours = passTime / 3600.0
+    needHours = IpyGameDataPY.GetFuncCfg("FamilyLeaderAutoChange", 1)
+    if passHours < needHours:
+        GameWorld.DebugLogEx("盟主离线未超过限制小时,不处理自动传位!familyID=%s,leaderID=%s,offTime=%s,passHours=%s < %s", 
+                             familyID, leaderID, GameWorld.ChangeTimeNumToStr(offTime), passHours, needHours)
+        return
+    
+    priorityHours = IpyGameDataPY.GetFuncCfg("FamilyLeaderAutoChange", 1) # 优先传给离线不超过x小时的成员,一样按优先级
+    
+    priorityList = []
+    commList = []
+    for i in range(0, curFamily.GetCount()):
+        member = curFamily.GetAt(i)
+        if member.GetFmLV() == IPY_PlayerDefine.fmlLeader:
+            continue
+        
+        memOffTime = member.GetOffTime()
+        if memOffTime:
+            sortTime = memOffTime
+            memPassTime = curTime - memOffTime
+            memPassHours = memPassTime / 3600.0
+        else:
+            sortTime = curTime # 排序用的时间,越大越优先
+            memPassTime = 0
+            memPassHours = 0
+            
+        fmLV = member.GetFmLV() # 职位
+        contribTotal = member.GetContribTotal() # 总贡献
+        
+        commList.append([fmLV, sortTime, contribTotal, member])
+        if priorityHours and memPassHours <= priorityHours:
+            priorityList.append([fmLV, sortTime, contribTotal, member])
+            
+    if not priorityList and not commList:
+        # 没有可传位的目标成员
+        return
+    
+    toMember = None
+    if priorityList:
+        priorityList.sort(reverse=True)
+        toMember = priorityList[0][-1]
+    else:
+        commList.sort(reverse=True)
+        toMember = commList[0][-1]
+        
+    if not toMember:
+        return
+    
+    newLeaderID = toMember.GetPlayerID()
+    GameWorld.Log("公会自动传位: familyID=%s,leaderID=%s,offTime=%s,passHours=%s,newLeaderID=%s" 
+                  % (familyID, leaderID, GameWorld.ChangeTimeNumToStr(offTime), passHours, newLeaderID))
+    ChangeFamilyLeader(curFamily, toMember)
+    Broadcast_FamilyInfo(familyID, changeMemIDList=[leaderID, newLeaderID]) # 自动传位
+    
+    # 邮件通知
+    toServerID = toMember.GetServerID()
+    PlayerMail.SendMailByKey("FamilyLeaderAutoChange", newLeaderID, [], [curFamily.GetName()], toServerID=toServerID)
+    return
+
+#// A6 26 请求家族成员列表 #tagCMGetFamilyInfo
+#
+#struct    tagCMGetFamilyInfo
+#{
+#    tagHead        Head;
+#};
+def OnGetFamilyInfo(index, clientData, tick):
+    #改为后端主动同步差异,不用再请求了
+    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    #Sync_FamilyInfo(crossPlayer)
+    return
+
+#// A6 03 离开家族 #tagCMLeaveFamily
+#
+#struct    tagCMLeaveFamily
+#{
+#    tagHead        Head;
+#};
+def OnLeaveFamily(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnMemLeaveFamily")
+    return
+
+def __OnMemLeaveFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    
+    familyLV = curMember.GetFmLV()  # 职位
+    if family.GetCount() > 1 and familyLV == IPY_PlayerDefine.fmlLeader:
+        GameWorld.DebugLog("族长在成员人数大于1时不能直接退出家族", playerID)
+        return
+    
+    # 功能限制退出公会
+    # ...
+    
+    # 进出时间限制暂不做,等正式功能再补
+    #PlayerControl.SetLeaveFamilyTime(curPlayer, updTime)
+    
+    # 执行退出
+    GameWorld.DebugLog("离开家族! familyID=%s" % familyID, playerID)
+    
+    family.DeleteMember(playerID)
+    AddFamilyActionNote(crossPlayer.GetPlayerName(), familyID, ShareDefine.Def_ActionType_FamilyEvent, 
+                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_Leave], tick)
+    
+    __DoPlayerLeaveFamilyByID(family, playerID, crossPlayer)
+    MapServer_FamilyRefresh(crossPlayer, 0, 1) # 主动退出
+    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyDel(playerID, crossPlayer.GetPlayerName(), 1))
+    CrossPlayer.FamilyNotify(familyID, "XW_JZ_LeaveFamily", [crossPlayer.GetPlayerName()])
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 退出
+    
+    if family.GetCount() == 0:
+        #玩家离开后, 家族没有人了 , 删除这个家族
+        familyMgr.DelFamily(familyID)
+        return
+    
+    return
+
+#// A6 05 删除家族成员 #tagCMDeleteFamilyMember
+#
+#struct    tagCMDeleteFamilyMember
+#{
+#    tagHead        Head;
+#    DWORD        MemberID;
+#};
+def OnDeleteFamilyMember(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnDeleteFamilyMember")
+    return
+
+def __OnDeleteFamilyMember(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    tagMemberID = clientData.MemberID
+    GameWorld.DebugLog("__OnDeleteFamilyMember tagMemberID=%s" % tagMemberID)
+    
+    playerID = crossPlayer.GetPlayerID()
+    if playerID == tagMemberID:
+        return
+    
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
+    
+    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Kick):
+        GameWorld.DebugLog("没有踢人权限!")
+        return
+    
+    tagMember = family.FindMember(tagMemberID)
+    if not tagMember:
+        return
+    
+    curFmlv = curMember.GetFmLV()
+    tagFmlv = tagMember.GetFmLV()
+    if tagFmlv >= curFmlv:
+        GameWorld.DebugLog("只能踢比自己职位低的成员! tagMemberID=%s,tagFmlv(%s) >= curFmlv(%s)" % (tagMemberID, tagFmlv, curFmlv), playerID)
+        return
+    
+    # 功能限制踢人
+    # ...
+    
+    tagPlayerName = tagMember.GetPlayerName()  # 被踢玩家名
+    tagPlayerID = tagMember.GetPlayerID()  # 被踢玩家ID
+    
+    family.DeleteMember(tagPlayerID)
+    
+    AddFamilyActionNote(tagPlayerName, familyID, ShareDefine.Def_ActionType_FamilyEvent, 
+                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_KickOut], tick)
+    
+    #XW_JZ_LeaveFamily   <n color="0,190,255">{%S1%}</n><n color="255,255,0">退出了家族!</n>  25  -   -
+    CrossPlayer.FamilyNotify(familyID, "XW_JZ_LeaveFamily", [tagPlayerName])
+    
+    #删除玩家
+    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagMemberID)
+    __DoPlayerLeaveFamilyByID(family, tagPlayerID, tagCrossPlayer)
+    if tagCrossPlayer:
+        MapServer_FamilyRefresh(tagCrossPlayer, 0) # 被踢
+        CrossPlayer.NotifyCode(tagCrossPlayer, "XW_JZ_LeaveFamilyKick", [curMember.GetPlayerName()])
+        
+    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyDel(tagMemberID, tagPlayerName, 0))
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 踢人
+    return
+
+def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID, crossPlayer=None):
+    ## 有玩家离开家族处理,主要针对家族层级的,玩家个人的在 __OnLeaveFamily 处理
+    PlayerFamilyTaofa.OnFamilyMemberLeave(curFamily, leavePlayerID)
+    return
+
 #// A6 11 家族改名 #tagCMRenameFamily
 #
 #struct tagCMRenameFamily
@@ -805,612 +1546,648 @@
 #    BYTE        ItemIndex;  //改名物品在背包中的位置
 #};
 def UpdateFamilyName(index, clientData, tick):
-    
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    if GameWorld.IsCrossServer():
-        #itemuse_lzxkoy_0:不可在跨服环境下使用改名功能。
-        PlayerControl.NotifyCode(curPlayer, "itemuse_lzxkoy_0")
-        return
-    
-    if not curPlayer.GetFamilyID():
-        return
-    
-    if curPlayer.GetDictByKey(ChConfig.Def_Player_Dict_UpdateFamilyName):
-        #正在改名中
-        GameWorld.Log("仙盟已经在改名中...", curPlayer.GetID())
-        return
-    
-    #新名字
     newName = clientData.NewName
-    itemIndex = clientData.ItemIndex
-     
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    curItem = itemPack.GetAt(itemIndex)
-    if not ItemCommon.CheckItemCanUse(curItem):
+    if not CheckInputFamilyName(curPlayer, newName):
+        GameWorld.DebugLog("名字验证不通过")
         return
-    if curItem.GetType() != ChConfig.Def_ItemType_ChangeFamilyName:
+    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
+    if moneyType and moneyValue and not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
         return
-
-    #改名物品在背包的位置
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex, itemIndex)
-    
-    #设置正在改名中...
-    curPlayer.SetDict(ChConfig.Def_Player_Dict_UpdateFamilyName, 1)
-    
-    #通知GameServer正式改名
-    newNameMsg = str(newName)
-    GameWorld.Log("UpdateFamilyName 通知GameServer正式改名为:%s"%newNameMsg, curPlayer.GetID())
-    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, 'UpdateFamilyName', newNameMsg, len(newNameMsg))
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__UpdateFamilyName", True, 20)
     return
 
-
-## 扣除改名消耗
-#  @param curPlayer: 玩家实例
-#  @return: None
-def PayUpdateFamilyNameCost(curPlayer): 
-    itemIndex = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex)
-    #扣除物品
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    curItem = itemPack.GetAt(itemIndex)
-    if curItem:
-        ItemCommon.DelItem(curPlayer, curItem, 1, True, 'UpdateFamilyName')
+def __UpdateFamilyName(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    newName = clientData.NewName
+    #itemIndex = clientData.ItemIndex
     
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex, 0)
-    return
-
-def GetFamilyDayAward(curPlayer):
-    ##领取仙盟每日奖励 2小时脱机挂时间
-    if not curPlayer.GetFamilyID():
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
         return
-    hasGot = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDayAward)
-    if hasGot:
-        GameWorld.DebugLog('领取仙盟每日奖励 当日已领取!')
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
         return
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDayAward, 1)
-    #addTime = IpyGameDataPY.GetFuncCfg('FamilyDayAward')
-    #PlayerTJG.AddTJGTime(curPlayer, addTime)
-    #通知
-    Sync_FamilyDayRewardState(curPlayer)
-    #PlayerControl.NotifyCode(curPlayer, 'OfflinePlugSuccess', [addTime/60/60])
+    curMember = family.FindMember(playerID)
+    if not curMember:
+        return
     
-    return
+    if curMember.GetFmLV() != IPY_PlayerDefine.fmlLeader:
+        GameWorld.DebugLog("非盟主不可改名!", playerID)
+        return
+    
+    curTime = int(time.time())
+    cdHours = IpyGameDataPY.GetFuncCfg("FamilyRename", 2)
+    if cdHours:
+        cdSeconds = cdHours * 3600
+        lastRenameTime = GetRenameTime(family)
+        if lastRenameTime and (curTime - lastRenameTime) < cdSeconds:
+            GameWorld.DebugLog("公会改名CD中! lastRenameTime=%s,cdHours=%s" % (GameWorld.ChangeTimeNumToStr(lastRenameTime), cdHours))
+            return
+        
+    # 验证重名
+    if CheckFamilyNameExists(crossPlayer, newName, fromServerID):
+        return
+    
+    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
+    if moneyType and moneyValue:
+        CrossPlayer.CostPlayerResources(crossPlayer, "FamilyRename", costMoneyDict={moneyType:moneyValue})
+        
+    family.SetName(newName)        
+    if cdHours:
+        SetRenameTime(family, curTime)
+        
+    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
+    for index in xrange(family.GetCount()):
+        member = family.GetAt(index)
+        memID = member.GetPlayerID()
+        memCrossPlayer = crossPlayerMgr.FindCrossPlayer(memID)
+        if not memCrossPlayer:
+            continue
+        MapServer_FamilyRefresh(memCrossPlayer, familyID) # 改名
+        #player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
+        
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 改名
+    return True
 
-def Sync_FamilyDayRewardState(curPlayer):
-    clientPack = ChPyNetSendPack.tagMCFamilyDayAward()
-    clientPack.GetState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDayAward)
-    clientPack.MoneyDonateCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyMoneyDonateCount)
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
-    return
-
-
-#// A6 06 家族兑换活跃令 #tagCMFamilyActivityExchange
+#// A6 19 查看目标公会 #tagCSViewTagFamily
 #
-#struct    tagCMFamilyActivityExchange
+#struct tagCSViewTagFamily
 #{
 #    tagHead        Head;
-#    BYTE        Count;        //材料所在背包索引的数量
-#    WORD        IndexList[Count];    //材料所在背包索引列表
-#    DWORD        ItemIDList[Count];    //材料所在背包物品ID列表
+#    DWORD        FamilyID;
+#    DWORD        DataServerID;    //数据所在服务器ID
 #};
-## 家族兑换活跃令 
-#  @param playerIndex 玩家索引  
-#  @param clientData 客户端封包  
-#  @param tick 时间
-#  @return None
-def OnFamilyActivityExchange(index, clientData, tick):
+def OnViewTagFamily(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    if not clientData.Count:
-        return
-    playerID = curPlayer.GetID()
-    eatIndexList = clientData.IndexList
-    eatItemIDList = clientData.ItemIDList
-    givePoint = 0 #分解得到活跃令
-    familyDonateDict = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 1, {})
-    familyDonateSpecialDict = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 2, {})
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    for i, index in enumerate(eatIndexList):
-        eatItem = itemPack.GetAt(index)
-        if not ItemCommon.CheckItemCanUse(eatItem):
-            GameWorld.DebugLog("物品不可用,无法兑换!itemIndex=%s" % index)
-            continue
-        eatItemID = eatItem.GetItemTypeID()
-        if eatItemID != eatItemIDList[i]:
-            GameWorld.Log('家族兑换活跃令 发的物品ID不对应index=%s eatItemID=%s,ItemIDList[i]=%s' % (index, eatItemID, eatItemIDList[i]), playerID)
-            continue
-        #if eatItem.GetIsBind():
-        #    GameWorld.DebugLog("装备已绑定,无法兑换!itemIndex=%s" % index)
-        #    continue
-        if eatItem.GetEndureReduceType():
-            GameWorld.DebugLog("有时效耐久物品,无法兑换!itemIndex=%s" % index)
-            continue
-        if eatItemID in familyDonateSpecialDict:
-            addPoint = familyDonateSpecialDict[eatItemID]
-        else:
-            if not ItemCommon.CheckItemIsEquip(eatItem):
-                GameWorld.DebugLog("非装备,无法兑换!itemIndex=%s" % index)
-                continue
-            itemColor = eatItem.GetItemColor()
-            if str(itemColor) not in familyDonateDict:
-                continue
-            isSuite = eatItem.GetSuiteID()
-            addPoint = familyDonateDict[str(itemColor)][1 if isSuite else 0]
-        itemCnt = eatItem.GetCount()
-        givePoint += addPoint * itemCnt
-        ItemCommon.DelItem(curPlayer, eatItem, itemCnt, True, ChConfig.ItemDel_FamilyStore, {'addPoint':addPoint}, True)
+    playerID = curPlayer.GetPlayerID()
+    tagFamilyID = clientData.FamilyID
+    dataServerID = clientData.DataServerID
     
-    if not givePoint:
-        GameWorld.DebugLog("家族兑换活跃令,没有装备可兑换!")
+    # 本服或主服是本服
+    if not dataServerID or dataServerID == GameWorld.GetGameWorld().GetServerID():
+        NetPackCommon.SendFakePack(curPlayer, GetTagFamilyInfoPack(tagFamilyID))
         return
-    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, givePoint)
     
-    #通知结果
-    packData = ChPyNetSendPack.tagMCFamilyActivityExchangeResult()
-    packData.Clear()
-    packData.Point = givePoint
-    NetPackCommon.SendFakePack(curPlayer, packData)
+    CrossMsg.SendToServer(ShareDefine.S2S_ViewTagFamily, {"tagFamilyID":tagFamilyID}, [dataServerID], ShareDefine.dirType_All, playerID)
     return
+
+def S2S_ViewTagFamily(dataMsg, fromServerID, playerID):
+    tagFamilyID = dataMsg["tagFamilyID"]
+    CrossPlayer.SendFakePackByID(playerID, GetTagFamilyInfoPack(tagFamilyID), fromServerID)
+    return
+
+def GetTagFamilyInfoPack(tagFamilyID):
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(tagFamilyID)
+    if not family:
+        GameWorld.ErrLog("本服数据找不到目标公会! tagFamilyID=%s" % tagFamilyID)
+        return
+    clientPack = ChPyNetSendPack.tagSCTagFamilyInfo()
+    clientPack.FamilyID = family.GetID()
+    clientPack.FamilyName = family.GetName()
+    clientPack.FamilyNameLen = len(clientPack.FamilyName)
+    clientPack.LeaderID = family.GetLeaderID()
+    leaderMember = family.FindMember(clientPack.LeaderID)
+    if leaderMember:
+        clientPack.LeaderName = leaderMember.GetPlayerName()
+        clientPack.LeaderNameLen = len(clientPack.LeaderName)
+        clientPack.LeaderServerID = leaderMember.GetServerID()
+    clientPack.FamilyLV = family.GetLV()
+    clientPack.ServerID = family.GetServerID()
+    clientPack.EmblemID = family.GetEmblemID()
+    clientPack.EmblemWord = family.GetEmblemWord()
+    clientPack.FightPower = family.GetFightPower()
+    clientPack.FightPowerEx = family.GetFightPowerEx()
+    clientPack.Broadcast = family.GetBroadcast()
+    clientPack.BroadcastLen = len(clientPack.Broadcast)
+    clientPack.MemberCount = family.GetCount()
+    clientPack.DataServerID = GameWorld.GetGameWorld().GetServerID()
+    return clientPack
+
+#// A6 20 搜索家族列表 #tagCMViewFamilyPage
+#
+#struct    tagCMViewFamilyPage
+#{
+#    tagHead        Head;
+#    BYTE        MsgLen;        //模糊搜索家族,如果输入为空,则为不限制该条件
+#    char        Msg[MsgLen];    //size = MsgLen
+#    BYTE        PageIndex;    //查询第X页索引,0~n
+#    BYTE        ShowCount;    //每页数量,前端可自行指定,最大50
+#};
+def OnViewFamilyPage(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnViewFamilyPage")
+    return
+
+def __OnViewFamilyPage(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    msg = clientData.Msg
+    pageIndex = clientData.PageIndex
+    showCount = min(clientData.ShowCount, 50)
+    
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    zoneID = familyMgr.GetZoneIDInThisServer(fromServerID)
+    if zoneID < 0:
+        GameWorld.ErrLog("找不到服务器ID在本服中的公会分区! fromServerID=%s" % fromServerID)
+        return
+    
+    playerFamilyID = crossPlayer.GetFamilyID()
+    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
+    zoneMgr.Sort(True)
+    familyCount = zoneMgr.GetCount()
+    
+    totalPage = 0
+    
+    if not msg:
+        startIndex = pageIndex * showCount
+        endIndex = startIndex + showCount - 1
+        if familyCount > 0:
+            totalPage = GameWorld.GetIntUpper(familyCount, showCount)
+            
+    # 有指定搜索内容的后端固定返回单页   
+    else:
+        pageIndex = 0
+        showCount = 20
+        totalPage = 1
+        startIndex = 0
+        endIndex = familyCount - 1
+        
+    clientPack = ChPyNetSendPack.tagMCFamilyViewList()
+    clientPack.Msg = msg
+    clientPack.MsgLen = len(clientPack.Msg)
+    clientPack.PageIndex = pageIndex
+    clientPack.ShowCount = showCount
+    clientPack.TotalPage = totalPage
+    clientPack.Rank = zoneMgr.GetFamilyRank(playerFamilyID)
+    clientPack.FamilyList = []
+    for index in range(startIndex, endIndex + 1):
+        if index >= familyCount:
+            break
+        family = zoneMgr.GetAt(index)
+        if not family:
+            continue
+        if msg:
+            if msg in family.GetName() or msg == str(family.GetID()):
+                pass
+            else:
+                continue
+        familyView = ChPyNetSendPack.tagMCFamilyView()
+        familyView.Rank = index + 1
+        familyView.FamilyID = family.GetID()
+        familyView.FamilyName = family.GetName()
+        familyView.FamilyNameLen = len(familyView.FamilyName)
+        familyView.LeaderID = family.GetLeaderID()
+        leaderMember = family.FindMember(familyView.LeaderID)
+        familyView.LeaderName = leaderMember.GetPlayerName() if leaderMember else ""
+        familyView.LeaderNameLen = len(familyView.LeaderName)
+        familyView.LeaderServerID = leaderMember.GetServerID() if leaderMember else family.GetServerID()
+        familyView.FamilyLV = family.GetLV()
+        familyView.JoinReview = family.GetJoinReview()
+        familyView.JoinLVMin = family.GetJoinLVMin()
+        familyView.ServerID = family.GetServerID()
+        familyView.EmblemID = family.GetEmblemID()
+        familyView.EmblemWord = family.GetEmblemWord()
+        familyView.FightPower = family.GetFightPower()
+        familyView.FightPowerEx = family.GetFightPowerEx()
+        familyView.MemberCount = family.GetCount()
+        clientPack.FamilyList.append(familyView)
+        clientPack.FamilyCount = len(clientPack.FamilyList)
+        if clientPack.FamilyCount >= showCount:
+            break
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
+    return
+
 
 #// A6 12 家族捐献货币 #tagCMFamilyMoneyDonate
 #
 #struct     tagCMFamilyMoneyDonate
 #{
 #    tagHead        Head;
-#    BYTE        MoneyType;    // 捐献货币类型
+#    BYTE        DonateType;    // 捐献类型
 #};
 def OnFamilyMoneyDonate(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    moneyType = clientData.MoneyType
     playerID = curPlayer.GetPlayerID()
+    donateType = clientData.DonateType
     
-    dailyDonateCountMax = IpyGameDataPY.GetFuncCfg('FamilyDonate', 3)
-    donateCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyMoneyDonateCount)
-    if dailyDonateCountMax and donateCount >= dailyDonateCountMax:
-        GameWorld.DebugLog("今日货币捐献次数已达上限. donateCount=%s" % donateCount, playerID)
+    ipyData = IpyGameDataPY.GetIpyGameData("FamilyDonate", donateType)
+    if not ipyData:
+        return
+    dailyCntMax = ipyData.GetDailyCnt()
+    donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
+    if donateCnt >= dailyCntMax:
+        GameWorld.DebugLog("今日捐献次数已达上限! donateType=%s,donateCnt=%s >= %s" % (donateType, donateCnt, dailyCntMax), playerID)
         return
     
-    donatePrizeInfo = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 4, {})
-    if str(moneyType) not in donatePrizeInfo:
-        GameWorld.DebugLog("不存在该货币类型捐献: moneyType=%s" % moneyType, playerID)
+    moneyType = ipyData.GetMoneyType()
+    moneyValue = ipyData.GetMoneyValue()
+    if not moneyType or not moneyValue:
         return
-    needMoney, contribution, familyActivity = donatePrizeInfo[str(moneyType)]
-    if not PlayerControl.PayMoney(curPlayer, moneyType, needMoney, "FamilyMoneyDonate"):
+    if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
         return
     
-    updDonateCount = donateCount + 1
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyMoneyDonateCount, updDonateCount)
-    Sync_FamilyDayRewardState(curPlayer)
-    GameWorld.DebugLog("货币捐献: moneyType=%s,needMoney=%s,updDonateCount=%s,contribution=%s,familyActivity=%s" 
-                       % (moneyType, needMoney, updDonateCount, contribution, familyActivity), playerID)
-    
-    if contribution > 0:
-        AddPlayerFamilyActiveValue(curPlayer, contribution, True, ShareDefine.Def_AddFAVReason_FamilyDonateItem, True)
-        
-    if familyActivity > 0:
-        PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, familyActivity)
-        
+    reqDataEx = {"donateCnt":donateCnt}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyMoneyDonate", True, 20, reqDataEx=reqDataEx)
     return
 
-
-##--------------------------------------- 仙盟事务 --------------------------------------------------
-AffairState_None = 0 # 无
-AffairState_Underway = 1 # 进行中
-AffairState_Finish = 2 # 已完成
-
-#// A6 13 家族事务操作 #tagCMFamilyAffairOP
-#
-#struct     tagCMFamilyAffairOP
-#{
-#    tagHead        Head;
-#    BYTE        OPType;    // 操作类型:1-刷新事务;2-开始事务;3-领取事务奖励;
-#    WORD        AffairID;    // 事务ID,可选
-#};
-def OnFamilyAffairOP(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    opType = clientData.OPType
-    affairID = clientData.AffairID
-    
-    if opType == 1:
-        __FamilyAffair_Refresh(curPlayer)
-    elif opType == 2:
-        __FamilyAffair_Start(curPlayer, affairID)
-    elif opType == 3:
-        __FamilyAffair_GetAward(curPlayer, affairID)        
-    return
-
-def __FamilyAffair_CheckReset(curPlayer):
-    ## 检查任务重置,登录,进入仙盟触发
-    info = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairInfo % 1)
-    if not info:
-        # 任务1还没分配,默认强制重置
-        __FamilyAffair_Refresh(curPlayer, True)
-    else:
-        SyncFamilyAffairInfo(curPlayer)
-    return
-
-def __FamilyAffair_Refresh(curPlayer, isReset=False):
-    ## 刷新事务
-    
-    playerID = curPlayer.GetPlayerID()
-    moneyType, moneyValue = 0, 0    
-    dayRefreshFreeCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairRefreshFree)
-    if not isReset:
-        freeCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 2)
-        if freeCountMax and dayRefreshFreeCount >= freeCountMax:
-            moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 3)
-            if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
-                return
-            
-    sendMailAffairList = []
-    refreshAffairIDList = []
-    affairCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 1)
-    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
-    maxStar = 0
-    starWeightList = []
-    for starStr, starInfo in affairStarDict.items():
-        star = int(starStr)
-        if star > maxStar:
-            maxStar = star
-        starWeightList.append([starInfo[0], star])
-        
-    for affairID in range(1, affairCountMax + 1):
-        star, state = __GetAffairInfo(curPlayer, affairID)
-        if isReset:
-            # 重置时还在进行中的直接发奖励
-            if state == AffairState_Underway:
-                sendMailAffairList.append([affairID, star])
-            refreshAffairIDList.append(affairID)
-        else:
-            # 非重置只处理没有状态非最高星的
-            if state == AffairState_None and star < maxStar:
-                refreshAffairIDList.append(affairID)
-                
-    GameWorld.DebugLog("刷新事务: isReset=%s,moneyType=%s,moneyValue=%s,dayRefreshFreeCount=%s" 
-                       % (isReset, moneyType, moneyValue, dayRefreshFreeCount), playerID)
-    GameWorld.DebugLog("    sendMailAffairList=%s" % sendMailAffairList, playerID)
-    
-    for mailInfo in sendMailAffairList:
-        affairID, star = mailInfo
-        if str(star) not in affairStarDict:
-            continue
-        paramList = [affairID, star]
-        addItemList = affairStarDict[str(star)][2]
-        PlayerControl.SendMailByKey("FamilyAffairAward", [playerID], addItemList, paramList)
-        
-    if isReset:
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairRefreshFree, 0)
-        for affairID in range(1, affairCountMax + 1):
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID, 0)
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID, 0)
-    else:
-        if moneyType and moneyValue:
-            PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyAffair")
-        else:
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairRefreshFree, dayRefreshFreeCount + 1)
-            
-    GameWorld.DebugLog("    starWeightList=%s" % starWeightList, playerID)
-    GameWorld.DebugLog("    refreshAffairIDList=%s" % refreshAffairIDList, playerID)
-    for affairID in refreshAffairIDList:
-        star = GameWorld.GetResultByWeightList(starWeightList, 1)
-        __SetAffairInfo(curPlayer, affairID, star, AffairState_None)
-        GameWorld.DebugLog("    随机事务:affairID=%s,star=%s" % (affairID, star), playerID)
-        
-    SyncFamilyAffairInfo(curPlayer)
-    return
-
-def __FamilyAffair_Start(curPlayer, affairID):
-    ## 开始事务
-    playerID = curPlayer.GetPlayerID()
-    star, state = __GetAffairInfo(curPlayer, affairID)
-    if not star:
+def __OnFamilyMoneyDonate(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    donateType = clientData.DonateType
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
         return
-    if state != AffairState_None:
-        GameWorld.DebugLog("仙盟事务已经进行中或已完成,无法开始: affairID=%s,star=%s,state=%s" % (affairID, star, state), playerID)
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    curFamily = familyMgr.FindFamily(familyID)
+    if not curFamily:
         return
-    startTime = int(time.time())
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID, startTime)
-    __SetAffairInfo(curPlayer, affairID, star, AffairState_Underway)
-    GameWorld.DebugLog("仙盟事务开始: affairID=%s,star=%s,startTime=%s" % (affairID, star, startTime), playerID)
-    SyncFamilyAffairInfo(curPlayer, affairID)
-    return
-
-def __FamilyAffair_GetAward(curPlayer, affairID):
-    ## 领取事务奖励
-    playerID = curPlayer.GetPlayerID()
-    curTime = int(time.time())
-    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
-    star, state = __GetAffairInfo(curPlayer, affairID)
-    if state != AffairState_Underway:
-        GameWorld.DebugLog("仙盟事务状态非进行中无法领取: affairID=%s,star=%s,state=%s" % (affairID, star, state), playerID)
-        SyncFamilyAffairInfo(curPlayer, affairID)
-        return
-    startTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID)
-    remainDuration = __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict)
-    if remainDuration != 0:
-        GameWorld.DebugLog("仙盟事务当前剩余时长未完成: affairID=%s,remainDuration=%s,startTime=%s" 
-                           % (affairID, remainDuration, startTime), playerID)
-        SyncFamilyAffairInfo(curPlayer, affairID)
-        return
-    if str(star) not in affairStarDict:
-        return
-    addItemList = affairStarDict[str(star)][2]
-    if not ItemCommon.GiveAwardItem(curPlayer, addItemList):
-        return
-    __SetAffairInfo(curPlayer, affairID, star, AffairState_Finish)
-    GameWorld.DebugLog("仙盟事务领奖: affairID=%s,star=%s" % (affairID, star), playerID)
-    SyncFamilyAffairInfo(curPlayer, affairID)
-    return
-
-def __GetAffairInfo(curPlayer, affairID):
-    affairInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID)
-    star, state = affairInfo / 10, affairInfo % 10
-    return star, state
-def __SetAffairInfo(curPlayer, affairID, star, state):
-    info = star * 10 + state
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID, info)
-    return
-def __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict):
-    ## -1-未开始;>=0-剩余时长
-    startTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID)
-    if not startTime:
-        return -1
-    starInfo = affairStarDict.get(str(star), [])
-    needDuration = starInfo[1] if len(starInfo) > 1 else 0
-    # 可扩展减时长属性
-    speedPer = PlayerControl.GetAffairSpeedPer(curPlayer)
-    if speedPer:
-        needDuration = int(needDuration * max(10000 - speedPer, 0) / 10000.0)
-        #GameWorld.DebugLog("事务加速: needDuration=%s,speedPer=%s" % (needDuration, speedPer), curPlayer.GetPlayerID())
-        
-    remainDuration = max(needDuration - (curTime - startTime), 0)
-    return remainDuration
-
-def SyncFamilyAffairInfo(curPlayer, affairID=None):
-    if affairID == None:
-        affairIDList = []
-        affairCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 1)
-        for affairID in range(1, affairCountMax + 1):
-            affairIDList.append(affairID)
-    else:
-        affairIDList = [affairID]
-        
-    curTime = int(time.time())
-    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
-    affairInfoList = []
-    for affairID in affairIDList:
-        star, state = __GetAffairInfo(curPlayer, affairID)
-        remainDuration = __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict)
-        affairInfo = ChPyNetSendPack.tagMCFamilyAffair()
-        affairInfo.AffairID = affairID
-        affairInfo.Star = star
-        affairInfo.State = state
-        affairInfo.RemainDuration = max(0, remainDuration)
-        affairInfoList.append(affairInfo)
-        
-    if not affairInfoList:
-        return
-    clientPack = ChPyNetSendPack.tagMCFamilyAffairInfo()
-    clientPack.Clear()
-    clientPack.RefreshFreeCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairRefreshFree)
-    clientPack.AffairInfoList = affairInfoList
-    clientPack.Count = len(clientPack.AffairInfoList)
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
-    return
-
-##--------------------------------------------------------------------------------------------------
-##--------------------------------------- 仙盟传功 --------------------------------------------------
-#// A6 15 传功操作 #tagCMChuangongOP
-#
-#struct    tagCMChuangongOP
-#{
-#    tagHead        Head;
-#    BYTE        OPType;        // 操作类型:1-邀请;2-回应;3-领奖;
-#    DWORD        PlayerID;        // 目标玩家ID;回应时为邀请方玩家ID
-#    BYTE        OPData;        // 操作数据,可选:回应时为是否同意
-#};
-def OnChuangongOP(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    opType = clientData.OPType
-    tagPlayerID = clientData.PlayerID
-    opData = clientData.OPData
-    
-    if tagPlayerID and curPlayer.GetPlayerID() == tagPlayerID:
-        GameWorld.DebugLog("不能自己传功")
+    curMember = curFamily.FindMember(playerID)
+    if not curMember:
         return
     
-    if opType == 1:
-        __Chuangong_Invite(curPlayer, tagPlayerID)
-    elif opType == 2:
-        __Chuangong_Response(curPlayer, tagPlayerID, opData)
-    elif opType == 3:
-        __Chuangong_GetAward(curPlayer)
-    return
-
-def SendGameServer_FamilyChuangong(curPlayer, msgType, msgData):
-    playerID = curPlayer.GetPlayerID()
-    GameWorld.DebugLog("传功同步GameServer: msgType=%s,%s" % (msgType, msgData), playerID)
-    msgInfo = str([msgType, msgData])
-    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "FamilyChuangong", msgInfo, len(msgInfo))
-    return
-
-def __CheckCanChuangong(curPlayer):
-    playerID = curPlayer.GetPlayerID()
-    remainCnt = PlayerActivity.GetDailyActionrRemainCnt(curPlayer, ShareDefine.DailyActionID_FamilyChuanGong)
-    if remainCnt <= 0:
-        GameWorld.DebugLog("没有传功次数了", playerID)
+    if not reqDataEx:
         return
-    openServerDayLimit = IpyGameDataPY.GetFuncCfg("FamilyChuangong", 1)
-    if openServerDayLimit:
-        openServerDay = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_ServerDay) + 1
-        if openServerDay > openServerDayLimit:
-            GameWorld.DebugLog("当前开服天不能传功: openServerDay=%s > %s" % (openServerDay, openServerDayLimit), playerID)
-            return
+    
+    if "donateCnt" not in reqDataEx:
+        return
+    donateCnt = reqDataEx["donateCnt"]
+    
+    ipyData = IpyGameDataPY.GetIpyGameData("FamilyDonate", donateType)
+    if not ipyData:
+        return
+    
+    moneyType = ipyData.GetMoneyType()
+    moneyValue = ipyData.GetMoneyValue()
+    if not moneyType or not moneyValue:
+        return
+    
+    CrossPlayer.CostPlayerResources(crossPlayer, "FamilyMoneyDonate", costMoneyDict={moneyType:moneyValue})
+    
+    awardItemList = ipyData.GetAwardItemList()
+    donateCnt += 1
+    CrossPlayer.SetPlayerNomalDict(crossPlayer, {ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType:donateCnt}, isDayReset=True)
+    #SyncDonateCntInfo(curPlayer)
+    # 增加成员捐献次数记录
+    memDonateCntDay = curMember.GetDonateCntDay() + 1
+    memDonateCntTotal = min(curMember.GetDonateCntTotal() + 1, ChConfig.Def_UpperLimit_DWord)
+    curMember.SetDonateCntDay(memDonateCntDay)
+    curMember.SetDonateCntTotal(memDonateCntTotal)
+    GameWorld.DebugLog("家族捐献: donateType=%s,donateCnt=%s,%s,memDonateCntDay=%s,memDonateCntDay=%s" 
+                       % (donateType, donateCnt, awardItemList, memDonateCntDay, memDonateCntTotal), playerID)
+    CrossPlayer.GivePlayerResources(crossPlayer, awardItemList, eventName="FamilyMoneyDonate")
     return True
 
-def __Chuangong_Invite(curPlayer, tagPlayerID):
-    ## 传功邀请
-    if not __CheckCanChuangong(curPlayer):
+def __OnFamilyMoneyDonate_Ret(curPlayer, clientData, isOK):
+    if not isOK:
         return
-    SendGameServer_FamilyChuangong(curPlayer, "Invite", [tagPlayerID])
+    SyncDonateCntInfo(curPlayer)
     return
 
-def __Chuangong_Response(curPlayer, tagPlayerID, isOK):
-    ## 传功回应
-    SendGameServer_FamilyChuangong(curPlayer, "Response", [tagPlayerID, isOK])
+def AddFamilyExp(curPlayer, addExp):
+    ## 增加玩家家族经验
+    clientData, tick = None, 0
+    reqDataEx = {"addExp":addExp}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__AddFamilyExp", reqCD=0, reqDataEx=reqDataEx)
     return
-
-def __Chuangong_GetAward(curPlayer):
-    ## 传功领奖
-    if not __CheckCanChuangong(curPlayer):
+def __AddFamilyExp(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
         return
-    PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_FamilyChuanGong, 1)
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    curFamily = familyMgr.FindFamily(familyID)
+    if not curFamily:
+        return
+    curLV = curFamily.GetLV()
+    curExp = curFamily.GetExp()
     
-    playerID = curPlayer.GetPlayerID()
-    fromLV = curPlayer.GetLV()
+    if not reqDataEx:
+        return
+    addExp = reqDataEx["addExp"]
     
-    # 给经验
-    totalExp = 0
-    giveRound = IpyGameDataPY.GetFuncCfg("FamilyChuangong", 3)
-    worldlv = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
-    playerControl = PlayerControl.PlayerControl(curPlayer)
-    for index in range(giveRound):
-        reLV = curPlayer.GetLV()
-        reExp = PlayerControl.GetPlayerReExp(curPlayer)
-        giveExp = eval(IpyGameDataPY.GetFuncCompileCfg("FamilyChuangong", 2))
-        finalAddExp = playerControl.AddExp(giveExp, ShareDefine.Def_ViewExpType_Chuangong)
-        totalExp += finalAddExp
-        GameWorld.DebugLog("传功经验: 轮次=%s,reLV=%s,reExp=%s,finalAddExp=%s, %s" % (index, reLV, reExp, finalAddExp, totalExp), playerID)
+    updLV = curLV
+    updExp = curExp + addExp
+    GameWorld.DebugLog("增加公会经验: curLV=%s,curExp=%s,addExp=%s,updExp=%s" % (curLV, curExp, addExp, updExp), playerID)
+    
+    ipyData = IpyGameDataPY.GetIpyGameData("Family", curLV)
+    lvUPExp = ipyData.GetNeedExp()
+    while lvUPExp and updExp >= lvUPExp:
+        ipyData = IpyGameDataPY.GetIpyGameDataNotLog("Family", updLV + 1)
+        if not ipyData:
+            break
+        updLV += 1
+        updExp -= lvUPExp
+        lvUPExp = ipyData.GetNeedExp()
+        GameWorld.DebugLog("    公会升级: updLV=%s,updExp=%s,lvUPExp=%s" % (updLV, updExp, lvUPExp), playerID)
         
-    PlayerWeekParty.AddWeekPartyActionCnt(curPlayer, ChConfig.Def_WPAct_FamilyChuanGong, 1)
-    PlayerTongTianLing.AddTongTianTaskValue(curPlayer, ChConfig.TTLTaskType_ChuanGong, 1)
+    curFamily.SetLV(updLV)
+    curFamily.SetExp(updExp)
     
-    # 给物品
-    giveItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyChuangong", 4)
-    ItemControler.GivePlayerItemOrMail(curPlayer, giveItemList)
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 公会经验
+    return True
+
+def AddFamilyContrib(curPlayer, addContribValue):
+    ## 增加玩家累计家族贡献
+    clientData, tick = None, 0
+    reqDataEx = {"addContribValue":addContribValue}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__AddFamilyContrib", reqCD=0, reqDataEx=reqDataEx)
+    return
+def __AddFamilyContrib(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if familyID <= 0:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    curFamily = familyMgr.FindFamily(familyID)
+    if not curFamily:
+        return
+    curMember = curFamily.FindMember(playerID)
+    if not curMember:
+        return
     
-    # 同步结果
-    syncItemList = []
-    for itemID, itemCount, _ in giveItemList:
-        syncItem = ChPyNetSendPack.tagMCChuangongItem()
-        syncItem.Clear()
-        syncItem.ItemID = itemID
-        syncItem.ItemCount = itemCount
-        syncItemList.append(syncItem)
-        # 有协助感谢礼盒
-        if IpyGameDataPY.GetIpyGameDataNotLog("AssistThanksGift", itemID):
-            SendGameServer_FamilyChuangong(curPlayer, "ThanksGift", [itemID])
-    clientPack = ChPyNetSendPack.tagMCChuangongResult()
-    clientPack.Clear()
-    clientPack.FromLV = fromLV
-    clientPack.ToLV = curPlayer.GetLV()
-    clientPack.Exp = totalExp % ChConfig.Def_PerPointValue
-    clientPack.ExpPoint = totalExp / ChConfig.Def_PerPointValue
-    clientPack.AwardItemList = syncItemList
-    clientPack.ItemCount = len(clientPack.AwardItemList)
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    if not reqDataEx:
+        return
+    addContribValue = reqDataEx["addContribValue"]
+    
+    contribDay = curMember.GetContribDay() + addContribValue
+    contribTotal = min(curMember.GetContribTotal() + addContribValue, ChConfig.Def_UpperLimit_DWord)
+    curMember.SetContribDay(contribDay)
+    curMember.SetContribTotal(contribTotal)
+    GameWorld.DebugLog("增加成员贡献: familyID=%s,addContribValue=%s,contribDay=%s,contribTotal=%s" % (familyID, addContribValue, contribDay, contribTotal), playerID)
+    
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 成员贡献
     return
 
-##--------------------------------------------------------------------------------------------------
-##----------------------------------------- 珍宝阁 --------------------------------------------------
-#// A6 16 珍宝阁操作 #tagCMZhenbaogeOP
+## ------------------------------------------------------------------------------------------------
+
+#// A6 17 查询家族行为信息 #tagCMQueryFamilyAction
 #
-#struct    tagCMZhenbaogeOP
+#struct    tagCMQueryFamilyAction
 #{
-#    tagHead     Head;
-#    BYTE    OpType;    // 操作:0-砍价;1-购买
+#    tagHead        Head;
+#    BYTE        ActionType;        // 行为类型
+#    DWORD        FamilyID;         // 家族ID,发0默认自己家族
 #};
-def OnZhenbaogeOP(index, clientData, tick):
+def OnQueryFamilyAction(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    opType = clientData.OpType
-    playerID = curPlayer.GetPlayerID()
-    if not curPlayer.GetFamilyID():
-        GameWorld.DebugLog("没有仙盟无法操作珍宝阁!", playerID)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnQueryFamilyAction")
+    return
+
+def __OnQueryFamilyAction(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    actionType = clientData.ActionType
+    familyID = clientData.FamilyID
+    if not familyID:
+        familyID = crossPlayer.GetFamilyID()
+    SendFamilyActionInfo(crossPlayer, familyID, actionType)
+    return
+
+def SendFamilyActionInfo(crossPlayer, familyID, actionType):
+    ## 发送家族行为
+    # @param crossPlayer: 为None时通知该公会所有成员
+    if not familyID:
         return
+    familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
     
-    # 砍价
-    if opType == 0:
-        leaveTimeEx = PlayerControl.GetLeaveFamilyTimeEx(curPlayer)
-        cutState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut)
-        if cutState and leaveTimeEx:
-            cutCDTimes = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 3) * 60
-            passTimes = int(time.time()) - leaveTimeEx
-            if passTimes < cutCDTimes:
-                GameWorld.DebugLog("今日已砍价变更仙盟砍价CD中! passTimes=%s < %s" % (passTimes, cutCDTimes), playerID)
-                return
-        SendGameServer_FamilyZhenbaoge(curPlayer, "Cut", [])
+    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
+    clientPack.FamilyID = familyID
+    clientPack.ActionType = actionType
+    clientPack.FamilyActionList = []
+    
+    for index in xrange(familyAction.Count()):
+        familyActionData = familyAction.At(index)
         
-    # 购买
-    elif opType == 1:
-        buyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
-        if buyState:
-            GameWorld.DebugLog("珍宝阁今日已购买!", playerID)
-            return
-        moneyType = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 1)
-        playerMoneyValue = PlayerControl.GetMoney(curPlayer, moneyType)
-        SendGameServer_FamilyZhenbaoge(curPlayer, "Buy", [playerMoneyValue])
+        actionData = ChPyNetSendPack.tagMCFamilyAction()
+        actionData.Time = familyActionData.GetTime()
+        actionData.Name = familyActionData.GetName()
+        actionData.NameLen = len(actionData.Name)
+        actionData.Value1 = familyActionData.GetValue1()
+        actionData.Value2 = familyActionData.GetValue2()
+        actionData.Value3 = familyActionData.GetValue3()
+        actionData.Value4 = familyActionData.GetValue4()
+        actionData.Value5 = familyActionData.GetValue5()
+        actionData.Value6 = familyActionData.GetValue6()
+        actionData.UseData = familyActionData.GetUserData()
+        actionData.UseDataLen = len(actionData.UseData)
+        clientPack.FamilyActionList.append(actionData)
         
-    return
-
-def SendGameServer_FamilyZhenbaoge(curPlayer, msgType, msgData):
-    playerID = curPlayer.GetPlayerID()
-    tick = GameWorld.GetGameWorld().GetTick()
-    if not GameWorld.SetPlayerTickTime(curPlayer, ChConfig.TYPE_Player_Tick_FamilyZhenbaoge, tick):
-        GameWorld.DebugLog("请求CD中...", playerID)
+    clientPack.Count = len(clientPack.FamilyActionList)
+    
+    if crossPlayer:
+        CrossPlayer.SendFakePack(crossPlayer, clientPack)
         return
-    GameWorld.DebugLog("珍宝阁同步GameServer: msgType=%s,%s" % (msgType, msgData), playerID)
-    msgInfo = str([msgType, msgData])
-    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "FamilyZhenbaoge", msgInfo, len(msgInfo))
+    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
     return
 
-def GameServer_FamilyZhenbaogeRet(curPlayer, resultList):
-    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_FamilyZhenbaoge, 0)
+def SendFamilyAction(actionDataList, crossPlayer=None):
+    ## 同步指定公会action
+    # @param actionDataList: 支持列表或指定actionData
+    # @param crossPlayer: 为None时通知该公会所有成员
+    if not isinstance(actionDataList, list):
+        actionDataList = [actionDataList]
+    if not actionDataList:
+        return
+    familyActionData = actionDataList[0]
+    familyID = familyActionData.GetFamilyID()
+    actionType = familyActionData.GetActionType()
+    
+    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
+    clientPack.FamilyID = familyID
+    clientPack.ActionType = actionType
+    clientPack.FamilyActionList = []
+    for familyActionData in actionDataList:
+        actionData = ChPyNetSendPack.tagMCFamilyAction()
+        actionData.Time = familyActionData.GetTime()
+        actionData.Name = familyActionData.GetName()
+        actionData.NameLen = len(actionData.Name)
+        actionData.Value1 = familyActionData.GetValue1()
+        actionData.Value2 = familyActionData.GetValue2()
+        actionData.Value3 = familyActionData.GetValue3()
+        actionData.Value4 = familyActionData.GetValue4()
+        actionData.Value5 = familyActionData.GetValue5()
+        actionData.Value6 = familyActionData.GetValue6()
+        actionData.UseData = familyActionData.GetUserData()
+        actionData.UseDataLen = len(actionData.UseData)
+        clientPack.FamilyActionList.append(actionData)
+    clientPack.Count = len(clientPack.FamilyActionList)
+    
+    if crossPlayer:
+        CrossPlayer.SendFakePack(crossPlayer, clientPack)
+        return
+    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
+    return
+
+def OnFamilyTalk(curPlayer, familyID, talkPack, tick):
+    clientData, tick = None, 0
+    reqDataEx = {"talkBuffer":talkPack.GetBuffer()}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyTalk", reqCD=0, reqDataEx=reqDataEx)
+    return
+def __OnFamilyTalk(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    talkBuffer = reqDataEx["talkBuffer"]
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
+    if not familyID or not talkBuffer:
+        return
+    clientPack = ChPyNetSendPack.tagMCTalk()
+    clientPack.ReadData(talkBuffer)
+    
+    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
+    
+    # 聊天缓存
+    channelType = clientPack.ChannelType
+    content = clientPack.Content
+    bubbleBox = clientPack.BubbleBox
+    PlayerTalk.DoTalkCache(channelType, playerID, content, bubbleBox, familyID)
+    return
+
+## -------------------------------------- 游戏服本服处理 --------------------------------------------
+'''
+为方便本服、跨服互通公会逻辑统一,公会相关数据处理统一使用 CrossPlayer,视为以前的GameServer处理,这样本服跨服的公会管理通用
+本服的curPlayer仅处理以前类似MapServer的curPlayer相关逻辑
+【注】 MapServer的逻辑不能再直接获取 family 实例进行逻辑处理,只能处理 curPlayer 可用的逻辑
+'''
+
+def C2S_FamilyMapPlayer(dataMsg, playerID):
+    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+    if not curPlayer:
+        return
+    doType = dataMsg["doType"]
+    doData = dataMsg["doData"]
+    if doType == "FamilyRefresh":
+        Do_MapServer_FamilyRefresh(curPlayer, doData)
+    return
+
+def Do_MapServer_PlayerOnDay(curPlayer):
+    ResetDailyDonateCnt(curPlayer)
+    return
+
+def Do_MapServer_PlayerLogin(curPlayer):
+    DBFamily.Sync_FamilyCrossInfo(curPlayer)
+    SyncDonateCntInfo(curPlayer)
+    PlayerFamilyZhenbaoge.OnPlayerLogin(curPlayer)
+    return
+
+def Do_MapServer_FamilyRefresh(curPlayer, doData):
+    tick = GameWorld.GetGameWorld().GetTick()
     playerID = curPlayer.GetPlayerID()
-    msgType, _ = resultList[:2]
-    retData = resultList[2:]
-    GameWorld.Log("仙盟珍宝阁GameServer返回: %s" % str(resultList), playerID)
-    if msgType == "Cut":
-        cutPrice = retData[0]
-        if not cutPrice:
-            # 砍价失败不处理后续
-            return
-        PlayerControl.NotifyCode(curPlayer, "ZhenbaogeCut", [cutPrice])
-        if not curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut):
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeCut, 1)
-            Sync_ZhenbaogeInfo(curPlayer)
+    refreshFamilyID = doData["FamilyID"]
+    refreshFmLV = doData.get("FmLV", 0)
+    refreshFamilyLV = doData.get("FamilyLV", 0)
+    refreshFamilyName = doData.get("FamilyName", "")
+    refreshEmblemID = doData.get("EmblemID", 0)
+    refreshEmblemWord = doData.get("EmblemWord", "")
+    
+    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, refreshFamilyID, refreshFamilyName, refreshEmblemID, refreshEmblemWord)
+    
+    lastFamilyID = curPlayer.GetFamilyID()
+    lastFamilyLV = curPlayer.GetFamilyLV() # 公会等级,非职位等级
+    lastFmLV = PlayerControl.GetFamilyMemberLV(curPlayer)
+    
+    if lastFamilyID != refreshFamilyID:
+        curPlayer.SetFamilyID(refreshFamilyID)
         
-    elif msgType == "Buy":
-        isOK = retData[0]
-        if not isOK:
-            return
-        buyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
-        if buyState:
-            #一天只能买一次,防止变更仙盟刷
-            return
-        nowPrice, giveItemList = retData[1:]
+    if curPlayer.GetFamilyName() != refreshFamilyName:
+        curPlayer.SetFamilyName(refreshFamilyName)
+        #curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
         
-        moneyType = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 1)
-        if nowPrice > 0:
-            if not PlayerControl.PayMoney(curPlayer, moneyType, nowPrice, "Zhenbaoge"):
-                GameWorld.ErrLog("珍宝阁购买货币不足! nowPrice=%s" % nowPrice, playerID)
-                return
-        elif nowPrice < 0:
-            PlayerControl.GiveMoney(curPlayer, moneyType, -nowPrice, "Zhenbaoge")
-        else: # 0不处理
+    if lastFmLV != refreshFmLV:
+        PlayerControl.SetFamilyMemberLV(curPlayer, refreshFmLV)
+        
+    if lastFamilyLV != refreshFamilyLV:
+        isLVUP = False
+        if lastFamilyLV and refreshFamilyLV > 0:
+            #触发家族升级事件
+            isLVUP = True
+        curPlayer.SetFamilyLV(refreshFamilyLV)
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
+        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
+        if isLVUP:
             pass
         
-        if giveItemList:
-            ItemControler.GivePlayerItemOrMail(curPlayer, giveItemList, event=["Zhenbaoge", False, {}], isNotifyAward=False)
-        ItemControler.NotifyGiveAwardInfo(curPlayer, giveItemList, "Zhenbaoge", moneyInfo={moneyType:0 if nowPrice > 0 else -nowPrice})
+    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
+        #玩家离开家族
+        isVoluntarily = doData.get("isVoluntarily", 0)
+        __OnLeaveFamily(curPlayer, isVoluntarily, tick)
         
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy, 1)
-        Sync_ZhenbaogeInfo(curPlayer)
+    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
+        #刚进家族并为族长,触发建家族事件
+        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
+            pass
             
+        #进入家族触发事件
+        __OnEnterFamily(curPlayer, tick)
+        
+    #---通知客户端刷新属性---
+    curPlayer.View_FamilyInfoRefresh() #//04 30 玩家家族名字职位等信息刷新#tagPlayerInFamilyInfoRefresh
     return
 
-def Sync_ZhenbaogeInfo(curPlayer):
-    clientPack = ChPyNetSendPack.tagMCFamilyZhenbaogeInfo()
-    clientPack.CutState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut)
-    clientPack.BuyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
+def __OnEnterFamily(curPlayer, tick):
+    ## 进入家族触发事件
+    PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily)
+    return
+
+def __OnLeaveFamily(curPlayer, isVoluntarily, tick):
+    ## 退出家族触发事件
+    #---清空家族相关信息---
+    curPlayer.SetPerExp(0)
+    curPlayer.SetFamilyHornor(0)
+    curPlayer.SetFamilyActiveValue(0)
+    curPlayer.SetLastWeekFamilyActiveValue(0)
+    curPlayer.SetFamilyLV(0)
+    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
+    leaveCnt, kickedCnt, _ = PlayerControl.GetLeaveFamilyInfo(curPlayer)
+    GameWorld.DebugLog("__OnLeaveFamily: isVoluntarily=%s,leaveCnt=%s,kickedCnt=%s" % (isVoluntarily, leaveCnt, kickedCnt))
+    delMoneyType, delMoneyPer = IpyGameDataPY.GetFuncCfg("FamilyLeave", 3), 0
+    if isVoluntarily:
+        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 4)
+        if delMoneyPerList:
+            delMoneyPer = delMoneyPerList[leaveCnt] if len(delMoneyPerList) > leaveCnt else delMoneyPerList[-1]
+            
+        leaveCnt += 1
+        GameWorld.DebugLog("    增加主动离开次数: leaveCnt=%s" % (leaveCnt))
+    else:
+        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 5)
+        if delMoneyPerList:
+            delMoneyPer = delMoneyPerList[kickedCnt] if len(delMoneyPerList) > kickedCnt else delMoneyPerList[-1]
+            
+        kickedCnt += 1
+        GameWorld.DebugLog("    增加被踢离开次数: kickedCnt=%s" % (kickedCnt))
+    PlayerControl.SetLeaveFamilyInfo(curPlayer, leaveCnt, kickedCnt, isVoluntarily)
+    if delMoneyType and delMoneyPer:
+        nowMoney = PlayerControl.GetMoney(curPlayer, delMoneyType)
+        delMoney = int(nowMoney * delMoneyPer / 100.0)
+        GameWorld.DebugLog("    扣除货币: delMoneyType=%s,delMoneyPer=%s,nowMoney=%s,delMoney=%s" % (delMoneyType, delMoneyPer, nowMoney, delMoney))        
+        PlayerControl.PayMoney(curPlayer, delMoneyType, delMoney, "LeaveFamily")
+        
+    PlayerFamilyTaofa.OnPlayerLeaveFamily(curPlayer)
+    FBLogic.OnLeaveFamily(curPlayer, tick)
+    return
+
+def ResetDailyDonateCnt(curPlayer):
+    isReset = False
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetFamilyDonateCount()):
+        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
+        donateType = ipyData.GetDonateType()
+        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
+        if donateCnt:
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType, 0)
+            isReset = True
+    if isReset:
+        SyncDonateCntInfo(curPlayer)
+    return
+
+def SyncDonateCntInfo(curPlayer):
+    donateCntList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetFamilyDonateCount()):
+        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
+        donateType = ipyData.GetDonateType()
+        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
+        donateCntList.append(donateCnt)
+    if not donateCntList:
+        return
+    clientPack = ChPyNetSendPack.tagSCDonateCntInfo()
+    clientPack.DonateCntList = donateCntList
+    clientPack.Count = len(clientPack.DonateCntList)
     NetPackCommon.SendFakePack(curPlayer, clientPack)
     return
-
-##--------------------------------------------------------------------------------------------------

--
Gitblit v1.8.0