ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -29,18 +29,27 @@
import DataRecordPack
import PlayerFamilyBoss
import IpyGameDataPY
import PlayerFamilyZhenbaoge
import PlayerFamilyRedPacket
import GameWorldFamilyWar
import ChPyNetSendPack
import NetPackCommon
import PyDataManager
import PyGameData
import PlayerBillboard
import PlayerActBossTrial
import PlayerCompensation
import PlayerFamilyEmblem
import PlayerFamilyParty
import PlayerFamilySWRH
import PlayerViewCache
import GameWorldBoss
import CrossRealmMsg
import CrossFamilyGCZ
import AuctionHouse
import PlayerAssist
import PlayerTalk
import PlayerTeam
import copy
import random
@@ -54,6 +63,62 @@
ImpeachLastTime  # 弹劾需要持续的时间
) = range(3)
class FamilyMgr():
    def __init__(self):
        self.sortFamilyIDList = [] #本服仙盟排序顺序 [familyID, ...]
        self.fightPowerChangeFamilyIDList = [] # 仙盟成员战力有变更的仙盟ID [familyID, ...]
        #这里仅针对增改信息,删除的另外处理,因为删除的需要确保成功删除,所以需要入库未成功删除的记录
        #而变更同步的会定时同步,每次重连服务器也会强制同步,所以不需要有成功回复
        self.syncCrossFamilyDict = {} # 需要同步跨服的仙盟 {familyID:[需要同步的成员ID, ...], ...}
        return
    def OnDeleteFamilyID(self, familyID):
        if familyID in self.sortFamilyIDList:
            self.sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
        self.SetSyncCrossFamilyDel(familyID) # 解散仙盟
        return
    def GetFamilyIDRank(self, familyID):
        if familyID not in self.sortFamilyIDList:
            return len(self.sortFamilyIDList) + 1
        return self.sortFamilyIDList.index(familyID) + 1
    def AddFamilyIDToFightPowerChangeList(self, familyID, playerID=0):
        if familyID not in self.fightPowerChangeFamilyIDList:
            self.fightPowerChangeFamilyIDList.append(familyID)
            GameWorld.DebugLog("仙盟战力变更待处理列表: fightPowerChangeFamilyIDList=%s" % self.fightPowerChangeFamilyIDList)
        self.SetSyncCrossFamilyUpd(familyID, playerID) # 仙盟战力变更、成员战力变更
        return
    def SetSyncCrossFamilyUpd(self, familyID, playerID=0, syncNow=False):
        if familyID not in self.syncCrossFamilyDict:
            self.syncCrossFamilyDict[familyID] = []
        if playerID:
            needSyncMemIDList = self.syncCrossFamilyDict[familyID]
            if playerID not in needSyncMemIDList:
                needSyncMemIDList.append(playerID)
        # 变更数据是否立即同步跨服,否则等待定时同步即可
        if syncNow:
            Sync_ClientFamilyUpdToCrossServer()
        return
    def SetSyncCrossFamilyDel(self, familyID, playerID=0):
        ## 设置同步跨服服务器仙盟删除
        # @param playerID: 如果有值代表仅成员删除
        valueSetList = [playerID]
        gameRecMgr = PyDataManager.GetDBGameRecDataManager()
        gameRecMgr.AddGameRecData(ShareDefine.Def_GameRecType_FamilyDelSyncCross, familyID, valueSetList)
        Sync_ClientFamilyDelToCrossServer() # 删除的立马同步
        return
def GetFamilyMgr():
    mgr = PyGameData.g_familyMgr
    if not mgr:
        mgr = FamilyMgr()
        PyGameData.g_familyMgr = mgr
    return mgr
## ------------------ 仙盟 ----------------------
## 仙盟联赛排名
@@ -64,12 +129,19 @@
def SetFamilyTotalFightPower(curFamily, totalFightPower):
    curFamily.SetExtra4(totalFightPower / ChConfig.Def_PerPointValue)
    curFamily.SetExtra5(totalFightPower % ChConfig.Def_PerPointValue)
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 仙盟战力变更
    return
def GetFamilyTotalFightPowerByID(familyID):
    family = GameWorld.GetFamilyManager().FindFamily(familyID)
    if not family:
        return 0
    return GetFamilyTotalFightPower(family)
# 徽章ID
def GetFamilyEmblemID(curFamily): return curFamily.GetExtra6()
def SetFamilyEmblemID(curFamily, emblemID):
    curFamily.SetExtra6(emblemID)
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 徽章变更
    return
# 公告修改次数
def GetFamilyBroadcastCnt(curFamily): return curFamily.GetExtra3()
@@ -82,13 +154,72 @@
def GetCurWeekMissionMoney(curFamily): return curFamily.GetExtra2()
def SetCurWeekMissionMoney(curFamily, value): return curFamily.SetExtra2(min(value, ChConfig.Def_UpperLimit_DWord))
# 仙盟上次处理的合服天
def GetFamilyMixServerDay(curFamily): return curFamily.GetExtra1()
def SetFamilyMixServerDay(curFamily, value): return curFamily.SetExtra1(value)
## ------------------ 成员 ----------------------
def GetMemberFightPower(curMember): return curMember.GetExattr3()
def SetMemberFightPower(curMember, fightPower): return curMember.SetExattr3(fightPower)
def GetMemberFightPower(curMember): return curMember.GetExattr3() + curMember.GetExattr5() * ChConfig.Def_PerPointValue
def SetMemberFightPower(curMember, fightPower):
    curMember.SetExattr5(fightPower / ChConfig.Def_PerPointValue)
    curMember.SetExattr3(fightPower % ChConfig.Def_PerPointValue)
    return
def GetMemberJoinTime(curMember): return curMember.GetExattr4()
def SetMemberJoinTime(curMember, joinTime): return curMember.SetExattr4(joinTime)
#----------------------------------------------------------------------
def OnGameServerInitOK():
    ## 服务器启动成功处理
    if GameWorld.IsCrossServer():
        pass
    else:
        DoFamilySort()
    return
def OnMixServerInit():
    ## 合服后首次启动成功处理
    if GameWorld.IsCrossServer():
        return
    # 仙盟联赛重置
    GameWorldFamilyWar.DoFamilyWarReset()
    # 重置所有仙盟联赛评级
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
        SetFamilyWarRank(family, 0)
        # 仙盟榜相关榜单重新上榜
        familyID = family.GetID()
        familyBillInfo = GetFamilyBillboardInfo(family)
        familySubmitTotal = PlayerActBossTrial.GetFamilySubmitTotalByID(familyID)
        PlayerBillboard.UpdateFamilyBillboard(ShareDefine.Def_BT_BossTrialSubmitFamily, familyBillInfo, familySubmitTotal)
    DoFamilySort()
    return
def OnLoadDBPlayerOK():
    ## 服务器启动加载DB玩家成功处理
    # 检查仙盟ServerID
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
        if family.GetServerID():
            continue
        familyID = family.GetID()
        # 没有则默认取盟主的
        leaderID = family.GetLeaderID()
        leaderAccID = PlayerControl.GetDBPlayerAccIDByID(leaderID)
        if not leaderAccID:
            continue
        serverID = GameWorld.GetAccIDServerID(leaderAccID)
        family.SetServerID(serverID)
        GameWorld.Log("启动更新仙盟所属服务器ID: familyID=%s,serverID=%s,leaderID=%s,%s" % (familyID, serverID, leaderID, leaderAccID))
    return
def RandomFakeFamily():
    '''随机3个假仙盟'''
@@ -127,9 +258,25 @@
            if not lackCnt:
                break
    elif lackCnt < 0:
        GameWorld.ErrLog('    随机假仙盟异常 已存在的随机数大于还需要的随机个数lackFakeCnt=%s,fakeIDList=%s'%(lackFakeCnt, fakeIDList))
        #GameWorld.DebugLog('    随机假仙盟异常 已存在的随机数大于还需要的随机个数lackFakeCnt=%s,fakeIDList=%s'%(lackFakeCnt, fakeIDList))
        return []
    return fakeIDList
def GetFamilyNameFakeIndex(familyName):
    ## 获取仙盟名是否是系统随机出来的假仙盟名
    # @return: 0-不是, >0 对应的 fakeIndex
    fakeFamilyNameList = IpyGameDataPY.GetFuncEvalCfg('FakeFamilyName')
    randomCnt = IpyGameDataPY.GetFuncCfg('FakeFamilyName', 2)
    for i in xrange(randomCnt):
        fakeID = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_FakeFamilyIndex % i)
        if not fakeID:
            continue
        nameIndex = fakeID - 1
        if nameIndex >=0 and nameIndex < len(fakeFamilyNameList):
            fakeName = GameWorld.GbkToCode(fakeFamilyNameList[nameIndex])
            if familyName == fakeName:
                return fakeID
    return 0
def SyncFakeFamilyInfo(curPlayer=None):
    '''通知假仙盟信息'''
@@ -157,6 +304,28 @@
        NetPackCommon.SendFakePack(curPlayer, fakeFamilyPack)
    return
def SyncCreatFamilyTimes(curPlayer=None):
    # 通知建盟次数
    packData = ChPyNetSendPack.tagGCServerCreatFamilyTimes()
    packData.Clear()
    packData.Times = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerCreatFamilyTimes)
    if not curPlayer:
        # 全服广播在线玩家
        playerManager = GameWorld.GetPlayerManager()
        for i in range(0, playerManager.GetPlayerCount()):
            curPlayer = playerManager.GetPlayerByIndex(i)
            if curPlayer == None or not curPlayer.GetInitOK():
                continue
            if PlayerControl.GetIsTJG(curPlayer):
                continue
            NetPackCommon.SendFakePack(curPlayer, packData)
    else:
        if PlayerControl.GetIsTJG(curPlayer):
            return
        NetPackCommon.SendFakePack(curPlayer, packData)
    return
#输入家族名称
#class   IPY_CInputFamilyName
#{
@@ -171,7 +340,7 @@
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def DoCreateFamily(curPlayer, familyName, fakeIndex, tick):
def DoCreateFamily(curPlayer, familyName, fakeIndex, tick, emblemID=0):
    #---验证玩家属性---
    curPlayerID = curPlayer.GetPlayerID()
@@ -228,20 +397,36 @@
    if curFamily == None:
        GameWorld.ErrLog("家族创建数目已满, 创建家族失败", curPlayerID)
        return
    GameWorld.Log("创建仙盟: familyID=%s,playerID=%s" % (curFamily.GetID(), curPlayerID))
    emblemIDList = PlayerFamilyEmblem.GetDefaultFamilyEmblemIDList()
    if not emblemID or emblemID not in emblemIDList:
        emblemID = random.choice(emblemIDList) # 从默认徽章中随机选择一个
    GameWorld.Log("创建仙盟: familyID=%s,playerID=%s,emblemID=%s" % (curFamily.GetID(), curPlayerID, emblemID))
    #---创建家族---
    curFamily.SetServerID(GameWorld.GetAccIDServerID(curPlayer.GetAccID()))
    curFamily.SetCreateTime(GameWorld.GetCurrentDataTimeStr())
    curFamily.SetLV(1)
    curFamily.SetAcceptJoin(ShareDefine.FamilyAcceptJoin_Agree)     #设置收人方式为直接通过申请
    SetFamilyEmblemID(curFamily, emblemID)
    PyDataManager.GetFamilyStoreItemManager().DelFamilyStoreItemAll(curFamily.GetID())
    #新创建的仙盟默认设置已处理过合服
    SetFamilyMixServerDay(curFamily, PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_LastMixServerDay))
    
    #-设置家族成员属性
    DoPlayerJionFamily(curFamily, curPlayer, IPY_GameServer.fmlLeader)
    creatFamilyTimes = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerCreatFamilyTimes)
    # 如果是手动指定仙盟名创建的,判断是否在系统随机的假仙盟里,如果是的话就当做创建系统分配的假仙盟处理
    if familyName and not fakeIndex:
        fakeIndex = GetFamilyNameFakeIndex(familyName)
        GameWorld.DebugLog("    玩家手动输入创建仙盟名与系统随机的假仙盟名相同,默认当做创建假仙盟!fakeIndexID=%s" % (fakeIndex))
    #扣道具(前N个战盟并且假编号在随机编号里不要钱)
    if fakeIndex and fakeIndex in fakeIndexList:
        PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_FakeFamilyIndex % fakeIndexList.index(fakeIndex), 0)
        GameWorld.DebugLog('    创建前3个仙盟不扣钱! 假仙盟索引%s'%fakeIndexList.index(fakeIndex))
        GameWorld.Log('    创建前n个假仙盟不扣钱! 假仙盟索引%s'%fakeIndexList.index(fakeIndex))
        fakeAwardItemList = IpyGameDataPY.GetFuncEvalCfg("FakeFamilyName", 3)
        PlayerCompensation.SendMailByKey("FackFamilyNotice", [curPlayerID], fakeAwardItemList)
    elif creatFamilyTimes < IpyGameDataPY.GetFuncCfg('CreateFamilyNeedMoney', 3):
        GameWorld.Log('    创建前n个仙盟不扣钱! creatFamilyTimes=%s' % creatFamilyTimes)
    else:
        for i, findex in enumerate(fakeIndexList):
            if findex:
@@ -251,7 +436,9 @@
        needMoney = IpyGameDataPY.GetFuncCfg('CreateFamilyNeedMoney')
        if needMoney:
            moneyType = IpyGameDataPY.GetFuncCfg('CreateFamilyNeedMoney', 2)
            curPlayer.MapServer_PayMoney(moneyType, needMoney)
            sendMsg = str([moneyType, needMoney])
            curPlayer.MapServer_QueryPlayerResult(0, 0, "CreateFamilyPayMoney", sendMsg, len(sendMsg))
            #curPlayer.MapServer_PayMoney(moneyType, needMoney)
            #玩家创建家族费用转化为家族初始资金
            #PlayerAddFamilyMoney(curPlayer, curFamily, needMoney)
  
@@ -277,13 +464,16 @@
    
    #XW_JZ_EstablishSud <n color="255,255,0">恭喜您,家族建立成功!</n>    25  -   -
    PlayerControl.NotifyCode(curPlayer, "XW_JZ_EstablishSud")
    PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerCreatFamilyTimes, min(creatFamilyTimes + 1, ShareDefine.Def_UpperLimit_DWord))
    SyncCreatFamilyTimes()
    #帮会创建流向
    DataRecordPack.DR_CreateFamily(curPlayer.GetAccID(), curPlayerID, curPlayer.GetName(),
                                   fullFamilyName, curFamily.GetID())
                                   fullFamilyName, curFamily.GetID(), creatFamilyTimes+1)
    GameWorld.Log('创建家族 : %s(%s), fakeIndex=%s' % (fullFamilyName, curFamily.GetID(), fakeIndex), curPlayerID)
    GameWorld.Log('创建家族 : %s(%s), fakeIndex=%s, creatFamilyTimes=%s' % (fullFamilyName, curFamily.GetID(), fakeIndex, creatFamilyTimes+1), curPlayerID)
    PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, curFamily.GetID()])
    PlayerFamilyZhenbaoge.OnZhenbaogeReset(curFamily)
    return
## 获取家族全名
@@ -318,7 +508,7 @@
    #加入家族
    familyMember = curFamily.AddMember(jionPlayer)
    #刷新基本信息
    RefreshFamilyMemberBaseMsg(familyMember, jionPlayer)
    RefreshFamilyMemberBaseMsg(curFamily, familyMember, jionPlayer)
    
    #族长设置
    if jionFamilySetLv == IPY_GameServer.fmlLeader:
@@ -348,25 +538,30 @@
    
    # 玩家战盟名变更处理
    __OnFamilyNameChange(jionPlayer.GetPlayerID(), curFamily.GetName())
    #玩家缓存
    PlayerViewCache.OnPlayerFamilyChange(jionPlayer.GetPlayerID(), curFamily.GetID(), curFamily.GetName())
    PlayerTeam.OnTeamMemFamilyRefresh(jionPlayer, curFamily.GetID())
    #加入仙盟联赛成员
    GameWorldFamilyWar.AddFamilyWarMem(jionPlayer.GetPlayerID(), curFamily.GetID())
    GameWorldFamilyWar.CheckPlayerJoinFamilyWarInfo(jionPlayer)
    #通知战盟红包信息
    PlayerFamilyRedPacket.NotifyRedPacketInfo(jionPlayer)
    
    #通知战盟BOSS开启信息
    PlayerFamilyBoss.NotifyFamilyBossFBInfo(jionPlayer)
    #通知战盟BOSS
    PlayerFamilyBoss.OnPlayerJionFamily(curFamily, jionPlayer)
    #通知家族仓库
    PyDataManager.GetFamilyStoreItemManager().SyncFamilyStoreItem(jionPlayer, curFamily.GetID())
    SetMemberFightPower(familyMember, jionPlayer.GetFightPower())
    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
    #仙盟拍品
    AuctionHouse.Sync_FamilyAuctionItemInfo(jionPlayer, curFamily.GetID())
    SetMemberFightPower(familyMember, PlayerControl.GetFightPower(jionPlayer))
    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), jionPlayer.GetPlayerID())
    
    #通知仙盟盛宴题目
    PlayerFamilyParty.NotifyFamilyPartyQuestion(jionPlayer)
    #通知守卫人皇信息
    PlayerFamilySWRH.NotifySWRHInfo(jionPlayer, curFamily.GetID())
    #通知仙盟协助信息
    PlayerAssist.SyncFamilyAssist(jionPlayer)
    #oss记录加入家族信息
    DataRecordPack.DR_PlayerJoinFamily(jionPlayer, curFamily.GetID(), curFamily.GetName(), curFamily.GetCount())
    return
@@ -376,8 +571,6 @@
    GameWorld.DebugLog('    玩家战盟名变更处理, newFamilyName=%s' % familyName, playerID)
    #不处理排行榜
    needChangeFamilyBillboardList = [
                                     #ShareDefine.Def_BT_MixCampaign_Recharge,  # 累计充值(合服活动)
                                     #ShareDefine.Def_BT_RechargeTeHui,  # 充值特惠活动排行榜-当前期记录
                                     ]
    billboardMgr = GameWorld.GetBillboard()
    for billboardIndex in needChangeFamilyBillboardList:
@@ -480,6 +673,7 @@
#    tagHead        Head;
#    char        Name[33];
#    WORD        FakeID;
#    BYTE        EmblemID; //选择徽章ID,解锁仙盟等级为1级的均为可选ID
#};
## 查看申请帮会的成员信息
#  @param index 玩家索引
@@ -490,8 +684,9 @@
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    inputName = clientPack.Name
    fakeIndex = clientPack.FakeID
    emblemID = clientPack.EmblemID
    #执行创建家族逻辑
    DoCreateFamily(curPlayer, inputName, fakeIndex, tick)
    DoCreateFamily(curPlayer, inputName, fakeIndex, tick, emblemID)
    #重置查看家族状态(仅创建家族时候重置, 其余状态由MapServer退出事件时重置)
    __ClearViewFamilyState(curPlayer)
    #玩家离开事件
@@ -551,7 +746,7 @@
# @remarks 通知客户端服务器家族信息
def Sync_AllFamilyInfo(curPlayer, viewPage, pageCnt=ChConfig.Def_ViewAllFamilyPageCount, sortRule=IPY_GameServer.fsrHornor):
    #familyCount = GameWorld.GetFamilyManager().GetCount()
    familyCount = len(PyGameData.g_sortFamilyIDList)
    familyCount = len(GetFamilyMgr().sortFamilyIDList)
    allPageCnt = GameWorld.GetIntUpper(familyCount, pageCnt)
    if allPageCnt != 0 and (viewPage < 0 or viewPage >= allPageCnt):
@@ -572,7 +767,7 @@
    #===============================================================================================
    return
def SendFamilyFakePack(familyID, clientPack):
def SendFamilyFakePack(familyID, clientPack, excludePlayerIDList=[]):
    ## 广播家族成员PY封包
    family = GameWorld.GetFamilyManager().FindFamily(familyID)
    if not family:
@@ -581,12 +776,16 @@
    for index in xrange(family.GetCount()):
        member = family.GetAt(index)
        memPlayer = member.GetPlayer()
        if memPlayer:
            NetPackCommon.SendFakePack(memPlayer, clientPack)
        if not memPlayer:
            continue
        if excludePlayerIDList and memPlayer.GetPlayerID() in excludePlayerIDList:
            continue
        NetPackCommon.SendFakePack(memPlayer, clientPack)
    return
def Sync_PyAllFamilyInfo(curPlayer, allPageCnt, viewPage, startIndex, endIndex):
    familyCount = len(PyGameData.g_sortFamilyIDList)
    sortFamilyIDList = GetFamilyMgr().sortFamilyIDList
    familyCount = len(sortFamilyIDList)
    if startIndex < 0 or endIndex >= familyCount:
        return
    
@@ -597,7 +796,7 @@
    familyViewPack.CurPage = viewPage
    familyViewPack.Family = []
    for i in xrange(startIndex, endIndex + 1):
        familyID = PyGameData.g_sortFamilyIDList[i]
        familyID = sortFamilyIDList[i]
        family = familyMgr.FindFamily(familyID)
        if not family:
            continue
@@ -624,6 +823,7 @@
    totalFightPower = GetFamilyTotalFightPower(family)
    familyView.TotalFightPower = totalFightPower % ChConfig.Def_PerPointValue
    familyView.TotalFightPowerEx = totalFightPower / ChConfig.Def_PerPointValue
    familyView.EmblemID = GetFamilyEmblemID(family)
    return familyView
## 玩家模糊查询家族,0F 0D封包
@@ -654,7 +854,7 @@
    familyViewPack.TotalCount = 1
    #familyViewPack.CurPage = viewPage
    familyViewPack.Family = []
    for i, familyID in enumerate(PyGameData.g_sortFamilyIDList):
    for i, familyID in enumerate(GetFamilyMgr().sortFamilyIDList):
        family = familyMgr.FindFamily(familyID)
        if not family:
            continue
@@ -663,6 +863,60 @@
        familyViewPack.Family.append(__GetFamilyView(i, family))
    familyViewPack.PageCount = len(familyViewPack.Family)
    NetPackCommon.SendFakePack(curPlayer, familyViewPack)
    return
#// A4 12 搜索家族 #tagCGPySearchFamily
#
#struct    tagCGPySearchFamily
#{
#    tagHead        Head;
#    BYTE        MsgLen;        //模糊搜索家族,如果输入为空,则为不限制该条件
#    char        Msg[MsgLen];    //size = MsgLen
#    BYTE        LV;        //最低家族等级,如果为0,则不限制该条件
#    BYTE        MaxCount;    //搜索结果所需最大条数,后端限制最多返回20条
#    BYTE        IsSearching;    //默认1,如果有指定其他值,则返回指定值
#};
def PySearchFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    msg = clientData.Msg
    minFamilyLV = clientData.LV
    maxCount = min(20, clientData.MaxCount)
    IsSearching = clientData.IsSearching
    familyMgr = GameWorld.GetFamilyManager()
    familyViewPack = ChPyNetSendPack.tagGCPyAllFamilyView()
    familyViewPack.Clear()
    familyViewPack.IsSearching = IsSearching
    familyViewPack.TotalCount = 1
    #familyViewPack.CurPage = viewPage
    familyViewPack.Family = []
    for i, familyID in enumerate(GetFamilyMgr().sortFamilyIDList):
        family = familyMgr.FindFamily(familyID)
        if not family:
            continue
        if msg not in family.GetName():
            continue
        if minFamilyLV and family.GetLV() < minFamilyLV:
            continue
        familyViewPack.Family.append(__GetFamilyView(i, family))
        if len(familyViewPack.Family) >= maxCount:
            break
    familyViewPack.PageCount = len(familyViewPack.Family)
    NetPackCommon.SendFakePack(curPlayer, familyViewPack)
    return
#// A4 13 修改家族徽章 #tagCGChangeFamilyEmblem
#
#struct    tagCGChangeFamilyEmblem
#{
#    tagHead        Head;
#    BYTE        EmblemID;    // 更换的徽章ID
#};
def OnChangeFamilyEmblem(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    changeEmblemID = clientData.EmblemID
    PlayerFamilyEmblem.OnChangeFamilyEmblem(curPlayer, changeEmblemID)
    return
#class   IPY_CFamilyChangeBroadcast
@@ -708,7 +962,7 @@
    pack = IPY_GameServer.IPY_CFamilyChangeBroadcast()
    #更改家族公告
    curFamily.SetBroadcast(pack.GetMsg())
    GameWorld.Log('更改仙盟公告 Family=%s,公告=%s'%(GameWorld.CodeToGBK(curFamily.GetName()), GameWorld.CodeToGBK(pack.GetMsg())), curPlayerID)
    #通知客户端家族信息改变
    curFamily.Broadcast_FamilyChange()
    playerManager = GameWorld.GetPlayerManager()
@@ -723,6 +977,7 @@
            continue
        curPlayer.ChatMi(notifyPlayer, 1, pack.GetMsg(), 0, PlayerTalk.GetTalkExtraValue(curPlayer))
        PyDataManager.GetContactsManager().AddContactsBoth(curPlayer.GetID(), notifyPlayer.GetID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID()) # 公告变更
    return
## 检测目标玩家是否可以加入家族
@@ -1153,10 +1408,11 @@
    if pack_FamilyLv not in ChConfig.Def_Family_MemberLVList:
        GameWorld.Log("更改家族成员等级->封包异常->等级 = %s不存在" % (pack_FamilyLv), curPlayerID)
        return
    if curMember.GetFamilyLV() != IPY_GameServer.fmlLeader:
        if tagMember.GetFamilyLV() >= curMember.GetFamilyLV() or pack_FamilyLv >= curMember.GetFamilyLV():
            GameWorld.Log("更改家族成员等级->目标职位比自己的高或者对方的当前职位比自己高", curPlayerID)
            return
    if not isGMOP:
        if curMember.GetFamilyLV() != IPY_GameServer.fmlLeader:
            if tagMember.GetFamilyLV() >= curMember.GetFamilyLV() or pack_FamilyLv >= curMember.GetFamilyLV():
                GameWorld.Log("更改家族成员等级->目标职位比自己的高或者对方的当前职位比自己高", curPlayerID)
                return
    
    familyID = curFamily.GetID()  # 家族ID
    familyName = curFamily.GetName()  # 家族名字
@@ -1205,7 +1461,10 @@
        if tagMember.GetFamilyLV() != pack_FamilyLv: 
            #XW_JZ_AppointFamily <n color="255,255,0">恭喜</n><n color="0,190,255">{%S1%}</n><n color="255,255,0">,被任命为{%S2%}!</n>    25  -   -
            NotifyAllFamilyMemberMsg(curFamily, curPlayer, "XW_JZ_AppointFamily", [tagMember.GetName(), pack_FamilyLv])
        if tagMember.GetFamilyLV() == IPY_GameServer.fmlViceLeader and tagMember.GetPlayerID() in PyGameData.g_autoViceleaderDict.get(familyID, []):
            #自动安排的副盟主被撤职则该盟不再自动安排
            if familyID not in PyGameData.g_forbidAutoViceleaderFamily:
                PyGameData.g_forbidAutoViceleaderFamily.append(familyID)
        #更改家族等级
        ChangeFamilyMemberLv(tagMember, pack_FamilyLv)
    
@@ -1223,7 +1482,8 @@
        if GetFamilyMemberHasPow(tagMember, ChConfig.Def_PurviewDictKey_CanCall):
            tagPlayer.Sync_FamilyInfo()
            PlayerFamilyAction.ViewFamilyRequestInfo(tagPlayer)
    if isGMOP:
        curFamily.SetBroadcast('')
    curFamily.Broadcast_FamilyChange()
    return True
#---------------------------------------------------------------------
@@ -1367,7 +1627,12 @@
    if GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_FamilyRobBoss):        
        PlayerControl.NotifyCode(curPlayer, "FairyGrabBossExitError")
        return
    if PlayerFamilyBoss.IsInAllFamilyBoss():
        PlayerControl.NotifyCode(curPlayer, "LeagueBOSSExitError1")
        return
    if AuctionHouse.IsFamilyMemberBiddingAuctionItem(curFamily.GetID(), tagMemberID):
        PlayerControl.NotifyCode(curPlayer, "Paimai7")
        return
    tagPlayerName = curTagMember.GetName()  # 被踢玩家名
    tagPlayerID = curTagMember.GetPlayerID()  # 被踢玩家ID
    tagFamilyLV = curTagMember.GetFamilyLV()  # 被踢玩家职位
@@ -1378,10 +1643,10 @@
    PlayerFamilyAction.AddFamilyActionNote(tagPlayerName, curFamily.GetID(), ShareDefine.Def_ActionType_FamilyEvent,
                                           [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_KickOut], tick)
    #删除玩家
    curFamily.DeleteMember(tagPlayerID)
    __DoPlayerLeaveFamilyByID(curFamily, tagPlayerID)
    curFamily.DeleteMember(tagPlayerID)
    tagPlayer = playerManager.FindPlayerByID(tagMemberID)
    __DoPlayerLeaveFamilyByID(curFamily, tagPlayerID, tagPlayer)
    #玩家在线, 设置这个玩家的属性
    PlayerForceLeaveFamily(tagPlayer, tick)
    
@@ -1446,6 +1711,26 @@
    if GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_DailyActionState % ShareDefine.DailyActionID_FamilyRobBoss):        
        PlayerControl.NotifyCode(curPlayer, "FairyGrabBossExitError")
        return
    if PlayerFamilyBoss.IsInAllFamilyBoss():
        PlayerControl.NotifyCode(curPlayer, "LeagueBOSSExitError1")
        return
    if AuctionHouse.IsFamilyMemberBiddingAuctionItem(curFamily.GetID(), curMember.GetPlayerID()):
        PlayerControl.NotifyCode(curPlayer, "Paimai8")
        return
    #判断退出时间间隔
    curTime = int(time.time())
    lastLeaveFamilyTime = PlayerControl.GetLeaveFamilyTime(curPlayer)
    if lastLeaveFamilyTime > 100:
        remainTime = IpyGameDataPY.GetFuncCfg('ExitFairyTime', 2) - (curTime - lastLeaveFamilyTime)
        if remainTime > 0:
            PlayerControl.NotifyCode(curPlayer, "ExitFairyTime", [remainTime*1000])
            return
        updTime = 1 if IpyGameDataPY.GetFuncCfg('ExitFairyTime') > 1 else curTime
    elif lastLeaveFamilyTime >= IpyGameDataPY.GetFuncCfg('ExitFairyTime')-1:
        updTime = curTime
    else:
        updTime = lastLeaveFamilyTime+1
    PlayerControl.SetLeaveFamilyTime(curPlayer, updTime)
    
    #XW_JZ_LeaveFamily   <n color="0,190,255">{%S1%}</n><n color="255,255,0">退出了家族!</n>  25  -   -
    NotifyAllFamilyMemberMsg(curFamily, curPlayer, "XW_JZ_LeaveFamily", [curPlayer.GetName()])
@@ -1458,10 +1743,10 @@
    curFamily.DeleteMember(curMember.GetPlayerID())
    #玩家在线, 设置这个玩家的属性
    PlayerForceLeaveFamily(curPlayer, tick)  
    __DoPlayerLeaveFamilyByID(curFamily, curPlayerID)
    __DoPlayerLeaveFamilyByID(curFamily, curPlayerID, curPlayer)
    DataRecordPack.DR_PlayerLeaveFamily(curPlayer, curFamily.GetID(), curFamily.GetName(), curFamily.GetCount(),
                                        familyLV, curPlayer.GetPlayerID(), curPlayer.GetName(), familyLV)
                                        familyLV, curPlayer.GetPlayerID(), curPlayer.GetName(), familyLV, updTime)
    
    if curFamily.GetCount() == 0:
        #玩家离开后, 家族没有人了 , 删除这个家族
@@ -1476,12 +1761,17 @@
#  @param curFamily 离开的家族
#  @param leavePlayerID 离开的玩家ID
#  @return None
def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID):
def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID, tagPlayer=None):
    PlayerCompensation.SendMailByKey("LeaveFamilyNotice", [leavePlayerID], [])
    PlayerFamilyAction.DelFamilyOfficerModelEquip(curFamily.GetID(), leavePlayerID)
    # 玩家战盟名变更处理
    __OnFamilyNameChange(leavePlayerID, '')
    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
    PlayerViewCache.OnPlayerLeaveFamily(leavePlayerID)
    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), leavePlayerID)
    PlayerViewCache.OnPlayerFamilyChange(leavePlayerID, 0, "")
    PlayerAssist.OnPlayerLeaveFamily(curFamily.GetID(), leavePlayerID, tagPlayer)
    if leavePlayerID in PyGameData.g_autoViceleaderDict.get(curFamily.GetID(),[]):
        PyGameData.g_autoViceleaderDict[curFamily.GetID()].remove(leavePlayerID)
    GetFamilyMgr().SetSyncCrossFamilyDel(curFamily.GetID(), leavePlayerID) # 成员离开、踢出
    return
#//////////////////////////////////////////////////////////////
@@ -1600,8 +1890,10 @@
                                          addFamilyMoney, curFamily.GetFamilyActiveValue(), addFamilyActiveValue)
    
    #通知客户端
    #curFamily.Broadcast_FamilyChange()
    curPlayer.Sync_FamilyInfo()
    if addFamilyHornor:
        curFamily.Broadcast_FamilyChange()
    else:
        curPlayer.Sync_FamilyInfo()
    
    #金钱变更时才通知
    if addFamilyMoney != 0:
@@ -1624,7 +1916,7 @@
    
    SetMemberFightPower(curMember, fightPower)
    GameWorld.DebugLog("仙盟成员战力变更 familyID=%s,fightPower=%s" % (familyID, fightPower), playerID)
    AddFamilyIDToFightPowerChangeList(familyID)
    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(familyID, playerID)
    return
## A4 07 升级家族#tagCGFamilyLVUp
@@ -1733,6 +2025,7 @@
    #世界服务器家族重新排序
    #GameWorld.GetFamilyManager().SortByLV()
    DoFamilySort() # 升级直接强排一次
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 仙盟等级变更
    return True
#---------------------------------------------------------------------
@@ -1744,7 +2037,6 @@
def PlayerForceLeaveFamily(curPlayer, tick):
    if curPlayer == None:
        return
    #设置当天加入家族
    GameWorld.GetPlayerManager().SetForbiddenEnterFamily(curPlayer.GetPlayerID(), True)
    # 通知map玩家不能参加家族活动
@@ -1759,6 +2051,8 @@
    
    #弹劾信息封包
    SendPackClientImpeachMsg(curPlayer, 0, 0)
    PlayerTeam.OnTeamMemFamilyRefresh(curPlayer, 0)
    return
#---------------------------------------------------------------------
@@ -1780,6 +2074,8 @@
#  @remarks 函数详细说明.
def OnPlayerChangeMap(curPlayer, tick):
    #同步给玩家, 最新的家族信息(家族等级刷新)
    if GameWorld.IsCrossServer():
        return
    curPlayer.MapServer_FamilyRefresh()
    return
@@ -1791,6 +2087,7 @@
#  @remarks 函数详细说明.
def PlayerLoginRefreshFamily(curPlayer, tick):
    SyncFakeFamilyInfo(curPlayer)
    SyncCreatFamilyTimes(curPlayer)
    familyID = curPlayer.GetFamilyID()
    curFamily = None
    
@@ -1844,8 +2141,10 @@
    
    if not PlayerControl.GetIsTJG(curPlayer):
        #上线重置离线时间为0, 非脱机挂才设置
        curMember.SetExattr2(0)
        curMember.SetExattr2(0) # 在线0,脱机1,>1离线时间
        curPlayer.Sync_FamilyInfo()
    else:
        curMember.SetExattr2(1) # 脱机1
    
    curPlayer.MapServer_FamilyRefresh()
    curMember = GetPlayerFamilyMember(curPlayer)
@@ -1861,6 +2160,37 @@
    PlayerRefresh(curPlayer, tick)
    
    GameWorldFamilyWar.OnPlayerLogin(curFamily, curPlayer)
    # 盟主上线处理
    if curMember.GetFamilyLV() == IPY_GameServer.fmlLeader:
        OnFamilyLeaderLogin(curPlayer, curFamily)
    return
def OnFamilyLeaderLogin(curPlayer, curFamily):
    ## 盟主登录处理
    __DoFamilyMixServerLogicOnLeaderLogin(curPlayer, curFamily)
    return
def __DoFamilyMixServerLogicOnLeaderLogin(curPlayer, curFamily):
    ## 合服仙盟盟主登录处理
    isMixServer = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_IsMixServer)
    if not isMixServer:
        return
    lastMixServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_LastMixServerDay)
    familyID = curFamily.GetID()
    familyMixServerDay = GetFamilyMixServerDay(curFamily)
    if familyMixServerDay == lastMixServerDay:
        GameWorld.DebugLog("已经处理过仙盟盟主合服登录逻辑! lastMixServerDay=%s" % (lastMixServerDay), familyID)
        return
    SetFamilyMixServerDay(curFamily, lastMixServerDay)
    GameWorld.Log("处理合服仙盟盟主登录! lastMixServerDay=%s" % (lastMixServerDay), familyID)
    mailItemList = IpyGameDataPY.GetFuncEvalCfg("MixServerMail", 4)
    playerID = curPlayer.GetPlayerID()
    detailDict = {}
    GameWorld.Log("    发送合服盟主专属补偿邮件! familyID=%s,mailItemList=%s" % (familyID, mailItemList), playerID)
    PlayerCompensation.SendMailByKey("MixServer2", [playerID], mailItemList, detail=detailDict)
    return
#---------------------------------------------------------------------
## 玩家下线家族刷新逻辑
@@ -1895,13 +2225,16 @@
#  @param curPlayer 真实玩家
#  @return None
#  @remarks 刷新家族成员基本信息
def RefreshFamilyMemberBaseMsg(curMember, curPlayer):
def RefreshFamilyMemberBaseMsg(curFamily, curMember, curPlayer):
    curMember.SetName(curPlayer.GetName())
    curMember.SetLV(curPlayer.GetLV())
    curMember.SetReincarnationLv(curPlayer.GetReincarnationLv())
    curMember.SetJob(curPlayer.GetJob())
    curMember.SetOperateInfo(curPlayer.GetOperateInfo())
    curMember.SetOfficialRank(curPlayer.GetOfficialRank())
    curMember.SetFace(curPlayer.GetFace())
    curMember.SetFacePic(curPlayer.GetFacePic())
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), curPlayer.GetPlayerID()) # 成员基础信息刷新,含加入仙盟刷新
    return
#---------------------------------------------------------------------
## 玩家刷新
@@ -1916,9 +2249,9 @@
    if curMember == None:
        return
    
    RefreshFamilyMemberBaseMsg(curMember, curPlayer)
    #家族长境界
    family = curPlayer.GetFamily()
    RefreshFamilyMemberBaseMsg(family, curMember, curPlayer)
    #家族长境界
    if family.GetLeaderID() == curPlayer.GetID():
        family.SetLeaderOfficialRank(curPlayer.GetOfficialRank())
    return
@@ -2034,8 +2367,7 @@
    
    #重新排序家族
    #GameWorld.GetFamilyManager().SortByLV()
    if familyID in PyGameData.g_sortFamilyIDList:
        PyGameData.g_sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
    GetFamilyMgr().OnDeleteFamilyID(familyID)
        
    #家族科技删除, 改为地图直接处理, 暂屏蔽
    #PlayerFamilyTech.DelFamilyTechData(familyID)
@@ -2064,6 +2396,7 @@
    #设置族长权限
    ChangeFamilyMemberLv(familyMember, IPY_GameServer.fmlLeader)
    GameWorldFamilyWar.OnChangeFamilyLeader(curFamily.GetID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 盟主变更
    return
#---------------------------------------------------------------------
##更改家族成员等级.
@@ -2095,6 +2428,8 @@
    # 变为普通成员,删除模型装备信息
    elif changeFamilyLV == IPY_GameServer.fmlMember:
        PlayerFamilyAction.DelFamilyOfficerModelEquip(familyMember.GetFamilyID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(familyMember.GetFamilyID(), familyMember.GetPlayerID(), syncNow=True) # 成员职位变更
    return
#---------------------------------------------------------------------
@@ -2255,6 +2590,7 @@
    #上面已经验证过了,家族肯定存在的
    curFamily = curPlayer.GetFamily()
    curFamilyID = curFamily.GetID()
    curPlayerID = curPlayer.GetID()
#    curFamilyTrig = PlayerDBGSEvent.FindDBGSTrig_ByEventID(curFamilyID , PlayerDBGSEvent.Def_Key_RenameFamily)
#    
#    if not curFamilyTrig:
@@ -2272,6 +2608,7 @@
    #通知家族刷新
    curFamily.Broadcast_FamilyChange()
    
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamilyID, syncNow=True) # 仙盟改名
    playerManager = GameWorld.GetPlayerManager()
    
    #仙盟联赛
@@ -2289,20 +2626,20 @@
        curMemberID = curMember.GetPlayerID()
        __OnFamilyNameChange(curMemberID, familyName)
        memberIDList.append(curMemberID)
        curPlayer = playerManager.FindPlayerByID(curMemberID)
        player = playerManager.FindPlayerByID(curMemberID)
        #玩家不在线
        if not curPlayer:
        if not player:
            continue
        
        curPlayerMapID = GameWorld.GetQueryPlayerMapID(curPlayer)
        curPlayerMapID = GameWorld.GetQueryPlayerMapID(player)
        
        if not curPlayerMapID:
            continue
        
        msgStr = str([curPlayer.GetPlayerID(), familyName])
        msgStr = str([curPlayerID, familyName])
        playerManager.MapServer_QueryPlayer(0, 0, curMemberID, curPlayerMapID, 'FamilyNameRefresh',
                                            msgStr, len(msgStr),
                                                curPlayer.GetRouteServerIndex())
                                                player.GetRouteServerIndex())
    
    PlayerCompensation.SendMailByKey('FamilyNameChange', memberIDList, [], [oldName, familyName])    
    PlayerControl.WorldNotify(0, 'Family_ChangeName', [oldName, familyName])
@@ -2452,6 +2789,8 @@
# @return 返回值无意义
# @remarks 家族过天
def FamilyOnDay(tick):
    if GameWorld.IsCrossServer():
        return
    #---设置所有玩家可以再次加入家族---
    GameWorld.GetPlayerManager().ClearForbiddenEnterFamily()
    #---扣除地图上所有家族的维护费---
@@ -2464,13 +2803,13 @@
            
        familyMoney = family.GetMoney()
        
        useMoney = GetFamilySetting(family, ChConfig.Def_FamilySetting_SystemLostMoney)
        #家族资金不足, 解散
        if familyMoney < useMoney:
            family.SetMoney(0)
            delFamilyList.append(family)
            continue
#        useMoney = GetFamilySetting(family, ChConfig.Def_FamilySetting_SystemLostMoney)
#
#        #家族资金不足, 解散
#        if familyMoney < useMoney:
#            family.SetMoney(0)
#            delFamilyList.append(family)
#            continue
        #多久没人上线,解散
        offlineDay = GetLastOnlineMemberOfflineTime(family)
        GameWorld.DebugLog('    仙盟%s %s天没人上线了'%(family.GetID(), offlineDay))
@@ -2486,12 +2825,10 @@
                                                   ChConfig.Def_Family_ClearRequestAddNote_Day)
        
        #此处不通知地图服务器家族资金变更
        family.SetMoney(familyMoney - useMoney)
        if useMoney > 0:
            #帮会日常维持消耗{%S1%}银两帮会资金
            PlayerControl.FamilyNotify(family.GetID(), 'jiazu_lhs_272921', [useMoney])
        #自动传位
        __AutoChangeLeader(family)
#        family.SetMoney(familyMoney - useMoney)
#        if useMoney > 0:
#            #帮会日常维持消耗{%S1%}银两帮会资金
#            PlayerControl.FamilyNotify(family.GetID(), 'jiazu_lhs_272921', [useMoney])
        
        #通知客户端刷新
        family.Broadcast_FamilyChange()
@@ -2511,6 +2848,17 @@
    __SetFamilyActivityDayStateValue(0)
    return
def FamilyOnDayEx(tick):
    if GameWorld.IsCrossServer():
        return
    familyManager = GameWorld.GetFamilyManager()
    for i in range(0, familyManager.GetCount()):
        family = familyManager.GetAt(i)
        #仙盟boss
        PlayerFamilyBoss.FamilyBossFBOnDayEx(family)
        #珍宝阁
        PlayerFamilyZhenbaoge.OnDayEx(family)
    return
#---------------------------------------------------------------------
##家族过周
@@ -2518,7 +2866,8 @@
# @return 返回值无意义
# @remarks 家族过周
def FamilyOnWeek(tick):
    if GameWorld.IsCrossServer():
        return
    #---计算上周家族活跃度---
    familyManager = GameWorld.GetFamilyManager()
    for i in range(0, familyManager.GetCount()):
@@ -2532,6 +2881,12 @@
        SetFamilyBroadcastCnt(family, 0)
        #清除本周任务已获得资金数量
        SetCurWeekMissionMoney(family, 0)
        for j in xrange(family.GetCount()):
            member = family.GetAt(j)
            #原先是地图玩家上线后重置,导致玩家不上线周贡献显示之前的,固在此重置
            member.SetFamilyActiveValue(0)
        #通知地图服务器刷新家族属性
        SendPack_MapServer_PlayerFamilyRefresh(family)
        #oss记录上周家族信息
@@ -2539,16 +2894,107 @@
        DataRecordPack.DR_FamilyActiveValueByOnWeek(familyID, family.GetName(), familyActiveValue)
        
        #清除家族boss副本信息
        PlayerFamilyBoss.FamilyBossFBOnWeek(familyID)
        PlayerFamilyBoss.FamilyBossFBOnWeek(family)
        
    return
def FamilyOnHour():
    if GameWorld.IsCrossServer():
        return
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
        #自动传位
        __AutoChangeLeader(family)
        __AutoChangeFamilyJobLV(family)
    return
def __AutoChangeFamilyJobLV(family):
    ##开服2天内新创建的仙盟,系统自动为仙盟安排两位副盟主
    familyID = family.GetID()
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay)
    if openServerDay >= IpyGameDataPY.GetFuncCfg('FairyFMZ'):
        #GameWorld.DebugLog('自动为仙盟安排两位副盟主 开服天超过 familyID=%s'%familyID)
        return
    if familyID in PyGameData.g_forbidAutoViceleaderFamily:
        #GameWorld.DebugLog('自动为仙盟安排两位副盟主 被禁止 familyID=%s'%familyID)
        return
    createTime = family.GetCreateTime()
    pastHour = GameWorld.GetPastHour(createTime)
    if pastHour < IpyGameDataPY.GetFuncCfg('FairyFMZ', 2):
        #GameWorld.DebugLog('自动为仙盟安排两位副盟主 建盟未超过X小时 familyID=%s,pastHour=%s'%(familyID, pastHour))
        return
    if (pastHour - IpyGameDataPY.GetFuncCfg('FairyFMZ', 2)) % IpyGameDataPY.GetFuncCfg('FairyFMZ', 3) != 0:
        GameWorld.DebugLog('自动为仙盟安排两位副盟主 间隔没到 familyID=%s,pastHour=%s'%(familyID, pastHour))
        return
    tofmlv = IPY_GameServer.fmlViceLeader
    viceLeaderCnt = 0 #副盟主数量
    changeMemberList = []
    for i in xrange(family.GetCount()):
        familyMember = family.GetAt(i)
        if familyMember.GetFamilyLV() == tofmlv:
            viceLeaderCnt += 1
            continue
        if familyMember.GetFamilyLV() < tofmlv:
            changeMemberList.append(familyMember)
    maxCnt = GetFamilySetting(family, ChConfig.Def_FamilyPowLvChangeFamilySettingDict[tofmlv])
    #GameWorld.DebugLog('自动为仙盟安排两位副盟主 familyID=%s,viceLeaderCnt=%s,changeMemberList=%s'%(familyID, viceLeaderCnt, changeMemberList))
    if viceLeaderCnt >= maxCnt or not changeMemberList:
        return
    if familyID not in PyGameData.g_autoViceleaderDict:
        PyGameData.g_autoViceleaderDict[familyID] = []
    changeMemberList.sort(cmp=CmpAutoMemberSort)
    for i, member in enumerate(changeMemberList):
        if viceLeaderCnt + i >= maxCnt:
            break
        ChangeFamilyMemberLv(member, tofmlv)
        playerID = member.GetPlayerID()
        if playerID not in PyGameData.g_autoViceleaderDict[familyID]:
            PyGameData.g_autoViceleaderDict[familyID].append(playerID)
        GameWorld.Log('自动为仙盟安排副盟主 familyID=%s,playerID=%s'%(familyID, playerID))
    return
def CmpAutoMemberSort(member1, member2):
    ## 排序规则: 等级>战力>入盟先后
    ret = -cmp(member1.GetLV(), member2.GetLV())
    if ret == 0:
        ret = -cmp(GetMemberFightPower(member1), GetMemberFightPower(member2))
        if ret == 0:
            return cmp(GetMemberJoinTime(member1), GetMemberJoinTime(member2))
    return ret
def SortCrossFamily(serverIDList, top=0):
    ''' 跨服仙盟排序, 排序规则: 总战力  > 等级 > ID
    @param serverIDList: 仙盟所属区服ID范围列表
    @param top: 返回排序靠前x个仙盟,0则全部返回
    '''
    familyList = []
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
        serverID = family.GetServerID()
        if not GameWorld.CheckServerIDInList(serverID, serverIDList):
            continue
        familyList.append(family)
    familyList.sort(key=lambda f: (GetFamilyTotalFightPower(f), f.GetLV(), f.GetID()), reverse=True)
    totalCnt = len(familyList)
    return familyList[:top] if top else familyList, totalCnt
#---------------------------------------------------------------------
##通知地图服务器, 玩家家族属性刷新
# @param curFamily 家族实例
# @return 返回值无意义
# @remarks IPY_MFamilyRefresh
def SendPack_MapServer_PlayerFamilyRefresh(curFamily):
    if GameWorld.IsCrossServer():
        return
#===============================================================================
#    当家族以下权限变更时要通知地图服务器 IPY_MFamilyRefresh
#    GetFamilyLV
@@ -2587,7 +3033,7 @@
        GameWorld.ErrLog("key = %s not in tagFamily.txt" % familyLv)
        return 0
    keyStr = ChConfig.FamilySettingDict[index]
    return getattr(curFamilyLvSetting, keyStr)
    return getattr(curFamilyLvSetting, "Get%s" % keyStr)()
#===============================================================================
@@ -2661,16 +3107,15 @@
        return 0
    leaderID = curFamily.GetLeaderID()
    # 帮主在线
    if GameWorld.GetPlayerManager().FindPlayerByID(leaderID) != None:
        return 0
    curMember = curFamily.FindMember(leaderID)    
    if curMember == None:
        GameWorld.Log("GetLeaderOfflineTime->FindMember, None;%s" % leaderID)
        return 0
    offLineTime = GameWorld.ChangeTimeNumToStr(curMember.GetExattr2())
    offLineTimeNum = curMember.GetExattr2()
    if not offLineTimeNum or offLineTimeNum == 1:
        return 0
    offLineTime = GameWorld.ChangeTimeNumToStr(offLineTimeNum)
    return GameWorld.GetPastHour(offLineTime)
def GetLastOnlineMemberOfflineTime(family):
@@ -2678,17 +3123,14 @@
    offLineTime = 0
    for i in range(0, family.GetCount()):
        member = family.GetAt(i) 
        playerID = member.GetPlayerID()
        tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if tagPlayer:
            return 0
        if not member.GetExattr2():
        offLineTimeNum = member.GetExattr2()
        if not offLineTimeNum or offLineTimeNum == 1:
            #有人在线直接返回
            return 0
        if not offLineTime:
            offLineTime = member.GetExattr2()
            offLineTime = offLineTimeNum
        else:
            offLineTime = max(offLineTime, member.GetExattr2())
            offLineTime = max(offLineTime, offLineTimeNum)
    if not offLineTime:
        return 0
    offLineTime = GameWorld.ChangeTimeNumToStr(offLineTime)
@@ -2698,7 +3140,7 @@
    '''自动传位'''
    # 获得帮主下线了多久(小时)
    leaderOffLineTime = GetLeaderOfflineTime(curFamily)
    GameWorld.DebugLog('帮主下线了%s小时'%leaderOffLineTime)
    GameWorld.DebugLog('帮主下线了%s小时'%leaderOffLineTime, curFamily.GetID())
    if leaderOffLineTime < IpyGameDataPY.GetFuncCfg('AutoChangeLeader'):
        return
    
@@ -2717,7 +3159,7 @@
        elif toMember2.GetExattr1() < member.GetExattr1():
            toMember2 = member
        offLineHour = GameWorld.GetPastHour(GameWorld.ChangeTimeNumToStr(member.GetExattr2())) if member.GetExattr2() else 0
        offLineHour = GameWorld.GetPastHour(GameWorld.ChangeTimeNumToStr(member.GetExattr2())) if member.GetExattr2() > 1 else 0
        #GameWorld.DebugLog('memberID=%s 离线%s小时,历史贡献度%s'%(member.GetPlayerID(), offLineHour, member.GetExattr1()))
        if offLineHour < 48:
            if not toMember1:
@@ -3085,17 +3527,6 @@
    PlayerFamilyAction.ViewFamilyRequestInfo(curPlayer)
    return
## 开启家族boss副本
#  @param index 玩家索引
#  @param clientData 封包数据结构体
#  @param tick 时间戳
#  @return None
def OpenFamilyBossFB(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    fbMapID = clientData.MapID
    PlayerFamilyBoss.OpenFamilyBossFB(curPlayer, tick)
    return
#===============================================================================
# //A4 06 变更家族成员加入审核方式#tagCGChangeFamilyAcceptJoinType
#struct tagCGChangeFamilyAcceptJoinType
@@ -3207,21 +3638,30 @@
            break
    return leaderLV
def AddFamilyIDToFightPowerChangeList(familyID):
    if familyID not in PyGameData.g_fightPowerChangeFamilyIDList:
        PyGameData.g_fightPowerChangeFamilyIDList.append(familyID)
        GameWorld.DebugLog("仙盟战力变更待处理列表: fightPowerChangeFamilyIDList=%s" % PyGameData.g_fightPowerChangeFamilyIDList)
def OnMinuteProcess(curMinute):
    if GameWorld.IsCrossServer():
        return
    #每5分钟触发一次仙盟更新
    if curMinute % 5 != 0:
        return
    UpdFamilyTotalFightPower()
    PlayerFamilyRedPacket.CheckDelRedpacketData()
    PlayerFamilyEmblem.CheckExpireEmblem()
    Sync_ClientFamilyUpdToCrossServer()
    return
def UpdFamilyTotalFightPower():
    ## 更新仙盟总战力
    if not PyGameData.g_fightPowerChangeFamilyIDList:
    mgr = GetFamilyMgr()
    if not mgr.fightPowerChangeFamilyIDList:
        #GameWorld.DebugLog("不需要更新仙盟总战力!")
        return
        
    GameWorld.DebugLog("更新仙盟总战力 fightPowerChangeFamilyIDList=%s" % PyGameData.g_fightPowerChangeFamilyIDList)
    GameWorld.DebugLog("更新仙盟总战力 fightPowerChangeFamilyIDList=%s" % mgr.fightPowerChangeFamilyIDList)
    familyManager = GameWorld.GetFamilyManager()
    for familyID in PyGameData.g_fightPowerChangeFamilyIDList:
    for familyID in mgr.fightPowerChangeFamilyIDList:
        family = familyManager.FindFamily(familyID)
        if not family:
            continue
@@ -3233,19 +3673,17 @@
        SetFamilyTotalFightPower(family, totalFightPower)
        GameWorld.DebugLog("    familyID=%s,totalFightPower=%s" % (familyID, totalFightPower))
        
    PyGameData.g_fightPowerChangeFamilyIDList = []
    mgr.fightPowerChangeFamilyIDList = []
    
    DoFamilySort(False) # 此处必须为False
    return True
def GetSortFamilyIDList(): return PyGameData.g_sortFamilyIDList
def GetSortFamilyIDList(): return GetFamilyMgr().sortFamilyIDList
def GetFamilyIDRank(familyID):
    '''获取仙盟的排名, 注意与联赛排名区分
    每个仙盟一定有排名,但是不一定有联赛排名,联赛排名只是决定仙盟最终排名的一个比较因素
    '''
    if familyID not in PyGameData.g_sortFamilyIDList:
        return len(PyGameData.g_sortFamilyIDList) + 1
    return PyGameData.g_sortFamilyIDList.index(familyID) + 1
    return GetFamilyMgr().GetFamilyIDRank(familyID)
def DoFamilySort(isUpdTotalFightPower=True):
    ''' 仙盟排序, 排序规则: 联赛评级 > 总战力 > 等级 > 创建时间
@@ -3263,12 +3701,13 @@
        familyList.append(family)
    familyList.sort(cmp=CmpFamilySort)
    
    PyGameData.g_sortFamilyIDList = []
    mgr = GetFamilyMgr()
    mgr.sortFamilyIDList = []
    for i, family in enumerate(familyList, 1):
        GameWorld.DebugLog("    i=%s,warRank=%s,fightPower=%s,LV=%s,CreateTime=%s,familyID=%s" 
                           % (i, GetFamilyWarRank(family), GetFamilyTotalFightPower(family), family.GetLV(), family.GetCreateTime(), family.GetID()))
        PyGameData.g_sortFamilyIDList.append(family.GetID())
    GameWorld.DebugLog("    sortFamilyIDList=%s" % PyGameData.g_sortFamilyIDList)
        mgr.sortFamilyIDList.append(family.GetID())
    GameWorld.DebugLog("    sortFamilyIDList=%s" % mgr.sortFamilyIDList)
    return
def CmpFamilySort(family1, family2):
@@ -3302,32 +3741,6 @@
    
    return 0
def UpdFamilyWarRank():
    '''更新仙盟联赛排名
    注意:该逻辑在仙盟联赛周期中不可执行,仅在下周分组定级状态下可更新
    仙盟联赛新一周期开始时、仙盟解散时 会触发该逻辑
    '''
    gameWorld = GameWorld.GetGameWorld()
    state = gameWorld.GetDictByKey(ShareDefine.Def_Notify_WorldKey_FBFuncState % ChConfig.Def_FBMapID_FamilyWar)
    if state != GameWorldFamilyWar.FamilyWarState_NextWeekGroupRank:
        GameWorld.DebugLog("非下周分组定级阶段不可更新仙盟联赛排名!")
        return
    familyMgr = GameWorld.GetFamilyManager()
    sortFamilyIDList = GetSortFamilyIDList()
    for rank, familyID in enumerate(sortFamilyIDList, 1):
        family = familyMgr.FindFamily(familyID)
        if not family:
            continue
        curRank = GetFamilyWarRank(family)
        if not curRank:
            break
        if curRank != rank:
            SetFamilyWarRank(family, rank)
            GameWorld.Log("更新仙盟联赛仙盟排名: familyID=%s,rank=%s" % (family.GetID(), rank))
    return
#// A4 11 一键申请入盟 #tagCGOneKeyJoinFamily
#
#struct    tagCGOneKeyJoinFamily
@@ -3343,7 +3756,8 @@
    requestPlayerName = curPlayer.GetName()
    playerLV = curPlayer.GetLV()
    #玩家Id, 等级,职业,战斗力
    actionDataList = [curPlayer.GetID(), playerLV, curPlayer.GetJob(), curPlayer.GetFightPower()]
    fightPower = PlayerControl.GetFightPower(curPlayer)
    actionDataList = [curPlayer.GetID(), playerLV, curPlayer.GetJob(), fightPower % ChConfig.Def_PerPointValue, fightPower / ChConfig.Def_PerPointValue]
    allFamilyActionManager = GameWorld.GetFamilyActionManager()
    familyManager = GameWorld.GetFamilyManager()
    indexList = range(familyManager.GetCount())
@@ -3398,3 +3812,384 @@
        
    PlayerControl.NotifyCode(curPlayer, "jiazu_pan_500807")
    return
##--------------------------------------- 仙盟传功 --------------------------------------------------
def MapServer_FamilyChuangong(curPlayer, msgList):
    msgType, msgData = msgList
    if msgType == "Invite":
        tagPlayerID = msgData[0]
        __DoChuangong_Invite(curPlayer, tagPlayerID)
        return
    if msgType == "Response":
        tagPlayerID, isOK = msgData
        __DoChuangong_Response(curPlayer, tagPlayerID, isOK)
        return
    if msgType == "ThanksGift":
        __DoChuangong_ThanksGift(curPlayer, msgData)
    return
def __CheckChuangongPlayer(curPlayer, tagPlayerID):
    curFamily = curPlayer.GetFamily()
    if not curFamily:
        return
    tagMember = curFamily.FindMember(tagPlayerID)
    if not tagMember:
        GameWorld.DebugLog("非盟友无法传功! tagPlayerID=%s" % tagPlayerID, curPlayer.GetPlayerID())
        return
    tagPlayer = tagMember.GetPlayer()
    if not tagPlayer:
        PlayerControl.NotifyCode(curPlayer, "FairyFeastPlayerOffline")
        return
    return tagPlayer
def __DoChuangong_Invite(curPlayer, tagPlayerID):
    ## 邀请
    playerID = curPlayer.GetPlayerID()
    curFamily = curPlayer.GetFamily()
    if not curFamily:
        return
    tagMember = curFamily.FindMember(tagPlayerID)
    if not tagMember:
        GameWorld.DebugLog("非盟友无法传功! tagPlayerID=%s" % tagPlayerID, curPlayer.GetPlayerID())
        return
    tagPlayer = tagMember.GetPlayer()
    if not tagPlayer:
        clientPack = ChPyNetSendPack.tagGCChuangongStart()
        clientPack.Clear()
        clientPack.PlayerID = tagMember.GetPlayerID()
        clientPack.Name = tagMember.GetName()
        clientPack.NameLen = len(clientPack.Name)
        clientPack.LV = tagMember.GetLV()
        clientPack.Job = tagMember.GetJob()
        clientPack.RealmLV = tagMember.GetOfficialRank()
        NetPackCommon.SendFakePack(curPlayer, clientPack)
        PyGameData.g_chuangongTagPlayerDict[playerID] = tagPlayerID
        GameWorld.DebugLog("对方离线,则自己直接开始传功: tagPlayerID=%s, %s" % (tagPlayerID, PyGameData.g_chuangongPlayerDict), playerID)
        return
    invitePlayerIDList = PyGameData.g_chuangongPlayerDict.get(playerID, [])
    if tagPlayerID not in invitePlayerIDList:
        invitePlayerIDList.append(tagPlayerID)
        PyGameData.g_chuangongPlayerDict[playerID] = invitePlayerIDList
    clientPack = ChPyNetSendPack.tagGCChuangongInviteInfo()
    clientPack.Clear()
    clientPack.PlayerID = curPlayer.GetPlayerID()
    clientPack.Name = curPlayer.GetName()
    clientPack.NameLen = len(clientPack.Name)
    clientPack.LV = curPlayer.GetLV()
    clientPack.Job = curPlayer.GetJob()
    clientPack.RealmLV = curPlayer.GetOfficialRank()
    clientPack.Face = curPlayer.GetFace()
    clientPack.FacePic = curPlayer.GetFacePic()
    NetPackCommon.SendFakePack(tagPlayer, clientPack)
    GameWorld.DebugLog("邀请传功: tagPlayerID=%s, %s" % (tagPlayerID, PyGameData.g_chuangongPlayerDict), playerID)
    return
def __DoChuangong_Response(curPlayer, tagPlayerID, isOK):
    ## 相应
    playerID = curPlayer.GetPlayerID()
    tagPlayer = __CheckChuangongPlayer(curPlayer, tagPlayerID)
    if not tagPlayer:
        return
    invitePlayerIDList = PyGameData.g_chuangongPlayerDict.get(tagPlayerID, [])
    if not isOK:
        if playerID in invitePlayerIDList:
            invitePlayerIDList.remove(playerID)
            PyGameData.g_chuangongPlayerDict[tagPlayerID] = invitePlayerIDList
        GameWorld.DebugLog("拒绝传功: tagPlayerID=%s, %s" % (tagPlayerID, PyGameData.g_chuangongPlayerDict), playerID)
        return
    if not invitePlayerIDList:
        PlayerControl.NotifyCode(curPlayer, "TagHadFinishChuangong")
        return
    if playerID not in invitePlayerIDList:
        GameWorld.DebugLog("不在对方邀请列表了,无法传功: tagPlayerID=%s, %s" % (tagPlayerID, invitePlayerIDList), playerID)
        return
    PyGameData.g_chuangongPlayerDict.pop(tagPlayerID)
    # 通知双方开始传功
    __NotifyChuangongStart(curPlayer, tagPlayer)
    __NotifyChuangongStart(tagPlayer, curPlayer)
    return
def __NotifyChuangongStart(curPlayer, tagPlayer):
    clientPack = ChPyNetSendPack.tagGCChuangongStart()
    clientPack.Clear()
    clientPack.PlayerID = tagPlayer.GetPlayerID()
    clientPack.Name = tagPlayer.GetName()
    clientPack.NameLen = len(clientPack.Name)
    clientPack.LV = tagPlayer.GetLV()
    clientPack.Job = tagPlayer.GetJob()
    clientPack.RealmLV = tagPlayer.GetOfficialRank()
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    PyGameData.g_chuangongTagPlayerDict[curPlayer.GetPlayerID()] = tagPlayer.GetPlayerID()
    return
def __DoChuangong_ThanksGift(curPlayer, msgData):
    itemID = msgData[0]
    playerID = curPlayer.GetPlayerID()
    tagPlayerID = PyGameData.g_chuangongTagPlayerDict.pop(playerID, 0)
    GameWorld.DebugLog("__DoChuangong_ThanksGift tagPlayerID=%s" % tagPlayerID, playerID)
    if not tagPlayerID:
        return
    curFamily = curPlayer.GetFamily()
    if not curFamily:
        return
    tagMember = curFamily.FindMember(tagPlayerID)
    if not tagMember:
        return
    assistPlayerDict = {tagPlayerID:{"PlayerName":tagMember.GetName(), "Job":tagMember.GetJob(),
                                     "LV":tagMember.GetLV(), "RealmLV":tagMember.GetOfficialRank(),
                                     "Face":tagMember.GetFace(), "FacePic":tagMember.GetFacePic()}}
    GameWorld.DebugLog("    assistPlayerDict %s" % assistPlayerDict, playerID)
    PlayerAssist.AddNewAssistThanksEx(curPlayer, itemID, assistPlayerDict)
    return
##--------------------------------------------------------------------------------------------------
def CrossServerMsg_FamilyDelRet(msgData):
    ## 跨服仙盟删除结果,有收到结果即代表成功
    familyID = msgData["familyID"]
    playerID = msgData.get("playerID", 0)
    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
    if playerID:
        gameRecMgr.DelGameRecDataByTypeValue(ShareDefine.Def_GameRecType_FamilyDelSyncCross, [playerID], familyID)
    else:
        gameRecMgr.DelGameRecDataByTypeID(ShareDefine.Def_GameRecType_FamilyDelSyncCross, familyID)
    return
def GetFamilyBillboardInfo(curFamily):
    ## 获取仙盟榜单信息 区服ID、徽章、仙盟名、盟主名、仙盟总战力、仙盟等级
    familyID = curFamily.GetID()
    name = curFamily.GetName()
    id2 = curFamily.GetLeaderID()
    name2 = curFamily.GetLeaderName()
    fightPower = GetFamilyTotalFightPower(curFamily)
    value1 = fightPower / ChConfig.Def_PerPointValue
    value2 = fightPower % ChConfig.Def_PerPointValue
    value3 = GetFamilyEmblemID(curFamily)
    value4 = curFamily.GetLV()
    value5 = curFamily.GetServerID()
    return {"id":familyID, "name":name, "id2":id2, "name2":name2, "value1":value1, "value2":value2,
            "value3":value3, "value4":value4, "value5":value5}
def Sync_ClientFamilyUpdToCrossServer():
    ## 定时同步仙盟变更数据到跨服服务器
    Sync_ClientFamilyDelToCrossServer() # 防止未删除成功,这里补通知
    mgr = GetFamilyMgr()
    if not mgr.syncCrossFamilyDict:
        return
    familyManager = GameWorld.GetFamilyManager()
    for familyID, updMemIDList in mgr.syncCrossFamilyDict.items():
        family = familyManager.FindFamily(familyID)
        if not family:
            continue
        Send_ClientServerMsg_SyncFamilyInfo("FamilyUpd", GetSyncCrossServerFamilyInfo(family, updMemIDList))
    mgr.syncCrossFamilyDict = {}
    return
def Sync_ClientFamilyAllToCrossServer():
    ## 同步子服所有仙盟信息到跨服
    if GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_SyncFamilyAllToCross):
        return
    GameWorld.GetGameWorld().SetDict(ChConfig.Def_WorldKey_SyncFamilyAllToCross, 1)
    GameWorld.Log("开始同步本服所有仙盟到跨服服务器!")
    Sync_ClientFamilyDelToCrossServer() # 防止未删除成功,这里补通知
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
        if not family:
            continue
        Send_ClientServerMsg_SyncFamilyInfo("FamilyUpd", GetSyncCrossServerFamilyInfo(family))
    return
def Sync_ClientFamilyDelToCrossServer():
    ## 同步仙盟删除数据到跨服服务器
    gameRecMgr = PyDataManager.GetDBGameRecDataManager()
    recDataDict = gameRecMgr.GetGameRecDataDict(ShareDefine.Def_GameRecType_FamilyDelSyncCross)
    for familyID, recDataList in recDataDict.items():
        for recData in recDataList:
            playerID = recData.GetValue1()
            Send_ClientServerMsg_SyncFamilyInfo("FamilyDel", {"familyID":familyID, "playerID":playerID})
    return
def GetCrossFamilyBaseInfo(family):
    return {"ID": family.GetID(), "Name": family.GetName(), "LV":family.GetLV(), "FightPower":GetFamilyTotalFightPower(family),
            "LeaderID": family.GetLeaderID(), "LeaderName": family.GetLeaderName(), "EmblemID":GetFamilyEmblemID(family),
            "ServerID":family.GetServerID(), "Broadcast":family.GetBroadcast()}
def GetCrossFamilyMemInfo(member):
    return {"Name":member.GetName(), "LV":member.GetLV(), "Job":member.GetJob(), "OfficialRank":member.GetOfficialRank(),
            "Face":member.GetFace(), "FacePic":member.GetFacePic(), "FamilyLV":member.GetFamilyLV(), "FightPower":GetMemberFightPower(member)}
def GetSyncCrossServerFamilyInfo(family, memIDList=None):
    ## 获取仙盟跨服所需信息
    # @param memIDList: 需要获取成员信息ID列表,不传则取所有成员
    crossFamilyInfo = GetCrossFamilyBaseInfo(family)
    memAll = True if memIDList == None else False
    memInfo = {}
    for m in xrange(family.GetCount()):
        member = family.GetAt(m)
        memID = member.GetPlayerID()
        if not memID:
            continue
        if memIDList and memID not in memIDList:
            continue
        memInfo[memID] = GetCrossFamilyMemInfo(member)
    crossFamilyInfo.update({"memInfo":memInfo, "memAll":memAll})
    return crossFamilyInfo
def Send_ClientServerMsg_SyncFamilyInfo(syncType, syncInfo):
    dataMsg = {"syncType":syncType, "syncInfo":syncInfo}
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_SyncFamilyInfo, dataMsg)
    return
def ClientServerMsg_SyncFamilyInfo(serverGroupID, msgData):
    ## 收到子服 - 仙盟信息
    syncType = msgData["syncType"]
    syncInfo = msgData["syncInfo"]
    if syncType == "FamilyUpd":
        __CrossServer_FamilyUpd(syncInfo)
    elif syncType == "FamilyDel":
        __CrossServer_FamilyDel(serverGroupID, syncInfo)
    return
def __CrossServer_FamilyDel(serverGroupID, syncInfo):
    ## 跨服服务器删除子服仙盟、成员
    familyID = syncInfo["familyID"]
    playerID = syncInfo.get("playerID", 0)
    if playerID:
        curFamily = GameWorld.GetFamilyManager().FindFamily(familyID)
        if curFamily:
            curFamily.DeleteMember(playerID)
    else:
        #删除家族
        GameWorld.GetFamilyManager().DelFamily(familyID)
        #删除家族行为数据
        PlayerFamilyAction.ClearFamilyAction(familyID)
    # 直接回复就是删除成功
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FamilyDelRet, syncInfo, [serverGroupID])
    return
def __CrossServer_FamilyUpd(familyInfo):
    ## 跨服服务器更新子服仙盟 - 这里只覆盖更新,不考虑删除的情况
    familyID = familyInfo["ID"]
    familyName = familyInfo["Name"]
    curFamily = FindAndFixCrossFamilyByIDName(familyID, familyName)
    if not curFamily:
        GameWorld.ErrLog("跨服更新仙盟失败! 仙盟不存在或者创建失败! familyID=%s" % familyID)
        return
    #curFamily.SetName(familyInfo["Name"]) # 这里不再更新名称
    curFamily.SetLV(familyInfo["LV"])
    SetFamilyTotalFightPower(curFamily, familyInfo["FightPower"])
    curFamily.SetLeaderID(familyInfo["LeaderID"])
    curFamily.SetLeaderName(familyInfo["LeaderName"])
    SetFamilyEmblemID(curFamily, familyInfo["EmblemID"])
    curFamily.SetServerID(familyInfo["ServerID"])
    curFamily.SetBroadcast(familyInfo["Broadcast"])
    updMemIDList = []
    memAll = familyInfo.get("memAll", False)
    memInfoDict = familyInfo.get("memInfo", {})
    if memInfoDict:
        for m in range(curFamily.GetCount())[::-1]:
            member = curFamily.GetAt(m)
            memID = member.GetPlayerID()
            if not memID:
                continue
            if memID not in memInfoDict:
                if memAll:
                    curFamily.DeleteMember(memID)
                continue
            memInfo = memInfoDict.pop(memID)
            __updCrossFamilyMemberInfo(curFamily, member, memInfo)
            updMemIDList.append(memID)
        # 剩下的就是新增的成员
        for memID, memInfo in memInfoDict.items():
            member = curFamily.AddMemberEx(memID)
            if not member:
                continue
            __updCrossFamilyMemberInfo(curFamily, member, memInfo)
            updMemIDList.append(memID)
    # 相关活动数据更新
    CrossFamilyGCZ.OnCrossJoinFamilyMemberUpd(curFamily, updMemIDList)
    return
def FindAndFixCrossFamilyByIDName(familyID, familyName):
    ## 按仙盟名及ID查找跨服仙盟,重名时系统会自动修改仙盟名
    familyManager = GameWorld.GetFamilyManager()
    curFamily = familyManager.FindFamily(familyID)
    if not curFamily:
        for i in range(100):
            crossFamilyName = familyName if i == 0 else ("%s_%s" % (familyName, i))
            curFamily = familyManager.AddFamilyEx(crossFamilyName, familyID)
            if curFamily:
                GameWorld.DebugLog("跨服添加新仙盟! familyID=%s, i=%s" % (familyID, i))
                break
        return curFamily
    # 验证当前名字是否还是唯一的,因为延迟同步的原因,仙盟可能改名时在本服唯一,但是在跨服不唯一
    nameFamily = familyManager.FindFamilyByName(familyName)
    if not nameFamily:
        GameWorld.DebugLog("新同步的名字已经不存在仙盟了,可直接替换新仙盟名! familyID=%s" % familyID)
        curFamily.SetName(familyName)
        return curFamily
    if nameFamily.GetID() == familyID:
        #GameWorld.DebugLog("还是自己原来的仙盟名,直接返回! familyID=%s" % familyID)
        return curFamily
    # 尝试修改仙盟名,如果没有唯一名,则不修改保留最后一次同步的仙盟名,至少最后一次的名字还是唯一的
    for i in range(1, 100):
        fixFamilyName = "%s_%s" % (familyName, i)
        family = familyManager.FindFamilyByName(fixFamilyName)
        if family:
            if family.GetID() == familyID:
                #GameWorld.DebugLog("保留原系统修改的仙盟名! familyID=%s" % familyID)
                break
            # 已存在该仙盟名,且不是自己的仙盟
            continue
        curFamily.SetName(fixFamilyName)
        GameWorld.Log("跨服强制修改仙盟名: familyID=%s,%s,%s" % (familyID, i, fixFamilyName))
        break
    return curFamily
def __updCrossFamilyMemberInfo(curFamily, member, memInfo):
    member.SetName(memInfo.get("Name", ""))
    member.SetLV(memInfo.get("LV", 1))
    member.SetJob(memInfo.get("Job", 1))
    member.SetOfficialRank(memInfo.get("OfficialRank", 1))
    member.SetFace(memInfo.get("Face", 0))
    member.SetFacePic(memInfo.get("FacePic", 0))
    SetMemberFightPower(member, memInfo.get("FightPower", 0))
    memFamilyLV = memInfo.get("FamilyLV", 0)
    member.SetFamilyLV(memFamilyLV)
    if memFamilyLV == IPY_GameServer.fmlLeader:
        curFamily.SetLeaderID(member.GetPlayerID())
        curFamily.SetLeaderName(member.GetName())
        curFamily.SetLeaderOfficialRank(member.GetOfficialRank())
    return