From bc5f3f1c88d225109fa39a85b209ef13f5fb52a9 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期五, 06 二月 2026 21:28:13 +0800
Subject: [PATCH] 66 【公会】基础主体-服务端(跨服公会所有基本功能支持、跨服砍价支持;修改查看目标公会、查看目标玩家方式;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py | 3185 +++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 1,830 insertions(+), 1,355 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 2420dbf..c2c6d64 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
@@ -28,11 +28,14 @@
 import IPY_PlayerDefine
 import IpyGameDataPY
 import IPY_GameWorld
-import ItemControler
-import GameFuncComm
+import PlayerMail
 import PlayerTask
-import DBDataMgr
+import CrossPlayer
 import DirtyList
+import DBDataMgr
+import DBFamily
+import CrossMsg
+import CrossMgr
 
 import random
 import time
@@ -41,24 +44,7 @@
 
 Def_CreatFamily_MaxStr = 33
 
-#仙盟变更类型
-FamilyChangeTypes = (
-FamilyChangeType_None,
-FamilyChangeType_MemJoin, # 成员加入 1
-FamilyChangeType_MemLeave, # 成员退出 2
-FamilyChangeType_JoinSet, # 收人设置修改 3
-FamilyChangeType_Broadcast, # 公告修改 4
-FamilyChangeType_EChange, # 徽章修改 5
-FamilyChangeType_LeaderChange, # 盟主变更 6
-FamilyChangeType_MemFmlvChange, # 成员职位变更 7
-FamilyChangeType_MemLogin, # 成员上线 8
-FamilyChangeType_MemLogout, # 成员离线9
-FamilyChangeType_FamilyLVExp, # 仙盟等级经验变更 10
-FamilyChangeType_MemContrib, # 成员贡献变更 11
-FamilyChangeType_OnDay, # 过天 12
-) = range(13)
-
-#仙盟权限
+#公会权限
 (
 FamilyPowerID_Call,         #招人 1
 FamilyPowerID_ChangeFmlv,   #变更职位 2
@@ -66,56 +52,133 @@
 FamilyPowerID_Kick,         #踢人 4
 ) = range(1, 1 + 4)
 
-#仙盟职位对应人数设置属性名
+#公会职位对应人数设置属性名
 Def_FmlSetAttrName = {
                       IPY_PlayerDefine.fmlMember:"MemberMax",
                       IPY_PlayerDefine.fmlCounsellor:"EliteMax",
                       IPY_PlayerDefine.fmlViceLeader:"DeputyLeaderMax",
                       }
 
-def FamilyOnDay():
+def GetRenameTime(family): return family.GetExtra1()
+def SetRenameTime(family, renameTime): return family.SetExtra1(renameTime)
+
+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
     
+    __doFamilyOnDay()
+    DBFamily.CheckCrossFamilyTransData()
+    return
+
+def __doFamilyOnDay():
     familyManager = DBDataMgr.GetFamilyMgr()
-    for i in range(0, familyManager.GetCount()):
-        family = familyManager.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)
+    for zoneID in familyManager.GetZoneIDListThisServer():
+        zoneMgr = familyManager.GetZoneFamilyMgr(zoneID)
+        for i in range(0, zoneMgr.GetCount()):
+            family = zoneMgr.GetAt(i)
+            familyID = family.GetID()
             
-        Broadcast_FamilyChange(familyID, FamilyChangeType_OnDay)
-        
+            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
+    __doPlayerOnDay(curPlayer)
     return
 
 def PlayerOnDay(curPlayer):
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
+    ## 本服时间的onday玩家事件
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已经跨服了,不处理成员本服过天", curPlayer.GetPlayerID())
         return
-    ResetDailyDonateCnt(curPlayer)
+    __doPlayerOnDay(curPlayer)
+    return
+
+def __doPlayerOnDay(curPlayer):
     PlayerFamilyZhenbaoge.PlayerOnDay(curPlayer)
     PlayerFamilyTaofa.PlayerOnDay(curPlayer)
+    Do_MapServer_PlayerOnDay(curPlayer)
     return
 
 def OnPlayerLogin(curPlayer, tick):
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
+    Do_MapServer_PlayerLogin(curPlayer)
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员登录逻辑 OnPlayerLogin", curPlayer.GetPlayerID())
         return
-    PlayerLoginRefreshFamily(curPlayer, tick)
-    Sync_RequestAddFamilyInfo(curPlayer, False)
-    SyncDonateCntInfo(curPlayer)
-    PlayerFamilyZhenbaoge.OnPlayerLogin(curPlayer)
-    PlayerFamilyTaofa.OnPlayerLogin(curPlayer)
+    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
+    OnCrossPlayerLogin(crossPlayer)
+    return
+
+def OnCrossPlayerLogin(crossPlayer):
+    ## 玩家上线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
+    PlayerLoginRefreshFamily(crossPlayer)
+    Sync_RequestAddFamilyInfo(crossPlayer, False)
+    #PlayerFamilyTaofa.OnPlayerLogin(curPlayer) 讨伐待修改
     return
 
 def OnPlayerLogout(curPlayer):
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    if DBFamily.IsFamilyCross():
+        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员离线逻辑 OnPlayerLogout", curPlayer.GetPlayerID())
+        return
+    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
+    OnCrossPlayerLogout(crossPlayer)
+    return
+
+def OnCrossPlayerLogout(crossPlayer):
+    ## 玩家下线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if not familyID:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -126,24 +189,64 @@
     if not curMember:
         return
     curMember.SetOffTime(int(time.time()))
-    #XW_JZ_LeaguerLeaveline  <n color="0,190,255">{%S1%}</n><n color="255,255,0">下线了!</n>    25  -   -
-    #NotifyAllFamilyMemberMsg(familyID, "XW_JZ_LeaguerLeaveline", [curPlayer.GetPlayerName()])
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLogout, excludeIDList=[playerID])
+    
+    # 通知成员下线
+    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID]) # 成员下线
     return
 
-def OnWeekEx(curPlayer):
-    #重置
+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 PlayerLoginRefreshFamily(curPlayer, tick):
+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 = curPlayer.GetPlayerID()
+    playerID = crossPlayer.GetPlayerID()
     familyMgr = DBDataMgr.GetFamilyMgr()
     refreshFamilyID = familyMgr.GetPlayerFamilyID(playerID)
-    MapServer_FamilyRefresh(curPlayer, refreshFamilyID)
-    
-    familyID = curPlayer.GetFamilyID()
+    GameWorld.DebugLog("PlayerLoginRefreshFamily playerID=%s,refreshFamilyID=%s" % (playerID, refreshFamilyID))
+    crossPlayer.SetFamilyID(refreshFamilyID)
+    MapServer_FamilyRefresh(crossPlayer, refreshFamilyID) # 登录
+    familyID = refreshFamilyID
     if not familyID:
         return
     
@@ -154,55 +257,169 @@
     if not curMember:
         return
     curMember.SetOffTime(0) # 在线0,脱机1,>1离线时间
-    curMember.RefreshMember(curPlayer)
-    Sync_FamilyInfo(curPlayer)
-    SendFamilyActionInfo(curPlayer, familyID, ShareDefine.Def_ActionType_FamilyData)
+    curMember.RefreshMemberByID(playerID)
+    
+    Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
+    # 广播成员在线
+    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员登录
     
     #通知招人
     if GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
-        NetPackCommon.SendFakePack(curPlayer, GetPack_FamilyReqJoinInfo(familyID))
-            
-    #XW_JZ_LeaguerOnline <n color="0,190,255">{%S1%}</n><n color="255,255,0">上线了!</n>    25  -   -
-    #NotifyAllFamilyMemberMsg(familyID, "XW_JZ_LeaguerOnline", [curPlayer.GetName()], [playerID])
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLogin, excludeIDList=[playerID])
-    
+        CrossPlayer.SendFakePack(crossPlayer, GetPack_FamilyReqJoinInfo(familyID))
+        
     # 盟主上线处理
-    if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
-        OnFamilyLeaderLogin(curPlayer)
+    #if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
+    #    OnFamilyLeaderLogin(curPlayer)
     return
 
-def OnFamilyLeaderLogin(curPlayer):
-    ## 盟主登录额外处理
-    
-    return
-
-def OnMinute():
-    #战力刷新在DBFamily.OnMinute
-    PlayerFamilyEmblem.CheckExpireEmblem()
-    return
-
-def RefreshFamilyMember(curPlayer):
+def RefreshFamilyMember(crossPlayer):
     ## 玩家成员相关属性变更时同步更新家族成员信息
-    familyID = curPlayer.GetFamilyID()
+    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 = curPlayer.GetPlayerID()
+    playerID = crossPlayer.GetPlayerID()
     member = family.FindMember(playerID)
     if not member:
         return
-    member.RefreshMember(curPlayer)
+    member.RefreshMemberByID(playerID)
     return
 
-def GetRenameTime(dataAction): return dataAction.GetValue1()
-def SetRenameTime(dataAction, setTime): dataAction.SetValue1(setTime)
-def GetFamilyDataAction(familyID):
-    ## 家族额外数据存储的行为数据,可以视为Family公共数据的一个扩展
-    action = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, ShareDefine.Def_ActionType_FamilyData)
-    return action.GetOneAction(True)
+def FamilyPyPackForwarding(curPlayer, clientData, tick, funcName, needResult=False, reqCD=0.5, reqDataEx=None):
+    ## 玩家请求公会功能包转发处理
+    # @needResult: 转发跨服处理完毕是否需要回复状态,一般有消耗货币、物品相关的都要回复,且设置一个较长请求cd,防止重复处理
+    # @param reqCD: 转发跨服请求cd,秒,支持小数
+    
+    playerID = curPlayer.GetPlayerID()
+    
+    crossServerID = DBDataMgr.GetFamilyMgr().GetCurCrossServerID()
+    if crossServerID == -2:
+        PlayerControl.NotifyCode(curPlayer, "FamilyInTransData")
+        return
+    
+    if crossServerID < 0:
+        GameWorld.ErrLog("公会功能异常! crossServerID=%s" % crossServerID)
+        return
+    
+    # 本服处理
+    if crossServerID == 0 or crossServerID == GameWorld.GetGameWorld().GetServerID():
+        isOK = CallPyPackFunc(playerID, clientData, funcName, reqDataEx=reqDataEx)
+        if needResult:
+            __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
+        return
+    
+    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
+        curPlayer.SetDict(funcName, tick)
+        
+    # 剩下的就是大于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
+
+def C2S_FamilyPyPackRet(dataMsg, playerID):
+    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+    if not curPlayer:
+        return
+    funcName = dataMsg["funcName"]
+    packBuff = dataMsg.get("packBuff")
+    
+    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
+
+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
+
+def __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK):
+    ## 处理后额外处理,如成就、任务等
+    funcName = "%s_Ret" % funcName
+    callFunc = GetCallFunc(funcName)
+    if not callFunc:
+        # 结果额外处理函数允许不一定需要,根据功能自行决定
+        return
+    callFunc(curPlayer, clientData, isOK)
+    return
+
+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
 #
@@ -210,72 +427,103 @@
 #{
 #    tagHead        Head;
 #    char        Name[33];
-#    WORD        EmblemID; //选择徽章ID,解锁仙盟等级为1级的均为可选ID
+#    WORD        EmblemID; //选择徽章ID,解锁公会等级为1级的均为可选ID
 #    char        EmblemWord[3];    //徽章文字
 #};
-def OnCreateFamily(index, clientPack, tick):
+def OnCreateFamily(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    inputName = clientPack.Name
-    emblemID = clientPack.EmblemID
-    emblemWord = clientPack.EmblemWord
+    inputName = clientData.Name
     
-    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
+    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
         
-    playerID = curPlayer.GetPlayerID()
-    playerFamilyID = curPlayer.GetFamilyID()
+    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):
                 #玩家已经有家族, 创建失败
-                PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_85890")
+                CrossPlayer.NotifyCode(crossPlayer, "GeRen_chenxin_85890")
                 return
             
-    fullFamilyName = CheckInputFamilyName(curPlayer, inputName)
-    if not fullFamilyName:
+    # 验证重名
+    if CheckFamilyNameExists(crossPlayer, fullFamilyName, fromServerID):
         return
     
-    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
-    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
-    if moneyType and needMoney:
-        if not PlayerControl.PayMoney(curPlayer, moneyType, needMoney, "CreateFamily"):
-            return
-        
-    serverID = GameWorld.GetPlayerServerID(curPlayer)
+    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" % (newFamilyID, playerID, emblemID))
+    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, curPlayer, IPY_PlayerDefine.fmlLeader)
-    familyMgr.Sort()
-    
+    DoPlayerJionFamily(curFamily, playerID, crossPlayer, IPY_PlayerDefine.fmlLeader)
+    if zoneMgr:
+        zoneMgr.Sort()
+        
     #XW_JZ_EstablishSud <n color="255,255,0">恭喜您,家族建立成功!</n>    25  -   -
-    PlayerControl.NotifyCode(curPlayer, "XW_JZ_EstablishSud")
-    PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
+    #CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_EstablishSud")
+    #PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
     PlayerFamilyZhenbaoge.OnZhenbaogeReset(curFamily)
-    return
+    return True
 
 def CheckInputFamilyName(curPlayer, inputName):
-    '''检查玩家输入的仙盟名是否合法,建盟、改名通用
-    @return: None-不合法;非空-合法的仙盟全名
+    '''检查玩家输入的公会名是否合法,建盟、改名通用
+    【注】该函数仅在游戏服验证名字的通用合法性,如长度、敏感词等,公会名重名请在公会所在数据服验证
+    @return: None-不合法;非空-合法的公会全名
     '''
     #C++过滤空格
     familyName = GameWorld.GetGameWorld().GetCharTrim(inputName)
-    fullFamilyName = GetFamilyFullName(curPlayer, familyName)
-    if not fullFamilyName:
-        GameWorld.ErrLog("家族全名异常!", curPlayer.GetPlayerID())
+    
+    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):
@@ -287,49 +535,30 @@
         PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [Def_CreatFamily_MaxStr / 3, Def_CreatFamily_MaxStr])
         return
     
+    return True
+
+def CheckFamilyNameExists(crossPlayer, familyName, fromServerID=0):
+    ## 重名在数据所在服务器验证
     familyMgr = DBDataMgr.GetFamilyMgr()
-    if familyMgr.FindFamilyByName(fullFamilyName):
+    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  -   -
-        PlayerControl.NotifyCode(curPlayer, "NameExists")
-        return
-    
-    return fullFamilyName
+        CrossPlayer.NotifyCode(crossPlayer, "NameExists")
+        return True
+    return False
 
-
-## 获取家族全名
-def GetFamilyFullName(curPlayer, familyName):
-    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 ""
-    
-    specServerDict = IpyGameDataPY.GetFuncEvalCfg("FamilyNameFormat", 4, {})
-    nameFormatInfo = GameWorld.GetDictValueByRangeKey(specServerDict, serverID)
-    if not nameFormatInfo:
-        nameFormatInfo = IpyGameDataPY.GetFuncEvalCfg("FamilyNameFormat", 1)
-    if not nameFormatInfo:
-        return ""
-    nameFormat = nameFormatInfo[0]
-    paramList = [eval(pName) for pName in nameFormatInfo[1:]]
-    fullName = nameFormat % tuple(paramList)
-    
-    maxLen = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 2)
-    if len(fullName) > maxLen:
-        GameWorld.ErrLog("仙盟全名 familyName=%s,全名=%s len=%s > %s, check FamilyNameFormat.txt" % (familyName, fullName, len(fullName), maxLen))
-        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [maxLen / 3, maxLen])
-        return ""
-    
-    return fullName
-
-def DoPlayerJionFamily(family, playerID, jionPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
+def DoPlayerJionFamily(family, playerID, crossPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
     '''加入家族,支持离线玩家加入
-    @param jionPlayer: 如果是离线玩家则为None
+    @param crossPlayer: 如果是离线玩家则为None
     '''
+    familyMgr = DBDataMgr.GetFamilyMgr()
     if isinstance(family, int):
         familyID = family
-        familyMgr = DBDataMgr.GetFamilyMgr()
         curFamily = familyMgr.FindFamily(familyID)
     else:
         curFamily = family
@@ -340,37 +569,33 @@
     familyID = curFamily.GetID()
     member = curFamily.AddMember(playerID)
     member.SetFmLV(jionFamilySetLv)
-    if jionPlayer:
-        member.RefreshMember(jionPlayer)
-    else:
-        member.RefreshMemberByID(playerID)
-    curFamily.SetFightPowerTotal(curFamily.GetFightPowerTotal() + member.GetFightPowerTotal())
+    member.RefreshMemberByID(playerID)
     
     if jionFamilySetLv == IPY_PlayerDefine.fmlLeader:
         curFamily.SetLeaderID(playerID)
         
+    if broadcastFamilyChange:
+        # 广播其他在线成员
+        Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员加入
+        
+    familyMgr.DelPlayerReqJoinFamilyIDAll(playerID)
     #设置玩家身上保存的家族信息
-    if jionPlayer:
-        MapServer_FamilyRefresh(jionPlayer, familyID)
-        Sync_FamilyInfo(jionPlayer)
+    if crossPlayer:
+        MapServer_FamilyRefresh(crossPlayer, familyID) # 加入
+        Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
+        Sync_RequestAddFamilyInfo(crossPlayer)
+        PlayerFamilyTaofa.OnCrossPlayerEnterFamily(crossPlayer)
         
     if jionFamilySetLv != IPY_PlayerDefine.fmlLeader:
-        if broadcastFamilyChange:
-            Broadcast_FamilyChange(familyID, FamilyChangeType_MemJoin, excludeIDList=[playerID])
         #通知所有家族成员, 这个人加入了家族
-        #NotifyAllFamilyMemberMsg(familyID, "XW_JZ_EnterFamily", [member.GetPlayerName()], excludeIDList=[playerID])
-        if jionPlayer:
-            PlayerControl.NotifyCode(jionPlayer, 'XW_JZ_EnterFamilyInfo', [family.GetName()])
+        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)
-    
-    #玩家缓存
-    #PlayerViewCache.OnPlayerFamilyChange(jionPlayer.GetPlayerID(), curFamily.GetID(), curFamily.GetName())
-    #SetMemberFightPower(familyMember, PlayerControl.GetFightPower(jionPlayer))
-    #GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), jionPlayer.GetPlayerID())
     return
 
 def AddFamilyActionNote(curName, familyID, actionType, actionDataList, tick, isClearNone=True, useData=""):
@@ -395,31 +620,1441 @@
             setFunc(value)
     return True
 
-def MapServer_FamilyRefresh(curPlayer, refreshFamilyID, isVoluntarily=0):
-    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
-    @param familyID: 玩家更新的familyID
-    @param isVoluntarily: 是否自愿离开的,仅离开刷新时有效
-    '''
+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
+
+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
+    
+    # 可再扩展自动请求,暂时不处理
+    GameWorld.DebugLog("没有可自动进入的公会!")
+    CrossPlayer.NotifyCode(crossPlayer, "QuickEnterFamilyFail")
+    return
+
+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
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
+    if playerID in reqFamilyIDList:
+        GameWorld.DebugLog('已经在申请加入公会列表中! familyID=%s' % familyID, playerID)
+        return
+    
+    maxReqFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 2)
+    if len(reqFamilyIDList) >= maxReqFamilyCnt:
+        GameWorld.DebugLog('已经达到最大申请加入公会数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
+        return
+    
+    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 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
+#{
+#    tagHead        Head;
+#    BYTE        NewNameLen; 
+#    char        NewName[NewNameLen];
+#    BYTE        ItemIndex;  //改名物品在背包中的位置
+#};
+def UpdateFamilyName(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    newName = clientData.NewName
+    if not CheckInputFamilyName(curPlayer, newName):
+        GameWorld.DebugLog("名字验证不通过")
+        return
+    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
+    if moneyType and moneyValue and not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
+        return
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__UpdateFamilyName", True, 20)
+    return
+
+def __UpdateFamilyName(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    newName = clientData.NewName
+    #itemIndex = clientData.ItemIndex
+    
+    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 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
+
+#// A6 19 查看目标公会 #tagCSViewTagFamily
+#
+#struct tagCSViewTagFamily
+#{
+#    tagHead        Head;
+#    DWORD        FamilyID;
+#    DWORD        DataServerID;    //数据所在服务器ID
+#};
+def OnViewTagFamily(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    tagFamilyID = clientData.FamilyID
+    dataServerID = clientData.DataServerID
+    
+    # 本服或主服是本服
+    if not dataServerID or dataServerID == GameWorld.GetGameWorld().GetServerID():
+        NetPackCommon.SendFakePack(curPlayer, GetTagFamilyInfoPack(tagFamilyID))
+        return
+    
+    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.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
+    clientPack.DataServerID = GameWorld.GetGameWorld().GetServerID()
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
+    return
+
+
+#// A6 12 家族捐献货币 #tagCMFamilyMoneyDonate
+#
+#struct     tagCMFamilyMoneyDonate
+#{
+#    tagHead        Head;
+#    BYTE        DonateType;    // 捐献类型
+#};
+def OnFamilyMoneyDonate(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    playerID = curPlayer.GetPlayerID()
+    donateType = clientData.DonateType
+    
+    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
+    
+    moneyType = ipyData.GetMoneyType()
+    moneyValue = ipyData.GetMoneyValue()
+    if not moneyType or not moneyValue:
+        return
+    if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
+        return
+    
+    reqDataEx = {"donateCnt":donateCnt}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyMoneyDonate", True, 20, reqDataEx=reqDataEx)
+    return
+
+def __OnFamilyMoneyDonate(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
+    donateType = clientData.DonateType
+    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
+    
+    if not reqDataEx:
+        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 __OnFamilyMoneyDonate_Ret(curPlayer, clientData, isOK):
+    if not isOK:
+        return
+    SyncDonateCntInfo(curPlayer)
+    return
+
+def AddFamilyExp(curPlayer, addExp):
+    ## 增加玩家家族经验
+    clientData, tick = None, 0
+    reqDataEx = {"addExp":addExp}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__AddFamilyExp", reqCD=0, reqDataEx=reqDataEx)
+    return
+def __AddFamilyExp(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
+    curLV = curFamily.GetLV()
+    curExp = curFamily.GetExp()
+    
+    if not reqDataEx:
+        return
+    addExp = reqDataEx["addExp"]
+    
+    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)
+        
+    curFamily.SetLV(updLV)
+    curFamily.SetExp(updExp)
+    
+    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
+    
+    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 17 查询家族行为信息 #tagCMQueryFamilyAction
+#
+#struct    tagCMQueryFamilyAction
+#{
+#    tagHead        Head;
+#    BYTE        ActionType;        // 行为类型
+#    DWORD        FamilyID;         // 家族ID,发0默认自己家族
+#};
+def OnQueryFamilyAction(index, clientData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    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)
+    
+    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
+    clientPack.FamilyID = familyID
+    clientPack.ActionType = actionType
+    clientPack.FamilyActionList = []
+    
+    for index in xrange(familyAction.Count()):
+        familyActionData = familyAction.At(index)
+        
+        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 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
+
+## -------------------------------------- 游戏服本服处理 --------------------------------------------
+'''
+为方便本服、跨服互通公会逻辑统一,公会相关数据处理统一使用 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()
-    refreshFmLV = 0
-    refreshFamilyLV = 0
-    refreshFamilyName = ""
-    if refreshFamilyID:
-        familyMgr = DBDataMgr.GetFamilyMgr()
-        curFamily = familyMgr.FindFamily(refreshFamilyID)
-        if curFamily:
-            refreshFamilyLV = curFamily.GetLV()
-            refreshFamilyName = curFamily.GetName()
-            member = curFamily.FindMember(playerID)
-            if member:
-                refreshFmLV = member.GetFmLV()
-        else:
-            refreshFamilyID = 0
-            
+    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() # 仙盟等级,非职位等级
+    lastFamilyLV = curPlayer.GetFamilyLV() # 公会等级,非职位等级
     lastFmLV = PlayerControl.GetFamilyMemberLV(curPlayer)
     
     if lastFamilyID != refreshFamilyID:
@@ -427,7 +2062,7 @@
         
     if curPlayer.GetFamilyName() != refreshFamilyName:
         curPlayer.SetFamilyName(refreshFamilyName)
-        curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
+        #curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
         
     if lastFmLV != refreshFmLV:
         PlayerControl.SetFamilyMemberLV(curPlayer, refreshFmLV)
@@ -445,6 +2080,7 @@
         
     if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
         #玩家离开家族
+        isVoluntarily = doData.get("isVoluntarily", 0)
         __OnLeaveFamily(curPlayer, isVoluntarily, tick)
         
     elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
@@ -461,10 +2097,6 @@
 
 def __OnEnterFamily(curPlayer, tick):
     ## 进入家族触发事件
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    familyMgr.DelPlayerReqJoinFamilyIDAll(curPlayer.GetPlayerID())
-    Sync_RequestAddFamilyInfo(curPlayer)
-    PlayerFamilyTaofa.OnPlayerEnterFamily(curPlayer)
     PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily)
     return
 
@@ -505,1003 +2137,6 @@
     FBLogic.OnLeaveFamily(curPlayer, tick)
     return
 
-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(curPlayer, infoPack=None):
-    ## // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
-    familyID = curPlayer.GetFamilyID()
-    if not familyID:
-        return
-    if not infoPack:
-        infoPack = GetPack_FamilyInfo(familyID)
-    NetPackCommon.SendFakePack(curPlayer, infoPack)
-    return
-
-def GetPack_FamilyInfo(familyID):
-    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 = []
-    for index in xrange(curFamily.GetCount()):
-        member = curFamily.GetAt(index)
-        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)
-    return clientPack
-
-def Broadcast_FamilyChange(familyID, changeType=FamilyChangeType_None, powerID=None, excludeIDList=None):
-    ## // A5 21 家族变更 #tagMCFamilyChange
-    # @param excludeIDList: 不广播的成员ID列表
-    clientPack = ChPyNetSendPack.tagMCFamilyChange()
-    clientPack.Type = changeType
-    Broadcast_FamilyPack(familyID, clientPack, powerID, excludeIDList)
-    return
-
-def Broadcast_FamilyPack(familyID, clientPack, powerID=None, excludeIDList=None):
-    ## 广播家族成员封包
-    # @param powerID: 可指定只发给有该权限的成员
-    # @param excludeIDList: 不广播的成员ID列表
-    if not clientPack:
-        return
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    playerManager = GameWorld.GetPlayerManager()
-    for index in xrange(curFamily.GetCount()):
-        member = curFamily.GetAt(index)
-        playerID = member.GetPlayerID()
-        if excludeIDList and playerID in excludeIDList:
-            continue
-        
-        curPlayer = playerManager.FindPlayerByID(playerID)
-        if not curPlayer:
-            continue
-        
-        if powerID != None and not GetFamilyMemberHasPow(member, powerID):
-            continue
-        
-        NetPackCommon.SendFakePack(curPlayer, clientPack)
-    return
-
-def NotifyAllFamilyMemberMsg(familyID, code, paramList=[], excludeIDList=None):
-    ## 通知所有家族成员信息
-    # @param excludeIDList: 不通知的成员ID列表
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    playerManager = GameWorld.GetPlayerManager()
-    for i in xrange(curFamily.GetCount()):
-        member = curFamily.GetAt(i)
-        playerID = member.GetPlayerID()
-        if excludeIDList and playerID in excludeIDList:
-            continue
-        curPlayer = playerManager.FindPlayerByID(playerID)
-        if not curPlayer:
-            continue
-        PlayerControl.NotifyCode(curPlayer, code, paramList)
-    return
-    
-#// A6 01 向玩家申请加入家族 #tagCMRequestJoinFamilyByPlayer
-#
-#struct    tagCMRequestJoinFamilyByPlayer
-#{
-#    tagHead        Head;
-#    DWORD        TagPlayerID;    //目标家族玩家ID
-#};
-def OnRequestJoinFamilyByPlayer(index, clientData, tick):
-    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)
-    
-    tagFamilyID = clientData.TagFamilyID  # 申请进入的家族ID
-    requestType = clientData.Type   # 申请类型(申请/撤销)
-    
-    # 申请加入
-    if requestType == 0:
-        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily, 1)
-        if not tagFamilyID:
-            AutoJoinFamily(curPlayer)
-        else:
-            RequestJoinTagFamily(curPlayer, tagFamilyID)
-            
-    # 撤销申请
-    elif requestType == 1:
-        CancelJoinTagFamily(curPlayer, tagFamilyID)
-        
-    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
-
-def AutoJoinFamily(curPlayer):
-    if curPlayer.GetFamilyID():
-        return
-    playerID = curPlayer.GetPlayerID()
-    realmLV = curPlayer.GetOfficialRank()
-    GameWorld.DebugLog("玩家一键自动加入家族! realmLV=%s" % realmLV, playerID)
-    if CheckInJoinCD(curPlayer):
-        return
-    
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    indexList = range(familyMgr.GetCount())
-    random.shuffle(indexList) #打乱顺序
-    for index in indexList:
-        family = familyMgr.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, curPlayer)
-        return
-    
-    # 可再扩展自动请求,暂时不处理
-    GameWorld.DebugLog("没有可自动进入的仙盟!")
-    PlayerControl.NotifyCode(curPlayer, "QuickEnterFamilyFail")
-    return
-
-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(curPlayer, familyID):
-    ## 申请加入
-    if CheckInJoinCD(curPlayer):
-        return
-    playerID = curPlayer.GetPlayerID()
-    if curPlayer.GetFamilyID():
-        GameWorld.DebugLog('已经有仙盟不能再申请加入! familyID=%s' % curPlayer.GetFamilyID(), playerID)
-        return
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
-    if playerID in reqFamilyIDList:
-        GameWorld.DebugLog('已经在申请加入仙盟列表中! familyID=%s' % familyID, playerID)
-        return
-    
-    maxReqFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 2)
-    if len(reqFamilyIDList) >= maxReqFamilyCnt:
-        GameWorld.DebugLog('已经达到最大申请加入仙盟数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
-        return
-    
-    tagFamily = familyMgr.FindFamily(familyID)
-    if not tagFamily:
-        return
-    
-    lvMin = tagFamily.GetJoinLVMin()
-    if curPlayer.GetOfficialRank() < lvMin:
-        GameWorld.DebugLog('官职未达到该仙盟加入最低限制! realmLV=%s < %s' % (curPlayer.GetOfficialRank(), 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)
-                PlayerControl.NotifyCode(curPlayer, "jiazu_pan_141056")
-                return
-            
-        tagFamily.AddReqJoinPlayerID(playerID)
-        # 广播给有招人权限的
-        Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
-        
-        #jiazu_pan_500807:申请入帮成功!请等待帮会管理人员审批! 
-        PlayerControl.NotifyCode(curPlayer, "jiazu_pan_500807")
-        Sync_RequestAddFamilyInfo(curPlayer)
-        return
-    
-    # 不需要审核,自动加入
-    memberMax = GetFamilySetting(tagFamily.GetLV(), "MemberMax")
-    if tagFamily.GetCount() >= memberMax:
-        GameWorld.DebugLog('目标仙盟成员已满! familyLV=%s,memberMax=%s' % (tagFamily.GetLV(), memberMax), playerID)
-        return
-    
-    DoPlayerJionFamily(tagFamily, playerID, curPlayer)
-    return
-
-def CancelJoinTagFamily(curPlayer, familyID):
-    # 撤销申请
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    playerID = curPlayer.GetPlayerID()
-    tagFamily = familyMgr.FindFamily(familyID)
-    if tagFamily:
-        tagFamily.DelReqJoinPlayerID(playerID)
-    familyMgr.DelPlayerReqJoinFamilyID(playerID, familyID)
-    Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
-    Sync_RequestAddFamilyInfo(curPlayer)
-    return
-
-def Sync_RequestAddFamilyInfo(curPlayer, isForce=True):
-    ## 通知当前申请加入的哪些家族
-    playerID = curPlayer.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)
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
-    return
-
-def GetPack_FamilyReqJoinInfo(familyID):
-    ## 获取 // A5 22 家族申请加入的玩家信息 #tagMCFamilyReqJoinInfo
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    reqPlayerIDDict = curFamily.GetReqJoinPlayerInfo()
-    
-    playerManager = GameWorld.GetPlayerManager()
-    clientPack = ChPyNetSendPack.tagMCFamilyReqJoinInfo()
-    clientPack.ReqJoinList = []
-    for playerID, reqTime in reqPlayerIDDict.items():
-        curPlayer = playerManager.FindPlayerByID(playerID)
-        reqInfo = ChPyNetSendPack.tagMCFamilyReqJoinPlayer()
-        reqInfo.PlayerID = playerID
-        reqInfo.ReqTime = reqTime
-        if curPlayer:
-            reqInfo.IsOnLine = True
-        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)
-    tagPlayerID = clientData.TagPlayerID
-    isOK = clientData.IsOK
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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
-    
-    playerManager = GameWorld.GetPlayerManager()
-    # 拒绝
-    if not isOK:
-        for tagPlayerID in tagPlayerIDList:
-            family.DelReqJoinPlayerID(tagPlayerID)
-            tagPlayer = playerManager.FindPlayerByID(tagPlayerID)
-            if not tagPlayer:
-                continue
-            Sync_RequestAddFamilyInfo(tagPlayer)
-            #jiazu_pan_592934:{%S}拒绝了您的入帮申请
-            PlayerControl.NotifyCode(tagPlayer, "jiazu_pan_592934", [family.GetName()])
-        Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
-        return
-    
-    # 处理同意
-    offlinePlayerCanJoin = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 3)
-    MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
-    joinOKPlayerIDList = []
-    for tagPlayerID in tagPlayerIDList:
-        if family.GetCount() >= MemberMax:
-            PlayerControl.NotifyCode(curPlayer, "jiazu_lhs_202580")
-            break
-        tagPlayer = playerManager.FindPlayerByID(tagPlayerID)
-        #申请目标不在线
-        if not tagPlayer:
-            if not offlinePlayerCanJoin:
-                GameWorld.DebugLog("离线玩家无法加入仙盟! tagPlayerID=%s" % tagPlayerID, playerID)
-                PlayerControl.NotifyCode(curPlayer, "jiazu_hwj35_367906")
-                continue
-            
-        if family.FindMember(tagPlayerID):
-            GameWorld.DebugLog("已经是本盟成员! tagPlayerID=%s" % tagPlayerID, playerID)
-            PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Repeat")
-            continue
-        
-        tagFamilyID = familyMgr.GetPlayerFamilyID(tagPlayerID)
-        if tagFamilyID:
-            GameWorld.DebugLog("已经加入其他仙盟! tagPlayerID=%s,tagFamilyID=%s" % (tagPlayerID, tagFamilyID), playerID)
-            PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Repeat")
-            continue
-        
-        DoPlayerJionFamily(family, tagPlayerID, tagPlayer, broadcastFamilyChange=False)
-        joinOKPlayerIDList.append(tagPlayerID)
-        
-    if not joinOKPlayerIDList:
-        return
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemJoin, excludeIDList=joinOKPlayerIDList)
-    Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
-    return
-
-#// A6 22 修改收人方式 #tagCMChangeFamilyJoin
-#
-#struct    tagCMChangeFamilyJoin
-#{
-#    tagHead         Head;
-#    BYTE        JoinReview;    //成员加入是否需要审核,默认0自动加入
-#    WORD        JoinLVMin;    //限制最低可加入的玩家等级
-#};
-def OnChangeFamilyJoin(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    joinReview = clientData.JoinReview
-    joinLVMin = clientData.JoinLVMin # 官职
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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)
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, FamilyChangeType_JoinSet, FamilyPowerID_Call, excludeIDList=[playerID])
-    return
-
-#// A6 23 修改家族公告 #tagCMChangeFamilyBroadcast
-#
-#struct    tagCMChangeFamilyBroadcast
-#{
-#    tagHead        Head;
-#    char        Msg[200];
-#};
-def OnChangeFamilyBroadcast(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    broadcast = clientData.Msg
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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)
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, FamilyChangeType_Broadcast, excludeIDList=[playerID])
-    return
-
-#// A6 24 修改家族徽章 #tagCMChangeFamilyEmblem
-#
-#struct    tagCMChangeFamilyEmblem
-#{
-#    tagHead        Head;
-#    BYTE        EmblemID;    // 更换的徽章ID
-#    char        EmblemWord[3];    // 徽章文字
-#};
-def OnChangeFamilyEmblem(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    changeEmblemID = clientData.EmblemID
-    emblemWord = clientData.EmblemWord
-    PlayerFamilyEmblem.OnChangeFamilyEmblem(curPlayer, 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)
-    OnChangeFamilyMemberLV(curPlayer, clientData.PlayerID, clientData.FmLV)
-    return
-
-def OnChangeFamilyMemberLV(curPlayer, tagID, changeFmlv, isGMOP=False):
-    '''变更成员职位
-    @param curPlayer: 操作的玩家
-    @param tagID: 目标成员ID
-    @param changeFmlv: 修改为xx职位
-    @param isGMOP: 是否是GM后台发起的,如果是GM发起的,一般curPlayer传入的为目标成员ID实例
-    '''
-    if not curPlayer:
-        return
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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
-            
-    if changeFmlv == IPY_PlayerDefine.fmlLeader:
-        changeType = FamilyChangeType_LeaderChange
-        ChangeFamilyLeader(family, tagMember)
-        
-    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
-            PlayerControl.NotifyCode(curPlayer, "jiazu_chenxin_31379")
-            GameWorld.DebugLog("目前该职位的人数已经达到上限! changeFmlv=%s,fmLVMemCnt=%s >= %s" % (changeFmlv, fmLVMemCnt, maxCnt))
-            return
-        changeType = FamilyChangeType_MemFmlvChange
-        ChangeFamilyMemberLv(tagMember, changeFmlv)
-        
-    if isGMOP:
-        family.SetBroadcast("")
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, changeType, excludeIDList=[playerID, tagID])
-    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)
-    
-    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagID)
-    if tagPlayer:
-        MapServer_FamilyRefresh(tagPlayer, familyID)
-        Sync_FamilyInfo(tagPlayer)
-        if GetFamilyMemberHasPow(tagMember, FamilyPowerID_Call):
-            NetPackCommon.SendFakePack(tagPlayer, 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
-    NotifyAllFamilyMemberMsg(familyID, "XW_JZ_AppointFamily", [memName, changeFamilyLV])
-    #GetFamilyMgr().SetSyncCrossFamilyUpd(familyMember.GetFamilyID(), familyMember.GetPlayerID(), syncNow=True) # 成员职位变更
-    return
-
-#// A6 26 请求家族成员列表 #tagCMGetFamilyInfo
-#
-#struct    tagCMGetFamilyInfo
-#{
-#    tagHead        Head;
-#};
-def OnGetFamilyInfo(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    Sync_FamilyInfo(curPlayer)
-    return
-
-#// A6 03 离开家族 #tagCMLeaveFamily
-#
-#struct    tagCMLeaveFamily
-#{
-#    tagHead        Head;
-#};
-def OnLeaveFamily(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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(curPlayer.GetName(), familyID, ShareDefine.Def_ActionType_FamilyEvent, 
-                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_Leave], tick)
-    
-    #XW_JZ_LeaveFamily   <n color="0,190,255">{%S1%}</n><n color="255,255,0">退出了家族!</n>  25  -   -
-    NotifyAllFamilyMemberMsg(familyID, "XW_JZ_LeaveFamily", [curPlayer.GetName()])
-    
-    __DoPlayerLeaveFamilyByID(family, playerID, curPlayer)
-    MapServer_FamilyRefresh(curPlayer, 0, 1)
-    
-    if family.GetCount() == 0:
-        #玩家离开后, 家族没有人了 , 删除这个家族
-        familyMgr.DelFamily(familyID)
-        return
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLeave)
-    return
-
-#// A6 05 删除家族成员 #tagCMDeleteFamilyMember
-#
-#struct    tagCMDeleteFamilyMember
-#{
-#    tagHead        Head;
-#    DWORD        MemberID;
-#};
-def OnDeleteFamilyMember(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    tagMemberID = clientData.MemberID
-    
-    playerID = curPlayer.GetPlayerID()
-    if playerID == tagMemberID:
-        return
-    
-    familyID = curPlayer.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  -   -
-    NotifyAllFamilyMemberMsg(familyID, "XW_JZ_LeaveFamily", [tagPlayerName])
-    
-    #删除玩家
-    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagMemberID)
-    __DoPlayerLeaveFamilyByID(family, tagPlayerID, tagPlayer)
-    if tagPlayer:
-        MapServer_FamilyRefresh(tagPlayer, 0)
-        
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLeave)
-    return
-
-def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID, tagPlayer=None):
-    ## 有玩家离开家族处理,主要针对家族层级的,玩家个人的在 __OnLeaveFamily 处理
-    PlayerFamilyTaofa.OnFamilyMemberLeave(curFamily, leavePlayerID, tagPlayer)
-    return
-
-#// A6 11 家族改名 #tagCMRenameFamily
-#
-#struct tagCMRenameFamily
-#{
-#    tagHead        Head;
-#    BYTE        NewNameLen; 
-#    char        NewName[NewNameLen];
-#    BYTE        ItemIndex;  //改名物品在背包中的位置
-#};
-def UpdateFamilyName(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    newName = clientData.NewName
-    #itemIndex = clientData.ItemIndex
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.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 curMember.GetFmLV() != IPY_PlayerDefine.fmlLeader:
-        GameWorld.DebugLog("非盟主不可改名!", playerID)
-        return
-    
-    cdHours = IpyGameDataPY.GetFuncCfg("FamilyRename", 2)
-    if cdHours:
-        cdSeconds = cdHours * 3600
-        curTime = int(time.time())
-        dataAction = GetFamilyDataAction(familyID)
-        lastRenameTime = GetRenameTime(dataAction)
-        if lastRenameTime and (curTime - lastRenameTime) < cdSeconds:
-            GameWorld.DebugLog("仙盟改名CD中! lastRenameTime=%s,cdHours=%s" % (GameWorld.ChangeTimeNumToStr(lastRenameTime), cdHours))
-            return
-        
-    familyName = CheckInputFamilyName(curPlayer, newName)
-    if not familyName:
-        return
-    
-    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
-    if moneyType and moneyValue and not PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyRename"):
-        return
-    
-    family.SetName(familyName)
-    
-    infoPack = GetPack_FamilyInfo(familyID)
-    playerManager = GameWorld.GetPlayerManager()
-    for index in xrange(family.GetCount()):
-        member = family.GetAt(index)
-        memID = member.GetPlayerID()
-        player = playerManager.FindPlayerByID(memID)
-        if not player:
-            continue
-        Sync_FamilyInfo(player, infoPack)
-        player.SetFamilyName(familyName)
-        #player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
-        
-    if cdHours:
-        SetRenameTime(dataAction, curTime)
-        SendFamilyActionInfo(None, familyID, ShareDefine.Def_ActionType_FamilyData)
-    return
-
-#// 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)
-    msg = clientData.Msg
-    pageIndex = clientData.PageIndex
-    showCount = min(clientData.ShowCount, 50)
-    
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    familyCount = familyMgr.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.FamilyList = []
-    for index in range(startIndex, endIndex + 1):
-        if index >= familyCount:
-            break
-        family = familyMgr.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.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
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
-    return
-
-
-#// A6 12 家族捐献货币 #tagCMFamilyMoneyDonate
-#
-#struct     tagCMFamilyMoneyDonate
-#{
-#    tagHead        Head;
-#    BYTE        DonateType;    // 捐献类型
-#};
-def OnFamilyMoneyDonate(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    donateType = clientData.DonateType
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
-    if familyID <= 0:
-        return
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    curMember = curFamily.FindMember(playerID)
-    if not curMember:
-        return
-    
-    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
-    
-    moneyType = ipyData.GetMoneyType()
-    moneyValue = ipyData.GetMoneyValue()
-    if not moneyType or not moneyValue:
-        return
-    if not PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyMoneyDonate"):
-        return
-    
-    awardItemList = ipyData.GetAwardItemList()
-    donateCnt += 1
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType, donateCnt)
-    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)
-    ItemControler.GivePlayerItemOrMail(curPlayer, awardItemList, event=["FamilyMoneyDonate", False, {}])
-    return
-
 def ResetDailyDonateCnt(curPlayer):
     isReset = False
     ipyDataMgr = IpyGameDataPY.IPY_Data()
@@ -1531,163 +2166,3 @@
     clientPack.Count = len(clientPack.DonateCntList)
     NetPackCommon.SendFakePack(curPlayer, clientPack)
     return
-
-def AddFamilyExp(curPlayer, addExp):
-    ## 增加玩家家族经验
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
-    if familyID <= 0:
-        return
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    curLV = curFamily.GetLV()
-    curExp = curFamily.GetExp()
-    
-    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)
-        
-    curFamily.SetLV(updLV)
-    curFamily.SetExp(updExp)
-    
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, FamilyChangeType_FamilyLVExp, excludeIDList=[playerID])
-    return True
-
-def AddFamilyContrib(curPlayer, addContribValue):
-    ## 增加玩家累计家族贡献
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
-    if familyID <= 0:
-        return
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    curFamily = familyMgr.FindFamily(familyID)
-    if not curFamily:
-        return
-    curMember = curFamily.FindMember(playerID)
-    if not curMember:
-        return
-    
-    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)
-    
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemContrib, excludeIDList=[playerID])
-    return
-
-## ------------------------------------------------------------------------------------------------
-
-#// A6 17 查询家族行为信息 #tagCMQueryFamilyAction
-#
-#struct    tagCMQueryFamilyAction
-#{
-#    tagHead        Head;
-#    BYTE        ActionType;        // 行为类型
-#    DWORD        FamilyID;         // 家族ID,发0默认自己家族
-#};
-def OnQueryFamilyAction(index, cliendData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    if not curPlayer:
-        return
-    
-    actionType = cliendData.ActionType
-    familyID = cliendData.FamilyID
-    if not familyID:
-        familyID = curPlayer.GetFamilyID()
-        
-    SendFamilyActionInfo(curPlayer, familyID, actionType)
-    return
-
-def SendFamilyActionInfo(curPlayer, familyID, actionType):
-    ## 发送家族行为
-    # @param curPlayer: 为None时通知该仙盟所有成员
-    if not familyID:
-        return
-    familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
-    
-    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
-    clientPack.FamilyID = familyID
-    clientPack.ActionType = actionType
-    clientPack.FamilyActionList = []
-    
-    for index in xrange(familyAction.Count()):
-        familyActionData = familyAction.At(index)
-        
-        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 curPlayer:
-        NetPackCommon.SendFakePack(curPlayer, clientPack)
-        return
-    Broadcast_FamilyPack(familyID, clientPack)
-    return
-
-def SendFamilyAction(actionDataList, curPlayer=None):
-    ## 同步指定仙盟action
-    # @param actionDataList: 支持列表或指定actionData
-    # @param curPlayer: 为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 curPlayer:
-        NetPackCommon.SendFakePack(curPlayer, clientPack)
-        return
-    Broadcast_FamilyPack(familyID, clientPack)
-    return
-

--
Gitblit v1.8.0