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 | 1781 +++++++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 1,180 insertions(+), 601 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 e5b7047..c2c6d64 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
@@ -23,17 +23,19 @@
 import PlayerViewCache
 import ChPyNetSendPack
 import PlayerFamilyEmblem
-#import PlayerFamilyZhenfa
 import PlayerFamilyZhenbaoge
+import PlayerFamilyTaofa
 import IPY_PlayerDefine
 import IpyGameDataPY
 import IPY_GameWorld
-import ItemControler
-import GameFuncComm
-import ItemCommon
-import DBDataMgr
+import PlayerMail
+import PlayerTask
+import CrossPlayer
 import DirtyList
-import ObjPool
+import DBDataMgr
+import DBFamily
+import CrossMsg
+import CrossMgr
 
 import random
 import time
@@ -42,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
@@ -67,55 +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)
-        
-        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)
-    #PlayerFamilyZhenfa.OnPlayerLogin(curPlayer)
-    PlayerFamilyZhenbaoge.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,31 +257,169 @@
     if not curMember:
         return
     curMember.SetOffTime(0) # 在线0,脱机1,>1离线时间
-    curMember.RefreshMember(curPlayer)
-    Sync_FamilyInfo(curPlayer)
+    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):
-    ## 盟主登录额外处理
+def RefreshFamilyMember(crossPlayer):
+    ## 玩家成员相关属性变更时同步更新家族成员信息
+    if GameWorld.IsCrossServer():
+        pass
+    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
+        return
+    familyID = crossPlayer.GetFamilyID()
+    if not familyID:
+        return
+    familyMgr = DBDataMgr.GetFamilyMgr()
+    family = familyMgr.FindFamily(familyID)
+    if not family:
+        return
+    playerID = crossPlayer.GetPlayerID()
+    member = family.FindMember(playerID)
+    if not member:
+        return
+    member.RefreshMemberByID(playerID)
+    return
+
+def FamilyPyPackForwarding(curPlayer, clientData, tick, funcName, needResult=False, reqCD=0.5, reqDataEx=None):
+    ## 玩家请求公会功能包转发处理
+    # @needResult: 转发跨服处理完毕是否需要回复状态,一般有消耗货币、物品相关的都要回复,且设置一个较长请求cd,防止重复处理
+    # @param reqCD: 转发跨服请求cd,秒,支持小数
     
+    playerID = curPlayer.GetPlayerID()
+    
+    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 OnMinute():
-    #战力刷新在DBFamily.OnMinute
-    PlayerFamilyEmblem.CheckExpireEmblem()
+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
 #
@@ -186,126 +427,138 @@
 #{
 #    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):
         #XW_JZ_Family_NameNoLegality 对不起,家族名称中含有非法字符
-        PlayerControl.NotifyCode(curPlayer, "XW_JZ_Family_NameNoLegality")
+        PlayerControl.NotifyCode(curPlayer, "NameSensitive")
         return
     
     if len(familyName) <= 0 or len(familyName) > Def_CreatFamily_MaxStr:
-        PlayerControl.NotifyCode(curPlayer, "GeRen_liubo_980181", [Def_CreatFamily_MaxStr / 2, Def_CreatFamily_MaxStr])
+        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [Def_CreatFamily_MaxStr / 3, Def_CreatFamily_MaxStr])
         return
     
+    return True
+
+def CheckFamilyNameExists(crossPlayer, familyName, fromServerID=0):
+    ## 重名在数据所在服务器验证
     familyMgr = DBDataMgr.GetFamilyMgr()
-    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, "XW_JZ_EstablishErr_Name")
-        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, "GeRen_liubo_980181", [maxLen / 2, 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
@@ -316,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=""):
@@ -371,108 +620,32 @@
             setFunc(value)
     return True
 
-def MapServer_FamilyRefresh(curPlayer, refreshFamilyID):
-    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
-    @param familyID: 玩家更新的familyID
-    '''
-    
-    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
-            
-    lastFamilyID = curPlayer.GetFamilyID()
-    lastFamilyLV = curPlayer.GetFamilyLV() # 仙盟等级,非职位等级
-    lastFmLV = PlayerControl.GetFamilyMemberLV(curPlayer)
-    
-    if lastFamilyID != refreshFamilyID:
-        curPlayer.SetFamilyID(refreshFamilyID)
-        
-    if curPlayer.GetFamilyName() != refreshFamilyName:
-        curPlayer.SetFamilyName(refreshFamilyName)
-        curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
-        
-    if lastFmLV != refreshFmLV:
-        PlayerControl.SetFamilyMemberLV(curPlayer, refreshFmLV)
-        
-    if lastFamilyLV != refreshFamilyLV:
-        isLVUP = False
-        if lastFamilyLV and refreshFamilyLV > 0:
-            #触发家族升级事件
-            isLVUP = True
-        curPlayer.SetFamilyLV(refreshFamilyLV)
-        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
-        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
-        if isLVUP:
-            pass
-        
-    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
-        #玩家离开家族
-        __OnLeaveFamily(curPlayer, tick)
-        
-    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
-        #刚进家族并为族长,触发建家族事件
-        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
-            pass
-            
-        #进入家族触发事件
-        __OnEnterFamily(curPlayer, tick)
-        
-    #---通知客户端刷新属性---
-    curPlayer.View_FamilyInfoRefresh() #//04 30 玩家家族名字职位等信息刷新#tagPlayerInFamilyInfoRefresh
-    return
-
-def __OnEnterFamily(curPlayer, tick):
-    ## 进入家族触发事件
-    familyMgr = DBDataMgr.GetFamilyMgr()
-    familyMgr.DelPlayerReqJoinFamilyIDAll(curPlayer.GetPlayerID())
-    Sync_RequestAddFamilyInfo(curPlayer)
-    return
-
-def __OnLeaveFamily(curPlayer, tick):
-    ## 退出家族触发事件
-    #---清空家族相关信息---
-    curPlayer.SetPerExp(0)
-    curPlayer.SetFamilyHornor(0)
-    curPlayer.SetFamilyActiveValue(0)
-    curPlayer.SetLastWeekFamilyActiveValue(0)
-    curPlayer.SetFamilyLV(0)
-    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
-    
-    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()
+def Sync_FamilyInfo(crossPlayer, syncMemIDList=None, isSyncMem=True):
+    ## 通知指定玩家 // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
+    familyID = crossPlayer.GetFamilyID()
     if not familyID:
         return
-    if not infoPack:
-        infoPack = GetPack_FamilyInfo(familyID)
-    NetPackCommon.SendFakePack(curPlayer, infoPack)
+    clientPack = GetPack_FamilyInfo(familyID, syncMemIDList, isSyncMem)
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
     return
 
-def GetPack_FamilyInfo(familyID):
+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:
@@ -494,84 +667,50 @@
     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.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)
+    # ---------------
+    # 测试用,同步全部,等前端同步修改后删除
+    #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 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
+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
 #
@@ -581,17 +720,18 @@
 #    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)
+    #屏蔽,默认只使用 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
@@ -604,60 +744,113 @@
 #};
 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(curPlayer)
+            AutoJoinFamily(crossPlayer)
         else:
-            RequestJoinTagFamily(curPlayer, tagFamilyID)
+            RequestJoinTagFamily(crossPlayer, tagFamilyID)
             
     # 撤销申请
     elif requestType == 1:
-        CancelJoinTagFamily(curPlayer, tagFamilyID)
+        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 AutoJoinFamily(curPlayer):
-    if curPlayer.GetFamilyID():
-        return
-    playerID = curPlayer.GetPlayerID()
-    playerLV = curPlayer.GetLV()
-    GameWorld.DebugLog("玩家一键自动加入家族! playerLV=%s" % playerLV, playerID)
+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()
-    indexList = range(familyMgr.GetCount())
+    zoneID = familyMgr.GetZoneIDInThisServer(mainServerID)
+    if zoneID < 0:
+        return
+    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
+    indexList = range(zoneMgr.GetCount())
     random.shuffle(indexList) #打乱顺序
     for index in indexList:
-        family = familyMgr.GetAt(index)
+        family = zoneMgr.GetAt(index)
         if not family:
             continue
-        familyID = family.GetID()
+        #familyID = family.GetID()
         lvMin = family.GetJoinLVMin()
-        if lvMin and playerLV < lvMin:
-            GameWorld.DebugLog("    等级不足的不处理! familyID=%s,lvMin=%s" % (familyID, lvMin), playerID)
+        if lvMin and realmLV < lvMin:
+            #GameWorld.DebugLog("    官职不足的不处理! familyID=%s,lvMin=%s" % (familyID, lvMin), playerID)
             continue
         if family.GetJoinReview():
-            GameWorld.DebugLog("    需要审核的不处理! familyID=%s" % familyID, playerID)
+            #GameWorld.DebugLog("    需要审核的不处理! familyID=%s" % familyID, playerID)
             continue
         MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
         if family.GetCount() >= MemberMax:
-            GameWorld.DebugLog("    成员已满的不处理! familyID=%s" % familyID, playerID)
+            #GameWorld.DebugLog("    成员已满的不处理! familyID=%s" % familyID, playerID)
             continue
         
         #直接加入
-        DoPlayerJionFamily(family, playerID, curPlayer)
+        DoPlayerJionFamily(family, playerID, crossPlayer)
         return
     
     # 可再扩展自动请求,暂时不处理
-    GameWorld.DebugLog("没有可自动进入的仙盟!")
+    GameWorld.DebugLog("没有可自动进入的公会!")
+    CrossPlayer.NotifyCode(crossPlayer, "QuickEnterFamilyFail")
     return
 
 def GetFamilySetting(familyLV, fieldName):
-    ## 获取仙盟等级表对应字段值
+    ## 获取公会等级表对应字段值
     if not fieldName:
         return 0
     ipyData = IpyGameDataPY.GetIpyGameData("Family", familyLV)
@@ -668,21 +861,23 @@
         return 0
     return getattr(ipyData, attrName)()
 
-def RequestJoinTagFamily(curPlayer, familyID):
+def RequestJoinTagFamily(crossPlayer, familyID):
     ## 申请加入
-    playerID = curPlayer.GetPlayerID()
-    if curPlayer.GetFamilyID():
-        GameWorld.DebugLog('已经有仙盟不能再申请加入! familyID=%s' % curPlayer.GetFamilyID(), playerID)
+    #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)
+        GameWorld.DebugLog('已经在申请加入公会列表中! familyID=%s' % familyID, playerID)
         return
     
     maxReqFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 2)
     if len(reqFamilyIDList) >= maxReqFamilyCnt:
-        GameWorld.DebugLog('已经达到最大申请加入仙盟数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
+        GameWorld.DebugLog('已经达到最大申请加入公会数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
         return
     
     tagFamily = familyMgr.FindFamily(familyID)
@@ -690,8 +885,8 @@
         return
     
     lvMin = tagFamily.GetJoinLVMin()
-    if curPlayer.GetLV() < lvMin:
-        GameWorld.DebugLog('等级未达到该仙盟加入最低等级限制! lv=%s < %s' % (curPlayer.GetLV(), lvMin), playerID)
+    if crossPlayer.GetRealmLV() < lvMin:
+        GameWorld.DebugLog('官职未达到该公会加入最低限制! realmLV=%s < %s' % (crossPlayer.GetRealmLV(), lvMin), playerID)
         return
     
     # 需要审核,满员后端不限制申请,由前端自行决定是否可申请
@@ -700,43 +895,43 @@
         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")
+                GameWorld.DebugLog('目标公会申请加入数已满! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
+                CrossPlayer.NotifyCode(crossPlayer, "jiazu_pan_141056")
                 return
             
         tagFamily.AddReqJoinPlayerID(playerID)
         # 广播给有招人权限的
-        Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
+        SendFamilyReqJoinInfo(familyID)
         
         #jiazu_pan_500807:申请入帮成功!请等待帮会管理人员审批! 
-        PlayerControl.NotifyCode(curPlayer, "jiazu_pan_500807")
-        Sync_RequestAddFamilyInfo(curPlayer)
+        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)
+        GameWorld.DebugLog('目标公会成员已满! familyLV=%s,memberMax=%s' % (tagFamily.GetLV(), memberMax), playerID)
         return
     
-    DoPlayerJionFamily(tagFamily, playerID, curPlayer)
+    DoPlayerJionFamily(tagFamily, playerID, crossPlayer)
     return
 
-def CancelJoinTagFamily(curPlayer, familyID):
+def CancelJoinTagFamily(crossPlayer, familyID):
     # 撤销申请
     familyMgr = DBDataMgr.GetFamilyMgr()
-    playerID = curPlayer.GetPlayerID()
+    playerID = crossPlayer.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)
+    SendFamilyReqJoinInfo(familyID)
+    Sync_RequestAddFamilyInfo(crossPlayer)
     return
 
-def Sync_RequestAddFamilyInfo(curPlayer, isForce=True):
+def Sync_RequestAddFamilyInfo(crossPlayer, isForce=True):
     ## 通知当前申请加入的哪些家族
-    playerID = curPlayer.GetPlayerID()
+    playerID = crossPlayer.GetPlayerID()
     familyMgr = DBDataMgr.GetFamilyMgr()
     reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
     # 非强制通知时没有申请记录的可不通知,如登录
@@ -746,7 +941,23 @@
     clientPack.Clear()
     clientPack.RequestJoinFamilyIDList = reqFamilyIDList
     clientPack.RequestCount = len(clientPack.RequestJoinFamilyIDList)
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    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):
@@ -756,17 +967,18 @@
     if not curFamily:
         return
     reqPlayerIDDict = curFamily.GetReqJoinPlayerInfo()
-    
-    playerManager = GameWorld.GetPlayerManager()
+    #没人申请也要通知
+    #if not reqPlayerIDDict:
+    #    return
+    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
     clientPack = ChPyNetSendPack.tagMCFamilyReqJoinInfo()
     clientPack.ReqJoinList = []
     for playerID, reqTime in reqPlayerIDDict.items():
-        curPlayer = playerManager.FindPlayerByID(playerID)
+        crossPlayer = crossPlayerMgr.FindCrossPlayer(playerID)
         reqInfo = ChPyNetSendPack.tagMCFamilyReqJoinPlayer()
         reqInfo.PlayerID = playerID
         reqInfo.ReqTime = reqTime
-        if curPlayer:
-            reqInfo.IsOnLine = True
+        reqInfo.IsOnLine = 1 if crossPlayer else 0
         viewCache = PlayerViewCache.FindViewCache(playerID)
         if viewCache:
             reqInfo.Name = viewCache.GetPlayerName()
@@ -776,6 +988,7 @@
             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()
@@ -795,10 +1008,16 @@
 #};
 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
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    GameWorld.DebugLog("__OnJoinFamilyReply: tagPlayerID=%s,isOK=%s" % (tagPlayerID, isOK))
+    
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -809,8 +1028,8 @@
     if not curMember:
         return
     if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
-        #GameWorld.DebugLog("没有招人权限,无法审核人员入盟!", playerID)
-        PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Popedom")
+        GameWorld.DebugLog("没有招人权限,无法审核人员入盟!", playerID)
+        #PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Popedom")
         return
     
     GameWorld.DebugLog("审核入盟申请: tagPlayerID=%s,familyID=%s,isOK=%s" % (tagPlayerID, familyID, isOK), playerID)
@@ -826,18 +1045,19 @@
         GameWorld.DebugLog("没有申请人员!")
         return
     
-    playerManager = GameWorld.GetPlayerManager()
+    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
     # 拒绝
     if not isOK:
         for tagPlayerID in tagPlayerIDList:
             family.DelReqJoinPlayerID(tagPlayerID)
-            tagPlayer = playerManager.FindPlayerByID(tagPlayerID)
-            if not tagPlayer:
+            tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
+            if not tagCrossPlayer:
                 continue
-            Sync_RequestAddFamilyInfo(tagPlayer)
+            Sync_RequestAddFamilyInfo(tagCrossPlayer)
             #jiazu_pan_592934:{%S}拒绝了您的入帮申请
-            PlayerControl.NotifyCode(tagPlayer, "jiazu_pan_592934", [family.GetName()])
-        Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
+            CrossPlayer.NotifyCode(tagCrossPlayer, "jiazu_pan_592934", [family.GetName()])
+            
+        SendFamilyReqJoinInfo(familyID)
         return
     
     # 处理同意
@@ -846,34 +1066,37 @@
     joinOKPlayerIDList = []
     for tagPlayerID in tagPlayerIDList:
         if family.GetCount() >= MemberMax:
-            PlayerControl.NotifyCode(curPlayer, "jiazu_lhs_202580")
+            CrossPlayer.NotifyCode(crossPlayer, "jiazu_lhs_202580")
             break
-        tagPlayer = playerManager.FindPlayerByID(tagPlayerID)
+        tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
         #申请目标不在线
-        if not tagPlayer:
+        if not tagCrossPlayer:
             if not offlinePlayerCanJoin:
-                GameWorld.DebugLog("离线玩家无法加入仙盟! tagPlayerID=%s" % tagPlayerID, playerID)
-                PlayerControl.NotifyCode(curPlayer, "jiazu_hwj35_367906")
+                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)
-            PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Repeat")
+            CrossPlayer.NotifyCode(crossPlayer, "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")
+            GameWorld.DebugLog("已经加入其他公会! tagPlayerID=%s,tagFamilyID=%s" % (tagPlayerID, tagFamilyID), playerID)
+            CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_InviteErr_Repeat")
             continue
         
-        DoPlayerJionFamily(family, tagPlayerID, tagPlayer, broadcastFamilyChange=False)
+        DoPlayerJionFamily(family, tagPlayerID, tagCrossPlayer, broadcastFamilyChange=False)
         joinOKPlayerIDList.append(tagPlayerID)
         
-    if not joinOKPlayerIDList:
-        return
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemJoin, excludeIDList=joinOKPlayerIDList)
-    Broadcast_FamilyPack(familyID, GetPack_FamilyReqJoinInfo(familyID), FamilyPowerID_Call)
+    #if not joinOKPlayerIDList:
+    #    return
+    SendFamilyReqJoinInfo(familyID)
+    if joinOKPlayerIDList:
+        Broadcast_FamilyInfo(familyID, changeMemIDList=joinOKPlayerIDList, excludeIDList=joinOKPlayerIDList) # 审核
     return
 
 #// A6 22 修改收人方式 #tagCMChangeFamilyJoin
@@ -886,11 +1109,16 @@
 #};
 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
+    joinLVMin = clientData.JoinLVMin # 官职
+    GameWorld.DebugLog("__OnChangeFamilyJoin: joinReview=%s,joinLVMin=%s" % (joinReview, joinLVMin))
     
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -906,8 +1134,7 @@
     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])
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改招人
     return
 
 #// A6 23 修改家族公告 #tagCMChangeFamilyBroadcast
@@ -919,10 +1146,14 @@
 #};
 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 = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -936,9 +1167,8 @@
         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])
+    GameWorld.DebugLog('更改公会公告: Family=%s,公告=%s' % (GameWorld.CodeToGbk(family.GetName()), GameWorld.CodeToGbk(broadcast)), playerID)
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改公告
     return
 
 #// A6 24 修改家族徽章 #tagCMChangeFamilyEmblem
@@ -951,9 +1181,13 @@
 #};
 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(curPlayer, changeEmblemID, emblemWord)
+    PlayerFamilyEmblem.OnChangeFamilyEmblem(crossPlayer, changeEmblemID, emblemWord)
     return
 
 #// A6 25 修改家族成员职位 #tagCMChangeFamilyMemLV
@@ -966,20 +1200,24 @@
 #};
 def OnChangeFamilyMemLV(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    OnChangeFamilyMemberLV(curPlayer, clientData.PlayerID, clientData.FmLV)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyMemLV")
     return
 
-def OnChangeFamilyMemberLV(curPlayer, tagID, changeFmlv, isGMOP=False):
+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 curPlayer:
+    if not crossPlayer:
         return
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -1014,9 +1252,10 @@
                 GameWorld.DebugLog("修改的目标成员职位不能比自己高或平级! tagFmlv=%s" % tagMember.GetFmLV(), playerID)
                 return
             
+    changeMemIDList = [tagID]
     if changeFmlv == IPY_PlayerDefine.fmlLeader:
-        changeType = FamilyChangeType_LeaderChange
         ChangeFamilyLeader(family, tagMember)
+        changeMemIDList.append(playerID)
         
     else:
         fmLVMemCnt = 0
@@ -1029,16 +1268,14 @@
         maxCnt = GetFamilySetting(family.GetLV(), Def_FmlSetAttrName.get(changeFmlv, ""))
         if fmLVMemCnt >= maxCnt:
             # jiazu_hwj35_272921 改为 jiazu_chenxin_31379
-            PlayerControl.NotifyCode(curPlayer, "jiazu_chenxin_31379")
+            CrossPlayer.NotifyCode(crossPlayer, "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])
+    Broadcast_FamilyInfo(familyID, changeMemIDList=changeMemIDList) # 修改职位
     return True
 
 def ChangeFamilyLeader(family, newLeaderMem):
@@ -1069,12 +1306,11 @@
     
     tagMember.SetFmLV(changeFamilyLV)
     
-    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagID)
-    if tagPlayer:
-        MapServer_FamilyRefresh(tagPlayer, familyID)
-        Sync_FamilyInfo(tagPlayer)
+    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagID)
+    if tagCrossPlayer:
+        MapServer_FamilyRefresh(tagCrossPlayer, familyID) # 修改职位
         if GetFamilyMemberHasPow(tagMember, FamilyPowerID_Call):
-            NetPackCommon.SendFakePack(tagPlayer, GetPack_FamilyReqJoinInfo(familyID))
+            CrossPlayer.SendFakePack(tagCrossPlayer, GetPack_FamilyReqJoinInfo(familyID))
         
     # 记录家族事件记录信息
     tick = GameWorld.GetGameWorld().GetTick()
@@ -1082,8 +1318,78 @@
                         [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) # 成员职位变更
+    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
@@ -1093,8 +1399,9 @@
 #    tagHead        Head;
 #};
 def OnGetFamilyInfo(index, clientData, tick):
-    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    Sync_FamilyInfo(curPlayer)
+    #改为后端主动同步差异,不用再请求了
+    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    #Sync_FamilyInfo(crossPlayer)
     return
 
 #// A6 03 离开家族 #tagCMLeaveFamily
@@ -1105,9 +1412,12 @@
 #};
 def OnLeaveFamily(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
-    
-    playerID = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    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()
@@ -1123,7 +1433,7 @@
         GameWorld.DebugLog("族长在成员人数大于1时不能直接退出家族", playerID)
         return
     
-    # 功能限制退出仙盟
+    # 功能限制退出公会
     # ...
     
     # 进出时间限制暂不做,等正式功能再补
@@ -1133,19 +1443,20 @@
     GameWorld.DebugLog("离开家族! familyID=%s" % familyID, playerID)
     
     family.DeleteMember(playerID)
-    AddFamilyActionNote(curPlayer.GetName(), familyID, ShareDefine.Def_ActionType_FamilyEvent, 
+    AddFamilyActionNote(crossPlayer.GetPlayerName(), 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()])
-    
-    MapServer_FamilyRefresh(curPlayer, 0)
+    __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
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLeave)
+    
     return
 
 #// A6 05 删除家族成员 #tagCMDeleteFamilyMember
@@ -1157,13 +1468,18 @@
 #};
 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 = curPlayer.GetPlayerID()
+    playerID = crossPlayer.GetPlayerID()
     if playerID == tagMemberID:
         return
     
-    familyID = curPlayer.GetFamilyID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -1200,14 +1516,22 @@
                         [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])
+    CrossPlayer.FamilyNotify(familyID, "XW_JZ_LeaveFamily", [tagPlayerName])
     
     #删除玩家
-    tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagMemberID)
-    if tagPlayer:
-        MapServer_FamilyRefresh(tagPlayer, 0)
+    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagMemberID)
+    __DoPlayerLeaveFamilyByID(family, tagPlayerID, tagCrossPlayer)
+    if tagCrossPlayer:
+        MapServer_FamilyRefresh(tagCrossPlayer, 0) # 被踢
+        CrossPlayer.NotifyCode(tagCrossPlayer, "XW_JZ_LeaveFamilyKick", [curMember.GetPlayerName()])
         
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLeave)
+    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
@@ -1222,10 +1546,21 @@
 def UpdateFamilyName(index, clientData, tick):
     curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
     newName = clientData.NewName
-    itemIndex = clientData.ItemIndex
+    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 = curPlayer.GetPlayerID()
-    familyID = curPlayer.GetFamilyID()
+    playerID = crossPlayer.GetPlayerID()
+    familyID = crossPlayer.GetFamilyID()
     if familyID <= 0:
         return
     familyMgr = DBDataMgr.GetFamilyMgr()
@@ -1240,35 +1575,94 @@
         GameWorld.DebugLog("非盟主不可改名!", playerID)
         return
     
-    familyName = CheckInputFamilyName(curPlayer, newName)
-    if not familyName:
+    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
     
-    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
-    curItem = itemPack.GetAt(itemIndex)
-    if not ItemCommon.CheckItemCanUse(curItem) or curItem.GetType() != ChConfig.Def_ItemType_ChangeFamilyName:
-        GameWorld.DebugLog("没有仙盟改名道具! itemIndex=%s" % itemIndex, playerID)
-        return
-    ItemCommon.DelItem(curPlayer, curItem, 1, True, 'UpdateFamilyName')
-    
-    #oldName = curFamily.GetName()
-    family.SetName(familyName)
-    
-    infoPack = GetPack_FamilyInfo(familyID)
-    playerManager = GameWorld.GetPlayerManager()
+    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()
-        player = playerManager.FindPlayerByID(memID)
-        if not player:
+        memCrossPlayer = crossPlayerMgr.FindCrossPlayer(memID)
+        if not memCrossPlayer:
             continue
-        Sync_FamilyInfo(player, infoPack)
-        player.SetFamilyName(familyName)
-        player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
+        MapServer_FamilyRefresh(memCrossPlayer, familyID) # 改名
+        #player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
         
-    #PlayerCompensation.SendMailByKey('FamilyNameChange', memberIDList, [], [oldName, familyName])    
-    #PlayerControl.WorldNotify(0, 'Family_ChangeName', [oldName, familyName])
+    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
 #
@@ -1282,12 +1676,25 @@
 #};
 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()
-    familyCount = familyMgr.GetCount()
+    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:
@@ -1310,11 +1717,12 @@
     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 = familyMgr.GetAt(index)
+        family = zoneMgr.GetAt(index)
         if not family:
             continue
         if msg:
@@ -1323,6 +1731,7 @@
             else:
                 continue
         familyView = ChPyNetSendPack.tagMCFamilyView()
+        familyView.Rank = index + 1
         familyView.FamilyID = family.GetID()
         familyView.FamilyName = family.GetName()
         familyView.FamilyNameLen = len(familyView.FamilyName)
@@ -1343,7 +1752,8 @@
         clientPack.FamilyCount = len(clientPack.FamilyList)
         if clientPack.FamilyCount >= showCount:
             break
-    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    clientPack.DataServerID = GameWorld.GetGameWorld().GetServerID()
+    CrossPlayer.SendFakePack(crossPlayer, clientPack)
     return
 
 
@@ -1356,18 +1766,8 @@
 #};
 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
+    donateType = clientData.DonateType
     
     ipyData = IpyGameDataPY.GetIpyGameData("FamilyDonate", donateType)
     if not ipyData:
@@ -1382,93 +1782,17 @@
     moneyValue = ipyData.GetMoneyValue()
     if not moneyType or not moneyValue:
         return
-    if not PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyMoneyDonate"):
+    if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
         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)
+    reqDataEx = {"donateCnt":donateCnt}
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyMoneyDonate", True, 20, reqDataEx=reqDataEx)
     return
 
-def ResetDailyDonateCnt(curPlayer):
-    isReset = False
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    for index in range(ipyDataMgr.GetFamilyDonateCount()):
-        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
-        donateType = ipyData.GetDonateType()
-        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
-        if donateCnt:
-            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType, 0)
-            isReset = True
-    if isReset:
-        SyncDonateCntInfo(curPlayer)
-    return
-
-def SyncDonateCntInfo(curPlayer):
-    donateCntList = []
-    ipyDataMgr = IpyGameDataPY.IPY_Data()
-    for index in range(ipyDataMgr.GetFamilyDonateCount()):
-        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
-        donateType = ipyData.GetDonateType()
-        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
-        donateCntList.append(donateCnt)
-    if not donateCntList:
-        return
-    clientPack = ObjPool.GetPoolMgr().acquire(ChPyNetSendPack.tagSCDonateCntInfo)
-    clientPack.DonateCntList = donateCntList
-    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()
+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()
@@ -1479,14 +1803,117 @@
     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)
     
-    Sync_FamilyInfo(curPlayer)
-    Broadcast_FamilyChange(familyID, FamilyChangeType_MemContrib, excludeIDList=[playerID])
+    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 成员贡献
     return
 
 ## ------------------------------------------------------------------------------------------------
@@ -1499,24 +1926,24 @@
 #    BYTE        ActionType;        // 行为类型
 #    DWORD        FamilyID;         // 家族ID,发0默认自己家族
 #};
-def OnQueryFamilyAction(index, cliendData, tick):
+def OnQueryFamilyAction(index, clientData, 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)
+    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnQueryFamilyAction")
     return
 
-def SendFamilyActionInfo(curPlayer, familyID, actionType):
+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 curPlayer: 为None时通知该仙盟所有成员
-    
+    # @param crossPlayer: 为None时通知该公会所有成员
+    if not familyID:
+        return
     familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
     
     clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
@@ -1543,16 +1970,16 @@
         
     clientPack.Count = len(clientPack.FamilyActionList)
     
-    if curPlayer:
-        NetPackCommon.SendFakePack(curPlayer, clientPack)
+    if crossPlayer:
+        CrossPlayer.SendFakePack(crossPlayer, clientPack)
         return
-    Broadcast_FamilyPack(familyID, clientPack)
+    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
     return
 
-def SendFamilyAction(actionDataList, curPlayer=None):
-    ## 同步指定仙盟action
+def SendFamilyAction(actionDataList, crossPlayer=None):
+    ## 同步指定公会action
     # @param actionDataList: 支持列表或指定actionData
-    # @param curPlayer: 为None时通知该仙盟所有成员
+    # @param crossPlayer: 为None时通知该公会所有成员
     if not isinstance(actionDataList, list):
         actionDataList = [actionDataList]
     if not actionDataList:
@@ -1581,9 +2008,161 @@
         clientPack.FamilyActionList.append(actionData)
     clientPack.Count = len(clientPack.FamilyActionList)
     
-    if curPlayer:
-        NetPackCommon.SendFakePack(curPlayer, clientPack)
+    if crossPlayer:
+        CrossPlayer.SendFakePack(crossPlayer, clientPack)
         return
-    Broadcast_FamilyPack(familyID, clientPack)
+    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()
+    refreshFamilyID = doData["FamilyID"]
+    refreshFmLV = doData.get("FmLV", 0)
+    refreshFamilyLV = doData.get("FamilyLV", 0)
+    refreshFamilyName = doData.get("FamilyName", "")
+    refreshEmblemID = doData.get("EmblemID", 0)
+    refreshEmblemWord = doData.get("EmblemWord", "")
+    
+    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, refreshFamilyID, refreshFamilyName, refreshEmblemID, refreshEmblemWord)
+    
+    lastFamilyID = curPlayer.GetFamilyID()
+    lastFamilyLV = curPlayer.GetFamilyLV() # 公会等级,非职位等级
+    lastFmLV = PlayerControl.GetFamilyMemberLV(curPlayer)
+    
+    if lastFamilyID != refreshFamilyID:
+        curPlayer.SetFamilyID(refreshFamilyID)
+        
+    if curPlayer.GetFamilyName() != refreshFamilyName:
+        curPlayer.SetFamilyName(refreshFamilyName)
+        #curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
+        
+    if lastFmLV != refreshFmLV:
+        PlayerControl.SetFamilyMemberLV(curPlayer, refreshFmLV)
+        
+    if lastFamilyLV != refreshFamilyLV:
+        isLVUP = False
+        if lastFamilyLV and refreshFamilyLV > 0:
+            #触发家族升级事件
+            isLVUP = True
+        curPlayer.SetFamilyLV(refreshFamilyLV)
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
+        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
+        if isLVUP:
+            pass
+        
+    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
+        #玩家离开家族
+        isVoluntarily = doData.get("isVoluntarily", 0)
+        __OnLeaveFamily(curPlayer, isVoluntarily, tick)
+        
+    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
+        #刚进家族并为族长,触发建家族事件
+        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
+            pass
+            
+        #进入家族触发事件
+        __OnEnterFamily(curPlayer, tick)
+        
+    #---通知客户端刷新属性---
+    curPlayer.View_FamilyInfoRefresh() #//04 30 玩家家族名字职位等信息刷新#tagPlayerInFamilyInfoRefresh
+    return
+
+def __OnEnterFamily(curPlayer, tick):
+    ## 进入家族触发事件
+    PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily)
+    return
+
+def __OnLeaveFamily(curPlayer, isVoluntarily, tick):
+    ## 退出家族触发事件
+    #---清空家族相关信息---
+    curPlayer.SetPerExp(0)
+    curPlayer.SetFamilyHornor(0)
+    curPlayer.SetFamilyActiveValue(0)
+    curPlayer.SetLastWeekFamilyActiveValue(0)
+    curPlayer.SetFamilyLV(0)
+    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
+    leaveCnt, kickedCnt, _ = PlayerControl.GetLeaveFamilyInfo(curPlayer)
+    GameWorld.DebugLog("__OnLeaveFamily: isVoluntarily=%s,leaveCnt=%s,kickedCnt=%s" % (isVoluntarily, leaveCnt, kickedCnt))
+    delMoneyType, delMoneyPer = IpyGameDataPY.GetFuncCfg("FamilyLeave", 3), 0
+    if isVoluntarily:
+        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 4)
+        if delMoneyPerList:
+            delMoneyPer = delMoneyPerList[leaveCnt] if len(delMoneyPerList) > leaveCnt else delMoneyPerList[-1]
+            
+        leaveCnt += 1
+        GameWorld.DebugLog("    增加主动离开次数: leaveCnt=%s" % (leaveCnt))
+    else:
+        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 5)
+        if delMoneyPerList:
+            delMoneyPer = delMoneyPerList[kickedCnt] if len(delMoneyPerList) > kickedCnt else delMoneyPerList[-1]
+            
+        kickedCnt += 1
+        GameWorld.DebugLog("    增加被踢离开次数: kickedCnt=%s" % (kickedCnt))
+    PlayerControl.SetLeaveFamilyInfo(curPlayer, leaveCnt, kickedCnt, isVoluntarily)
+    if delMoneyType and delMoneyPer:
+        nowMoney = PlayerControl.GetMoney(curPlayer, delMoneyType)
+        delMoney = int(nowMoney * delMoneyPer / 100.0)
+        GameWorld.DebugLog("    扣除货币: delMoneyType=%s,delMoneyPer=%s,nowMoney=%s,delMoney=%s" % (delMoneyType, delMoneyPer, nowMoney, delMoney))        
+        PlayerControl.PayMoney(curPlayer, delMoneyType, delMoney, "LeaveFamily")
+        
+    PlayerFamilyTaofa.OnPlayerLeaveFamily(curPlayer)
+    FBLogic.OnLeaveFamily(curPlayer, tick)
+    return
+
+def ResetDailyDonateCnt(curPlayer):
+    isReset = False
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetFamilyDonateCount()):
+        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
+        donateType = ipyData.GetDonateType()
+        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
+        if donateCnt:
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType, 0)
+            isReset = True
+    if isReset:
+        SyncDonateCntInfo(curPlayer)
+    return
+
+def SyncDonateCntInfo(curPlayer):
+    donateCntList = []
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in range(ipyDataMgr.GetFamilyDonateCount()):
+        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
+        donateType = ipyData.GetDonateType()
+        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
+        donateCntList.append(donateCnt)
+    if not donateCntList:
+        return
+    clientPack = ChPyNetSendPack.tagSCDonateCntInfo()
+    clientPack.DonateCntList = donateCntList
+    clientPack.Count = len(clientPack.DonateCntList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return

--
Gitblit v1.8.0