ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFamily.py
@@ -29,17 +29,23 @@
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
@@ -57,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
## ------------------ 仙盟 ----------------------
## 仙盟联赛排名
@@ -67,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()
@@ -98,6 +167,59 @@
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个假仙盟'''
@@ -218,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()
@@ -275,11 +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())
    
    #新创建的仙盟默认设置已处理过合服
@@ -296,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:
@@ -343,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
## 获取家族全名
@@ -377,7 +508,7 @@
    #加入家族
    familyMember = curFamily.AddMember(jionPlayer)
    #刷新基本信息
    RefreshFamilyMemberBaseMsg(familyMember, jionPlayer)
    RefreshFamilyMemberBaseMsg(curFamily, familyMember, jionPlayer)
    
    #族长设置
    if jionFamilySetLv == IPY_GameServer.fmlLeader:
@@ -416,14 +547,14 @@
    #通知战盟红包信息
    PlayerFamilyRedPacket.NotifyRedPacketInfo(jionPlayer)
    
    #通知战盟BOSS开启信息
    PlayerFamilyBoss.NotifyFamilyBossFBInfo(jionPlayer)
    #通知战盟BOSS
    PlayerFamilyBoss.OnPlayerJionFamily(curFamily, jionPlayer)
    #通知家族仓库
    PyDataManager.GetFamilyStoreItemManager().SyncFamilyStoreItem(jionPlayer, curFamily.GetID())
    #仙盟拍品
    AuctionHouse.Sync_FamilyAuctionItemInfo(jionPlayer, curFamily.GetID())
    SetMemberFightPower(familyMember, PlayerControl.GetFightPower(jionPlayer))
    AddFamilyIDToFightPowerChangeList(curFamily.GetID())
    GetFamilyMgr().AddFamilyIDToFightPowerChangeList(curFamily.GetID(), jionPlayer.GetPlayerID())
    
    #通知仙盟盛宴题目
    PlayerFamilyParty.NotifyFamilyPartyQuestion(jionPlayer)
@@ -542,6 +673,7 @@
#    tagHead        Head;
#    char        Name[33];
#    WORD        FakeID;
#    BYTE        EmblemID; //选择徽章ID,解锁仙盟等级为1级的均为可选ID
#};
## 查看申请帮会的成员信息
#  @param index 玩家索引
@@ -552,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)
    #玩家离开事件
@@ -613,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):
@@ -651,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
    
@@ -662,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
@@ -689,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封包
@@ -719,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
@@ -756,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
@@ -769,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
@@ -829,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
## 检测目标玩家是否可以加入家族
@@ -1613,14 +1762,16 @@
#  @param leavePlayerID 离开的玩家ID
#  @return None
def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID, tagPlayer=None):
    PlayerCompensation.SendMailByKey("LeaveFamilyNotice", [leavePlayerID], [])
    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
#//////////////////////////////////////////////////////////////
@@ -1765,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
@@ -1874,6 +2025,7 @@
    #世界服务器家族重新排序
    #GameWorld.GetFamilyManager().SortByLV()
    DoFamilySort() # 升级直接强排一次
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 仙盟等级变更
    return True
#---------------------------------------------------------------------
@@ -1922,6 +2074,8 @@
#  @remarks 函数详细说明.
def OnPlayerChangeMap(curPlayer, tick):
    #同步给玩家, 最新的家族信息(家族等级刷新)
    if GameWorld.IsCrossServer():
        return
    curPlayer.MapServer_FamilyRefresh()
    return
@@ -2071,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
#---------------------------------------------------------------------
## 玩家刷新
@@ -2092,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
@@ -2210,8 +2367,7 @@
    
    #重新排序家族
    #GameWorld.GetFamilyManager().SortByLV()
    if familyID in PyGameData.g_sortFamilyIDList:
        PyGameData.g_sortFamilyIDList.remove(familyID) # 直接从排序列表中移除, 不需要重新排序
    GetFamilyMgr().OnDeleteFamilyID(familyID)
        
    #家族科技删除, 改为地图直接处理, 暂屏蔽
    #PlayerFamilyTech.DelFamilyTechData(familyID)
@@ -2240,6 +2396,7 @@
    #设置族长权限
    ChangeFamilyMemberLv(familyMember, IPY_GameServer.fmlLeader)
    GameWorldFamilyWar.OnChangeFamilyLeader(curFamily.GetID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamily.GetID(), syncNow=True) # 盟主变更
    return
#---------------------------------------------------------------------
##更改家族成员等级.
@@ -2271,6 +2428,8 @@
    # 变为普通成员,删除模型装备信息
    elif changeFamilyLV == IPY_GameServer.fmlMember:
        PlayerFamilyAction.DelFamilyOfficerModelEquip(familyMember.GetFamilyID(), familyMember.GetPlayerID())
    GetFamilyMgr().SetSyncCrossFamilyUpd(familyMember.GetFamilyID(), familyMember.GetPlayerID(), syncNow=True) # 成员职位变更
    return
#---------------------------------------------------------------------
@@ -2449,6 +2608,7 @@
    #通知家族刷新
    curFamily.Broadcast_FamilyChange()
    
    GetFamilyMgr().SetSyncCrossFamilyUpd(curFamilyID, syncNow=True) # 仙盟改名
    playerManager = GameWorld.GetPlayerManager()
    
    #仙盟联赛
@@ -2629,6 +2789,8 @@
# @return 返回值无意义
# @remarks 家族过天
def FamilyOnDay(tick):
    if GameWorld.IsCrossServer():
        return
    #---设置所有玩家可以再次加入家族---
    GameWorld.GetPlayerManager().ClearForbiddenEnterFamily()
    #---扣除地图上所有家族的维护费---
@@ -2686,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
#---------------------------------------------------------------------
##家族过周
@@ -2693,7 +2866,8 @@
# @return 返回值无意义
# @remarks 家族过周
def FamilyOnWeek(tick):
    if GameWorld.IsCrossServer():
        return
    #---计算上周家族活跃度---
    familyManager = GameWorld.GetFamilyManager()
    for i in range(0, familyManager.GetCount()):
@@ -2720,11 +2894,13 @@
        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)
@@ -2794,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
@@ -2838,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)()
#===============================================================================
@@ -3332,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
@@ -3454,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
@@ -3480,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):
    ''' 仙盟排序, 排序规则: 联赛评级 > 总战力 > 等级 > 创建时间
@@ -3510,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):
@@ -3620,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