ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -29,6 +29,7 @@
import DataRecordPack
import PlayerFamilyBoss
import IpyGameDataPY
import PlayerFamilyZhenbaoge
import PlayerFamilyRedPacket
import GameWorldFamilyWar
import ChPyNetSendPack
@@ -38,10 +39,13 @@
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
@@ -59,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
## ------------------ 仙盟 ----------------------
## 仙盟联赛排名
@@ -69,15 +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)
# 徽章
def GetFamilyEmblem(curFamily): return curFamily.GetExtra6()
def SetFamilyEmblem(curFamily, value): return curFamily.SetExtra6(value)
# 徽章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()
@@ -106,11 +170,17 @@
def OnGameServerInitOK():
    ## 服务器启动成功处理
    DoFamilySort()
    if GameWorld.IsCrossServer():
        pass
    else:
        DoFamilySort()
    return
def OnMixServerInit():
    ## 合服后首次启动成功处理
    if GameWorld.IsCrossServer():
        return
    
    # 仙盟联赛重置
    GameWorldFamilyWar.DoFamilyWarReset()
@@ -270,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()
@@ -327,12 +397,16 @@
    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())
    
    #新创建的仙盟默认设置已处理过合服
@@ -349,6 +423,8 @@
    if fakeIndex and fakeIndex in fakeIndexList:
        PlayerDBGSEvent.SetDBGSTrig_ByKey(PlayerDBGSEvent.Def_FakeFamilyIndex % fakeIndexList.index(fakeIndex), 0)
        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:
@@ -396,6 +472,8 @@
    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
## 获取家族全名
@@ -430,7 +508,7 @@
    #加入家族
    familyMember = curFamily.AddMember(jionPlayer)
    #刷新基本信息
    RefreshFamilyMemberBaseMsg(familyMember, jionPlayer)
    RefreshFamilyMemberBaseMsg(curFamily, familyMember, jionPlayer)
    
    #族长设置
    if jionFamilySetLv == IPY_GameServer.fmlLeader:
@@ -476,7 +554,7 @@
    #仙盟拍品
    AuctionHouse.Sync_FamilyAuctionItemInfo(jionPlayer, curFamily.GetID())
    SetMemberFightPower(familyMember, PlayerControl.GetFightPower(jionPlayer))
    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), jionPlayer.GetPlayerID())
    
    #通知仙盟盛宴题目
    PlayerFamilyParty.NotifyFamilyPartyQuestion(jionPlayer)
@@ -595,6 +673,7 @@
#    tagHead        Head;
#    char        Name[33];
#    WORD        FakeID;
#    BYTE        EmblemID; //选择徽章ID,解锁仙盟等级为1级的均为可选ID
#};
## 查看申请帮会的成员信息
#  @param index 玩家索引
@@ -605,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)
    #玩家离开事件
@@ -666,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):
@@ -704,7 +784,8 @@
    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
    
@@ -715,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
@@ -742,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封包
@@ -772,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
@@ -809,7 +891,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
@@ -822,6 +904,19 @@
            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
@@ -882,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
## 检测目标玩家是否可以加入家族
@@ -1670,11 +1766,12 @@
    PlayerFamilyAction.DelFamilyOfficerModelEquip(curFamily.GetID(), leavePlayerID)
    # 玩家战盟名变更处理
    __OnFamilyNameChange(leavePlayerID, '')
    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
    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
#//////////////////////////////////////////////////////////////
@@ -1819,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
@@ -1928,6 +2025,7 @@
    #世界服务器家族重新排序
    #GameWorld.GetFamilyManager().SortByLV()
    DoFamilySort() # 升级直接强排一次
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 仙盟等级变更
    return True
#---------------------------------------------------------------------
@@ -1976,6 +2074,8 @@
#  @remarks 函数详细说明.
def OnPlayerChangeMap(curPlayer, tick):
    #同步给玩家, 最新的家族信息(家族等级刷新)
    if GameWorld.IsCrossServer():
        return
    curPlayer.MapServer_FamilyRefresh()
    return
@@ -2125,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
#---------------------------------------------------------------------
## 玩家刷新
@@ -2146,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
@@ -2264,8 +2367,7 @@
    
    #重新排序家族
    #GameWorld.GetFamilyManager().SortByLV()
    if familyID in PyGameData.g_sortFamilyIDList:
        PyGameData.g_sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
    GetFamilyMgr().OnDeleteFamilyID(familyID)
        
    #家族科技删除, 改为地图直接处理, 暂屏蔽
    #PlayerFamilyTech.DelFamilyTechData(familyID)
@@ -2294,6 +2396,7 @@
    #设置族长权限
    ChangeFamilyMemberLv(familyMember, IPY_GameServer.fmlLeader)
    GameWorldFamilyWar.OnChangeFamilyLeader(curFamily.GetID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 盟主变更
    return
#---------------------------------------------------------------------
##更改家族成员等级.
@@ -2325,6 +2428,8 @@
    # 变为普通成员,删除模型装备信息
    elif changeFamilyLV == IPY_GameServer.fmlMember:
        PlayerFamilyAction.DelFamilyOfficerModelEquip(familyMember.GetFamilyID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(familyMember.GetFamilyID(), familyMember.GetPlayerID(), syncNow=True) # 成员职位变更
    return
#---------------------------------------------------------------------
@@ -2503,6 +2608,7 @@
    #通知家族刷新
    curFamily.Broadcast_FamilyChange()
    
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamilyID, syncNow=True) # 仙盟改名
    playerManager = GameWorld.GetPlayerManager()
    
    #仙盟联赛
@@ -2683,6 +2789,8 @@
# @return 返回值无意义
# @remarks 家族过天
def FamilyOnDay(tick):
    if GameWorld.IsCrossServer():
        return
    #---设置所有玩家可以再次加入家族---
    GameWorld.GetPlayerManager().ClearForbiddenEnterFamily()
    #---扣除地图上所有家族的维护费---
@@ -2741,11 +2849,15 @@
    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
#---------------------------------------------------------------------
@@ -2754,7 +2866,8 @@
# @return 返回值无意义
# @remarks 家族过周
def FamilyOnWeek(tick):
    if GameWorld.IsCrossServer():
        return
    #---计算上周家族活跃度---
    familyManager = GameWorld.GetFamilyManager()
    for i in range(0, familyManager.GetCount()):
@@ -2786,6 +2899,8 @@
    return
def FamilyOnHour():
    if GameWorld.IsCrossServer():
        return
    familyManager = GameWorld.GetFamilyManager()
    for i in xrange(familyManager.GetCount()):
        family = familyManager.GetAt(i)
@@ -2855,12 +2970,31 @@
            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
@@ -3504,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
@@ -3530,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):
    ''' 仙盟排序, 排序规则: 联赛评级 > 总战力 > 等级 > 创建时间
@@ -3560,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):
@@ -3685,6 +3827,8 @@
        __DoChuangong_Response(curPlayer, tagPlayerID, isOK)
        return
    
    if msgType == "ThanksGift":
        __DoChuangong_ThanksGift(curPlayer, msgData)
    return
def __CheckChuangongPlayer(curPlayer, tagPlayerID):
@@ -3704,8 +3848,26 @@
def __DoChuangong_Invite(curPlayer, tagPlayerID):
    ## 邀请
    playerID = curPlayer.GetPlayerID()
    tagPlayer = __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:
        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:
@@ -3719,6 +3881,8 @@
    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
@@ -3759,9 +3923,41 @@
    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、徽章、仙盟名、盟主名、仙盟总战力、仙盟等级
@@ -3772,9 +3968,228 @@
    fightPower = GetFamilyTotalFightPower(curFamily)
    value1 = fightPower / ChConfig.Def_PerPointValue
    value2 = fightPower % ChConfig.Def_PerPointValue
    value3 = GetFamilyEmblem(curFamily)
    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