ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
@@ -23,16 +23,20 @@
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 DBDataMgr
import PlayerMail
import PlayerTask
import CrossPlayer
import PlayerTalk
import DirtyList
import ObjPool
import DBDataMgr
import DBFamily
import CrossMsg
import CrossMgr
import random
import time
@@ -41,24 +45,7 @@
Def_CreatFamily_MaxStr = 33
#仙盟变更类型
FamilyChangeTypes = (
FamilyChangeType_None,
FamilyChangeType_MemJoin, # 成员加入 1
FamilyChangeType_MemLeave, # 成员退出 2
FamilyChangeType_JoinSet, # 收人设置修改 3
FamilyChangeType_Broadcast, # 公告修改 4
FamilyChangeType_EChange, # 徽章修改 5
FamilyChangeType_LeaderChange, # 盟主变更 6
FamilyChangeType_MemFmlvChange, # 成员职位变更 7
FamilyChangeType_MemLogin, # 成员上线 8
FamilyChangeType_MemLogout, # 成员离线9
FamilyChangeType_FamilyLVExp, # 仙盟等级经验变更 10
FamilyChangeType_MemContrib, # 成员贡献变更 11
FamilyChangeType_OnDay, # 过天 12
) = range(13)
#仙盟权限
#公会权限
(
FamilyPowerID_Call,         #招人 1
FamilyPowerID_ChangeFmlv,   #变更职位 2
@@ -66,55 +53,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)
    ResetDailyDonateCnt(curPlayer)
    return
def OnPlayerLogin(curPlayer, tick):
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
    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)
    PlayerTalk.NotifyTalkCache(crossPlayer, [IPY_GameWorld.tcFamily]) # 公会聊天缓存
    PlayerFamilyTaofa.OnCrossPlayerLogin(crossPlayer)
    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()
@@ -125,24 +190,68 @@
    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, isLogin=False):
    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
    '''
    playerID = crossPlayer.GetPlayerID()
    FmLV = 0 # ְλ
    FamilyLV = 0 # 公会等级
    JoinTime = 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()
                JoinTime = member.GetJoinTime()
        else:
            familyID = 0
    crossPlayer.SetFamilyID(familyID)
    # 同步更新查看缓存
    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, familyID, FamilyName, EmblemID, EmblemWord)
    doData = {"FamilyID":familyID}
    if familyID:
        doData.update({"FmLV":FmLV, "JoinTime":JoinTime, "FamilyLV":FamilyLV, "FamilyName":FamilyName, "EmblemID":EmblemID, "EmblemWord":EmblemWord})
    if isVoluntarily:
        doData["isVoluntarily"] = 1
    if isLogin:
        doData["isLogin"] = 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, isLogin=True) # 登录
    familyID = refreshFamilyID
    if not familyID:
        return
    
@@ -153,55 +262,169 @@
    if not curMember:
        return
    curMember.SetOffTime(0) # 在线0,脱机1,>1离线时间
    curMember.RefreshMember(curPlayer)
    Sync_FamilyInfo(curPlayer)
    SendFamilyActionInfo(curPlayer, familyID, ShareDefine.Def_ActionType_FamilyData)
    curMember.RefreshMemberByID(playerID)
    Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
    # 广播成员在线
    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员登录
    
    #通知招人
    if GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
        NetPackCommon.SendFakePack(curPlayer, GetPack_FamilyReqJoinInfo(familyID))
    #XW_JZ_LeaguerOnline <n color="0,190,255">{%S1%}</n><n color="255,255,0">上线了!</n>    25  -   -
    #NotifyAllFamilyMemberMsg(familyID, "XW_JZ_LeaguerOnline", [curPlayer.GetName()], [playerID])
    Broadcast_FamilyChange(familyID, FamilyChangeType_MemLogin, excludeIDList=[playerID])
        CrossPlayer.SendFakePack(crossPlayer, GetPack_FamilyReqJoinInfo(familyID))
    # 盟主上线处理
    if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
        OnFamilyLeaderLogin(curPlayer)
    #if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
    #    OnFamilyLeaderLogin(curPlayer)
    return
def OnFamilyLeaderLogin(curPlayer):
    ## 盟主登录额外处理
    return
def OnMinute():
    #战力刷新在DBFamily.OnMinute
    PlayerFamilyEmblem.CheckExpireEmblem()
    return
def RefreshFamilyMember(curPlayer):
def RefreshFamilyMember(crossPlayer):
    ## 玩家成员相关属性变更时同步更新家族成员信息
    familyID = curPlayer.GetFamilyID()
    if GameWorld.IsCrossServer():
        pass
    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
        return
    familyID = crossPlayer.GetFamilyID()
    if not familyID:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    playerID = curPlayer.GetPlayerID()
    playerID = crossPlayer.GetPlayerID()
    member = family.FindMember(playerID)
    if not member:
        return
    member.RefreshMember(curPlayer)
    member.RefreshMemberByID(playerID)
    return
def GetRenameTime(dataAction): return dataAction.GetValue1()
def SetRenameTime(dataAction, setTime): dataAction.SetValue1(setTime)
def GetFamilyDataAction(familyID):
    ## 家族额外数据存储的行为数据,可以视为Family公共数据的一个扩展
    action = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, ShareDefine.Def_ActionType_FamilyData)
    return action.GetOneAction(True)
def FamilyPyPackForwarding(curPlayer, clientData, tick, funcName, needResult=False, reqCD=0.5, reqDataEx=None):
    ## 玩家请求公会功能包转发处理
    # @needResult: 转发跨服处理完毕是否需要回复状态,一般有消耗货币、物品相关的都要回复,且设置一个较长请求cd,防止重复处理
    # @param reqCD: 转发跨服请求cd,秒,支持小数
    playerID = curPlayer.GetPlayerID()
    crossServerID = DBDataMgr.GetFamilyMgr().GetCurCrossServerID()
    if crossServerID == -2:
        PlayerControl.NotifyCode(curPlayer, "FamilyInTransData")
        return
    if crossServerID < 0:
        GameWorld.ErrLog("公会功能异常! crossServerID=%s" % crossServerID)
        return
    # 本服处理
    if crossServerID == 0 or crossServerID == GameWorld.GetGameWorld().GetServerID():
        isOK = CallPyPackFunc(playerID, clientData, funcName, reqDataEx=reqDataEx)
        if needResult:
            __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
        return
    ssServer = CrossMgr.GetSSServerMgr().GetSSServer(crossServerID)
    if not ssServer or not ssServer.IsServerOpen():
        PlayerControl.NotifyCode(curPlayer, "ServerNoOpen")
        return
    # 转发请求CD验证
    if reqCD:
        reqTick = curPlayer.GetDictByKey(funcName) # 根据函数名单独处理CD,防止相互影响
        if reqTick and (tick - reqTick) < reqCD * 1000:
            PlayerControl.NotifyCode(curPlayer, "RequestLater")
            return
        curPlayer.SetDict(funcName, tick)
    # 剩下的就是大于0
    dataMsg = {"funcName":funcName}
    if clientData:
        dataMsg["packBuff"] = clientData.GetBuffer()
    if reqDataEx:
        dataMsg["reqDataEx"] = reqDataEx
    if needResult:
        dataMsg["needResult"] = 1
    CrossMsg.SendToCrossServer(ShareDefine.S2C_FamilyPyPack, dataMsg, [crossServerID], playerID)
    return
def S2C_FamilyPyPack(dataMsg, fromServerID, playerID):
    funcName = dataMsg["funcName"]
    packBuff = dataMsg.get("packBuff")
    reqDataEx = dataMsg.get("reqDataEx")
    clientData = None
    if packBuff:
        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
        if not clientData:
            return
    isOK = CallPyPackFunc(playerID, clientData, funcName, fromServerID, reqDataEx)
    # 处理结束回复,无论成功与否
    if "needResult" in dataMsg:
        dataMsg.pop("needResult")
        dataMsg["isOK"] = isOK
        CrossMsg.SendToClientServer(ShareDefine.C2S_FamilyPyPackRet, dataMsg, [fromServerID], playerID)
    return
def C2S_FamilyPyPackRet(dataMsg, playerID):
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    funcName = dataMsg["funcName"]
    packBuff = dataMsg.get("packBuff")
    curPlayer.SetDict(funcName, 0) # 重置CD
    clientData = None
    if packBuff:
        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
        if not clientData:
            return
    isOK = dataMsg.get("isOK")
    __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
    return
def CallPyPackFunc(playerID, clientData, funcName, fromServerID=0, reqDataEx=None):
    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(playerID)
    if not crossPlayer:
        GameWorld.ErrLog("找不到该CrossPlayer! playerID=%s" % playerID, playerID)
        return
    callFunc = GetCallFunc(funcName)
    if not callFunc:
        GameWorld.ErrLog("公会封包功能函数名不存在! %s" % funcName)
        return
    # 执行函数
    tick = GameWorld.GetGameWorld().GetTick()
    isOK = callFunc(crossPlayer, clientData, tick, fromServerID, reqDataEx)
    return isOK
def __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK):
    ## 处理后额外处理,如成就、任务等
    funcName = "%s_Ret" % funcName
    callFunc = GetCallFunc(funcName)
    if not callFunc:
        # 结果额外处理函数允许不一定需要,根据功能自行决定
        return
    callFunc(curPlayer, clientData, isOK)
    return
def GetCallFunc(funcName):
    callFunc = None
    if "." in funcName:
        # 分割字符串,最后一个部分是函数名
        parts = funcName.split('.')
        if len(parts) == 2:
            moduleName, func_name = parts
            # 导入模块
            module = __import__(moduleName)
            # 获取函数
            if hasattr(module, func_name):
                func = getattr(module, func_name)
                if callable(func):
                    callFunc = func
    else:
        gDict = globals()
        if funcName in gDict and callable(gDict[funcName]):
            # 获取函数对象
            callFunc = gDict[funcName]
    return callFunc
#// A6 04 创建家族 #tagCMCreateFamily
#
@@ -209,72 +432,103 @@
#{
#    tagHead        Head;
#    char        Name[33];
#    WORD        EmblemID; //选择徽章ID,解锁仙盟等级为1级的均为可选ID
#    WORD        EmblemID; //选择徽章ID,解锁公会等级为1级的均为可选ID
#    char        EmblemWord[3];    //徽章文字
#};
def OnCreateFamily(index, clientPack, tick):
def OnCreateFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    inputName = clientPack.Name
    emblemID = clientPack.EmblemID
    emblemWord = clientPack.EmblemWord
    inputName = clientData.Name
    
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
    GameWorld.DebugLog("OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
    # 通用的前置验证,直接游戏服处理即可
    if not CheckInputFamilyName(curPlayer, inputName):
        return
    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
    if moneyType and needMoney:
        if not PlayerControl.HaveMoney(curPlayer, moneyType, needMoney):
            return
        
    playerID = curPlayer.GetPlayerID()
    playerFamilyID = curPlayer.GetFamilyID()
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnCreateFamily", True, 20)
    return
def __OnCreateFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    inputName = clientData.Name
    emblemID = clientData.EmblemID
    emblemWord = clientData.EmblemWord
    GameWorld.DebugLog("__OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
    fullFamilyName = inputName
    playerID = crossPlayer.GetPlayerID()
    playerFamilyID = crossPlayer.GetFamilyID()
    familyMgr = DBDataMgr.GetFamilyMgr()
    if playerFamilyID:
        curFamily = familyMgr.FindFamily(playerFamilyID)
        if curFamily:
            if curFamily.FindMember(playerID):
                #玩家已经有家族, 创建失败
                PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_85890")
                CrossPlayer.NotifyCode(crossPlayer, "GeRen_chenxin_85890")
                return
            
    fullFamilyName = CheckInputFamilyName(curPlayer, inputName)
    if not fullFamilyName:
    # 验证重名
    if CheckFamilyNameExists(crossPlayer, fullFamilyName, fromServerID):
        return
    
    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
    if moneyType and needMoney:
        if not PlayerControl.PayMoney(curPlayer, moneyType, needMoney, "CreateFamily"):
            return
    serverID = GameWorld.GetPlayerServerID(curPlayer)
    serverID = crossPlayer.GetServerID()
    curFamily = familyMgr.AddFamily(fullFamilyName, serverID)
    if curFamily == None:
        GameWorld.ErrLog("创建家族失败", playerID)
        return
    newFamilyID = curFamily.GetID()
    zoneMgr = familyMgr.GetZoneFamilyMgrByFamilyID(newFamilyID)
    zoneID = zoneMgr.GetZoneID() if zoneMgr else -1
    curFamily.SetLV(1)
    emblemIDList = PlayerFamilyEmblem.GetDefaultFamilyEmblemIDList()
    if not emblemID or emblemID not in emblemIDList:
        emblemID = random.choice(emblemIDList) # 从默认徽章中随机选择一个
    GameWorld.Log("创建仙盟: familyID=%s,playerID=%s,emblemID=%s" % (newFamilyID, playerID, emblemID))
    GameWorld.Log("创建公会: familyID=%s,playerID=%s,emblemID=%s,serverID=%s,zoneID=%s" % (newFamilyID, playerID, emblemID, serverID, zoneID))
    curFamily.SetEmblemID(emblemID)
    curFamily.SetEmblemWord(emblemWord)
    
    # 扣除消耗
    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
    if moneyType and needMoney:
        CrossPlayer.CostPlayerResources(crossPlayer, "CreateFamily", costMoneyDict={moneyType:needMoney})
    #-设置家族成员属性
    DoPlayerJionFamily(curFamily, playerID, curPlayer, IPY_PlayerDefine.fmlLeader)
    familyMgr.Sort()
    DoPlayerJionFamily(curFamily, playerID, crossPlayer, IPY_PlayerDefine.fmlLeader)
    if zoneMgr:
        zoneMgr.Sort()
    #XW_JZ_EstablishSud <n color="255,255,0">恭喜您,家族建立成功!</n>    25  -   -
    PlayerControl.NotifyCode(curPlayer, "XW_JZ_EstablishSud")
    PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
    #CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_EstablishSud")
    #PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
    PlayerFamilyZhenbaoge.OnZhenbaogeReset(curFamily)
    return
    return True
def CheckInputFamilyName(curPlayer, inputName):
    '''检查玩家输入的仙盟名是否合法,建盟、改名通用
    @return: None-不合法;非空-合法的仙盟全名
    '''检查玩家输入的公会名是否合法,建盟、改名通用
    【注】该函数仅在游戏服验证名字的通用合法性,如长度、敏感词等,公会名重名请在公会所在数据服验证
    @return: None-不合法;非空-合法的公会全名
    '''
    #C++过滤空格
    familyName = GameWorld.GetGameWorld().GetCharTrim(inputName)
    fullFamilyName = GetFamilyFullName(curPlayer, familyName)
    if not fullFamilyName:
        GameWorld.ErrLog("家族全名异常!", curPlayer.GetPlayerID())
    serverID = GameWorld.GetPlayerServerID(curPlayer)
    maxServerID = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 3)
    if serverID > maxServerID or serverID <= 0:
        GameWorld.ErrLog("公会全名 serverID=%s error! maxServerID=%s, check FamilyNameFormat.txt" % (serverID, maxServerID))
        return
    maxLen = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 2)
    if len(familyName) > maxLen:
        GameWorld.ErrLog("公会全名 familyName=%s, len=%s > %s, check FamilyNameFormat.txt" % (familyName, len(familyName), maxLen))
        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [maxLen / 3, maxLen])
        return
    
    if DirtyList.IsWordForbidden(familyName):
@@ -286,49 +540,30 @@
        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [Def_CreatFamily_MaxStr / 3, Def_CreatFamily_MaxStr])
        return
    
    return True
def CheckFamilyNameExists(crossPlayer, familyName, fromServerID=0):
    ## 重名在数据所在服务器验证
    familyMgr = DBDataMgr.GetFamilyMgr()
    if familyMgr.FindFamilyByName(fullFamilyName):
    zoneID = familyMgr.GetZoneIDInThisServer(fromServerID)
    if zoneID < 0:
        GameWorld.ErrLog("验证公会重名时分区异常也视为重名")
        return True
    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
    # 不同分区暂时允许重名
    if zoneMgr.FindFamilyByName(familyName):
        #XW_JZ_EstablishErr_Name    <n color="255,255,0">对不起,您输入的家族名已存在,建立家族失败!</n> 25  -   -
        PlayerControl.NotifyCode(curPlayer, "NameExists")
        return
    return fullFamilyName
        CrossPlayer.NotifyCode(crossPlayer, "NameExists")
        return True
    return False
## 获取家族全名
def GetFamilyFullName(curPlayer, familyName):
    serverID = GameWorld.GetPlayerServerID(curPlayer)
    maxServerID = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 3)
    if serverID > maxServerID or serverID <= 0:
        GameWorld.ErrLog("仙盟全名 serverID=%s error! maxServerID=%s, check FamilyNameFormat.txt" % (serverID, maxServerID))
        return ""
    specServerDict = IpyGameDataPY.GetFuncEvalCfg("FamilyNameFormat", 4, {})
    nameFormatInfo = GameWorld.GetDictValueByRangeKey(specServerDict, serverID)
    if not nameFormatInfo:
        nameFormatInfo = IpyGameDataPY.GetFuncEvalCfg("FamilyNameFormat", 1)
    if not nameFormatInfo:
        return ""
    nameFormat = nameFormatInfo[0]
    paramList = [eval(pName) for pName in nameFormatInfo[1:]]
    fullName = nameFormat % tuple(paramList)
    maxLen = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 2)
    if len(fullName) > maxLen:
        GameWorld.ErrLog("仙盟全名 familyName=%s,全名=%s len=%s > %s, check FamilyNameFormat.txt" % (familyName, fullName, len(fullName), maxLen))
        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [maxLen / 3, maxLen])
        return ""
    return fullName
def DoPlayerJionFamily(family, playerID, jionPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
def DoPlayerJionFamily(family, playerID, crossPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
    '''加入家族,支持离线玩家加入
    @param jionPlayer: 如果是离线玩家则为None
    @param crossPlayer: 如果是离线玩家则为None
    '''
    familyMgr = DBDataMgr.GetFamilyMgr()
    if isinstance(family, int):
        familyID = family
        familyMgr = DBDataMgr.GetFamilyMgr()
        curFamily = familyMgr.FindFamily(familyID)
    else:
        curFamily = family
@@ -339,37 +574,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=""):
@@ -394,131 +625,32 @@
            setFunc(value)
    return True
def MapServer_FamilyRefresh(curPlayer, refreshFamilyID, isVoluntarily=0):
    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
    @param familyID: 玩家更新的familyID
    @param isVoluntarily: 是否自愿离开的,仅离开刷新时有效
    '''
    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, 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):
    ## 进入家族触发事件
    familyMgr = DBDataMgr.GetFamilyMgr()
    familyMgr.DelPlayerReqJoinFamilyIDAll(curPlayer.GetPlayerID())
    Sync_RequestAddFamilyInfo(curPlayer)
    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")
    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:
@@ -540,85 +672,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.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)
    # ---------------
    # 测试用,同步全部,等前端同步修改后删除
    #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
#
@@ -628,17 +725,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
@@ -651,25 +749,41 @@
#};
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 CheckInJoinCD(curPlayer):
    ## 检查是否加入仙盟CD中
    ## 检查是否加入公会CD中
    leaveFamilyTime = PlayerControl.GetLeaveFamilyTimeEx(curPlayer)
    if not leaveFamilyTime:
        return False
@@ -692,51 +806,56 @@
        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"
            GameWorld.DebugLog("加入公会CD中: leaveCnt=%s,kickedCnt=%s,lastVoluntarily=%s,leaveFamilyTime=%s(%s),passTimes=%s < %s"
                   % (leaveCnt, kickedCnt, lastVoluntarily, leaveFamilyTime, GameWorld.ChangeTimeNumToStr(leaveFamilyTime), passTimes, cdTimes))
            return True
    return False
def AutoJoinFamily(curPlayer):
    if curPlayer.GetFamilyID():
def AutoJoinFamily(crossPlayer):
    if crossPlayer.GetFamilyID():
        return
    playerID = curPlayer.GetPlayerID()
    realmLV = curPlayer.GetOfficialRank()
    playerID = crossPlayer.GetPlayerID()
    realmLV = crossPlayer.GetRealmLV()
    GameWorld.DebugLog("玩家一键自动加入家族! realmLV=%s" % realmLV, playerID)
    if CheckInJoinCD(curPlayer):
        return
    #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 realmLV < lvMin:
            GameWorld.DebugLog("    官职不足的不处理! familyID=%s,lvMin=%s" % (familyID, lvMin), playerID)
            #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("没有可自动进入的仙盟!")
    PlayerControl.NotifyCode(curPlayer, "QuickEnterFamilyFail")
    GameWorld.DebugLog("没有可自动进入的公会!")
    CrossPlayer.NotifyCode(crossPlayer, "QuickEnterFamilyFail")
    return
def GetFamilySetting(familyLV, fieldName):
    ## 获取仙盟等级表对应字段值
    ## 获取公会等级表对应字段值
    if not fieldName:
        return 0
    ipyData = IpyGameDataPY.GetIpyGameData("Family", familyLV)
@@ -747,23 +866,23 @@
        return 0
    return getattr(ipyData, attrName)()
def RequestJoinTagFamily(curPlayer, familyID):
def RequestJoinTagFamily(crossPlayer, familyID):
    ## 申请加入
    if CheckInJoinCD(curPlayer):
        return
    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)
@@ -771,8 +890,8 @@
        return
    
    lvMin = tagFamily.GetJoinLVMin()
    if curPlayer.GetOfficialRank() < lvMin:
        GameWorld.DebugLog('官职未达到该仙盟加入最低限制! realmLV=%s < %s' % (curPlayer.GetOfficialRank(), lvMin), playerID)
    if crossPlayer.GetRealmLV() < lvMin:
        GameWorld.DebugLog('官职未达到该公会加入最低限制! realmLV=%s < %s' % (crossPlayer.GetRealmLV(), lvMin), playerID)
        return
    
    # 需要审核,满员后端不限制申请,由前端自行决定是否可申请
@@ -781,43 +900,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)
    # 非强制通知时没有申请记录的可不通知,如登录
@@ -827,7 +946,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):
@@ -837,17 +972,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()
@@ -877,10 +1013,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()
@@ -891,8 +1033,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)
@@ -908,18 +1050,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
    
    # 处理同意
@@ -928,34 +1071,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
@@ -968,11 +1114,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 # 官职
    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()
@@ -988,8 +1139,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
@@ -1001,10 +1151,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()
@@ -1018,9 +1172,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
@@ -1033,9 +1186,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
@@ -1048,20 +1205,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()
@@ -1096,9 +1257,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
@@ -1111,16 +1273,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):
@@ -1151,12 +1311,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()
@@ -1164,8 +1323,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
@@ -1175,8 +1404,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
@@ -1187,9 +1417,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()
@@ -1205,7 +1438,7 @@
        GameWorld.DebugLog("族长在成员人数大于1时不能直接退出家族", playerID)
        return
    
    # 功能限制退出仙盟
    # 功能限制退出公会
    # ...
    
    # 进出时间限制暂不做,等正式功能再补
@@ -1215,19 +1448,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, 1)
    __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
@@ -1239,13 +1473,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()
@@ -1282,14 +1521,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
@@ -1304,10 +1551,21 @@
def UpdateFamilyName(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    newName = clientData.NewName
    if not CheckInputFamilyName(curPlayer, newName):
        GameWorld.DebugLog("名字验证不通过")
        return
    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
    if moneyType and moneyValue and not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
        return
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__UpdateFamilyName", True, 20)
    return
def __UpdateFamilyName(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    newName = clientData.NewName
    #itemIndex = clientData.ItemIndex
    
    playerID = curPlayer.GetPlayerID()
    familyID = curPlayer.GetFamilyID()
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
@@ -1322,42 +1580,94 @@
        GameWorld.DebugLog("非盟主不可改名!", playerID)
        return
    
    curTime = int(time.time())
    cdHours = IpyGameDataPY.GetFuncCfg("FamilyRename", 2)
    if cdHours:
        cdSeconds = cdHours * 3600
        curTime = int(time.time())
        dataAction = GetFamilyDataAction(familyID)
        lastRenameTime = GetRenameTime(dataAction)
        lastRenameTime = GetRenameTime(family)
        if lastRenameTime and (curTime - lastRenameTime) < cdSeconds:
            GameWorld.DebugLog("仙盟改名CD中! lastRenameTime=%s,cdHours=%s" % (GameWorld.ChangeTimeNumToStr(lastRenameTime), cdHours))
            GameWorld.DebugLog("公会改名CD中! lastRenameTime=%s,cdHours=%s" % (GameWorld.ChangeTimeNumToStr(lastRenameTime), cdHours))
            return
        
    familyName = CheckInputFamilyName(curPlayer, newName)
    if not familyName:
    # 验证重名
    if CheckFamilyNameExists(crossPlayer, newName, fromServerID):
        return
    
    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
    if moneyType and moneyValue and not PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyRename"):
        return
    family.SetName(familyName)
    infoPack = GetPack_FamilyInfo(familyID)
    playerManager = GameWorld.GetPlayerManager()
    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)
        MapServer_FamilyRefresh(memCrossPlayer, familyID) # 改名
        #player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
        
    if cdHours:
        SetRenameTime(dataAction, curTime)
        SendFamilyActionInfo(None, familyID, ShareDefine.Def_ActionType_FamilyData)
    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
#
@@ -1371,12 +1681,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:
@@ -1399,11 +1722,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:
@@ -1420,6 +1744,7 @@
        leaderMember = family.FindMember(familyView.LeaderID)
        familyView.LeaderName = leaderMember.GetPlayerName() if leaderMember else ""
        familyView.LeaderNameLen = len(familyView.LeaderName)
        familyView.LeaderServerID = leaderMember.GetServerID() if leaderMember else family.GetServerID()
        familyView.FamilyLV = family.GetLV()
        familyView.JoinReview = family.GetJoinReview()
        familyView.JoinLVMin = family.GetJoinLVMin()
@@ -1433,7 +1758,7 @@
        clientPack.FamilyCount = len(clientPack.FamilyList)
        if clientPack.FamilyCount >= showCount:
            break
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    CrossPlayer.SendFakePack(crossPlayer, clientPack)
    return
@@ -1446,18 +1771,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:
@@ -1472,93 +1787,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()
@@ -1569,14 +1808,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
## ------------------------------------------------------------------------------------------------
@@ -1589,24 +1931,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()
@@ -1633,16 +1975,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:
@@ -1671,9 +2013,188 @@
        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
def OnFamilyTalk(curPlayer, familyID, talkPack, tick):
    clientData, tick = None, 0
    reqDataEx = {"talkBuffer":talkPack.GetBuffer()}
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyTalk", reqCD=0, reqDataEx=reqDataEx)
    return
def __OnFamilyTalk(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    talkBuffer = reqDataEx["talkBuffer"]
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if not familyID or not talkBuffer:
        return
    clientPack = ChPyNetSendPack.tagMCTalk()
    clientPack.ReadData(talkBuffer)
    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
    # 聊天缓存
    channelType = clientPack.ChannelType
    content = clientPack.Content
    bubbleBox = clientPack.BubbleBox
    PlayerTalk.DoTalkCache(channelType, playerID, content, bubbleBox, familyID)
    return
## -------------------------------------- 游戏服本服处理 --------------------------------------------
'''
为方便本服、跨服互通公会逻辑统一,公会相关数据处理统一使用 CrossPlayer,视为以前的GameServer处理,这样本服跨服的公会管理通用
本服的curPlayer仅处理以前类似MapServer的curPlayer相关逻辑
【注】 MapServer的逻辑不能再直接获取 family 实例进行逻辑处理,只能处理 curPlayer 可用的逻辑
'''
def C2S_FamilyMapPlayer(dataMsg, playerID):
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    doType = dataMsg["doType"]
    doData = dataMsg["doData"]
    if doType == "FamilyRefresh":
        Do_MapServer_FamilyRefresh(curPlayer, doData)
    return
def Do_MapServer_FamilyRefresh(curPlayer, doData):
    tick = GameWorld.GetGameWorld().GetTick()
    playerID = curPlayer.GetPlayerID()
    refreshFamilyID = doData["FamilyID"]
    refreshFmLV = doData.get("FmLV", 0)
    refreshJoinTime = doData.get("JoinTime", 0)
    refreshFamilyLV = doData.get("FamilyLV", 0)
    refreshFamilyName = doData.get("FamilyName", "")
    refreshEmblemID = doData.get("EmblemID", 0)
    refreshEmblemWord = doData.get("EmblemWord", "")
    isLogin = doData.get("isLogin", 0) # 是否登录刷新的
    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, refreshFamilyID, refreshFamilyName, refreshEmblemID, refreshEmblemWord)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyJoinTime, refreshJoinTime)
    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
    if isLogin:
        Do_MapServer_PlayerLogin(curPlayer)
    return
def Do_MapServer_PlayerLogin(curPlayer):
    ## 地图公会玩家的登录逻辑由最新所属公会刷新后处理
    DBFamily.Sync_FamilyCrossInfo(curPlayer)
    SyncDonateCntInfo(curPlayer)
    PlayerFamilyZhenbaoge.OnPlayerLogin(curPlayer)
    PlayerFamilyTaofa.OnPlayerLogin(curPlayer)
    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