ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerFamily.py
@@ -13,788 +13,1529 @@
#---------------------------------------------------------------------
#"""Version = 2017-07-12 21:00""" 
#---------------------------------------------------------------------
import IPY_GameWorld
import GameWorld
import EventShell
import FBLogic
import ChConfig
import PlayerControl
import GameWorld
import ShareDefine
import ReadChConfig
import PlayerTruck
import PlayerControl
import NetPackCommon
import PlayerViewCache
import ChPyNetSendPack
import DataRecordPack
import GameLogic_FamilyWar
import GameLogic_FamilyBoss
import ChMapToGamePyPack
import PlayerFamilyTech
import PlayerFamilyRedPacket
import PlayerFamilyZhenfa
import PlayerTongTianLing
import PlayerWeekParty
import PlayerActivity
import ItemControler
import SkillCommon
import BuffSkill
import ItemCommon
import PlayerSuccess
import GameFuncComm
import PlayerTJG
import PlayerFamilyEmblem
import PlayerFamilyZhenbaoge
import PlayerFamilyTaofa
import IPY_PlayerDefine
import IpyGameDataPY
import cPickle
import IPY_GameWorld
import PlayerMail
import PlayerTask
import CrossPlayer
import PlayerTalk
import DirtyList
import DBDataMgr
import DBFamily
import CrossMsg
import CrossMgr
import time
import random
#---------------------------------------------------------------------
#家族刷新
#class   IPY_MFamilyRefresh
#{
#public:
#
#    int      GetFamilyID();
#
#    char *      GetFamilyName();
#};
## 家族刷新
#  @param index 玩家索引
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def GameServer_FamilyRefresh(index, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    refreshPack = IPY_GameWorld.IPY_MFamilyRefresh()
    if curPlayer.GetID() != refreshPack.GetPlayerID():
        #不是自己
        return
    lastFamilyID = curPlayer.GetFamilyID()
#************族长变更删除发布任务*************************************************
    if (curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader
        and refreshPack.GetFamilyMemberLV()!= IPY_GameWorld.fmlLeader):
        EventShell.EventResponse_OnMemberChange(curPlayer)
#*******************************************************************
import time
    #---根据封包修改玩家家族属性---
    if lastFamilyID != refreshPack.GetFamilyID():
        curPlayer.SetFamilyID(refreshPack.GetFamilyID())
        familyIDChangeTime = int(time.time())
        #PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyIDChangeTime, familyIDChangeTime)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_LastFamilyID, lastFamilyID)
        GameWorld.Log("记录家族变更时间...familyIDChangeTime=%s,lastFamilyID=%s,nextFamilyID=%s"
                           % (familyIDChangeTime, lastFamilyID, refreshPack.GetFamilyID()), curPlayer.GetPlayerID())
        #触发玩家加入家族事件
        EventShell.EventResponse_OnFamilyAdd(curPlayer)
    if curPlayer.GetFamilyName() != refreshPack.GetFamilyName():
        curPlayer.SetFamilyName(refreshPack.GetFamilyName())
        #通知周围玩家家族名称刷新
        curPlayer.Notify_FamilyNameRefresh()
    refreshFamilyLV = refreshPack.GetFamilyLV()
    lastFamilyLV = curPlayer.GetFamilyLV()
    if lastFamilyLV != refreshFamilyLV:
        #任务需求,记录先前的家族等级
        curPlayer.SetDict(ChConfig.Def_PlayerKey_FamilyLVLeave, lastFamilyLV)
        isLVUP = False
        if lastFamilyLV and refreshFamilyLV > 0:
            #触发家族升级事件
            isLVUP = True
        curPlayer.SetFamilyLV(refreshFamilyLV)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
        __OnFamilyLVBuffChange(curPlayer, tick)
        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
        if isLVUP:
            EventShell.EventResponse_OnFamilyLVUp(curPlayer)
    if curPlayer.GetFamilyMemberLV() != refreshPack.GetFamilyMemberLV():
        # 因为仙盟职位没有存DBPlayer,而跨服后又没有Family信息,所以这里做个存储,用于跨服用
        curPlayer.SetReceivedSalary(refreshPack.GetFamilyMemberLV())
        curPlayer.SetFamilyMemberLV(refreshPack.GetFamilyMemberLV())
        #通知周围玩家家族职位刷新
        #curPlayer.Notify_FamilyMemberLVRefresh()
        GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
    if curPlayer.GetFamilyMoney() != refreshPack.GetFamilyMoney():
        curPlayer.SetFamilyMoney(refreshPack.GetFamilyMoney())
    if curPlayer.GetFamilyLastWeekActiveValue() != refreshPack.GetLastWeekFamilyActiveValue():
        curPlayer.SetFamilyLastWeekActiveValue(refreshPack.GetLastWeekFamilyActiveValue())
    if PlayerControl.GetFamilyEmblemID(curPlayer) != refreshPack.GetExtra6():
        PlayerControl.SetFamilyEmblemID(curPlayer, refreshPack.GetExtra6())
    #---处理特殊逻辑, 进入退出家族---
#申请、审核、退出、踢人、捐献、升级
    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
        #玩家离开家族
        __OnLeaveFamily(curPlayer, tick)
        #改变镖车身上记录的主人信息
        PlayerTruck.ChangeTruckNoteInfo(curPlayer)
Def_CreatFamily_MaxStr = 33
    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
        #刚进家族并为族长,触发建家族事件
        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
            EventShell.EventResponse_OnCreateFamily(curPlayer)
        #进入家族触发事件
        __OnEnterFamily(curPlayer, tick)
        #改变镖车身上记录的主人信息
        PlayerTruck.ChangeTruckNoteInfo(curPlayer)
    #---通知客户端刷新属性---
    curPlayer.View_FamilyInfoRefresh()
    return
#公会权限
(
FamilyPowerID_Call,         #招人 1
FamilyPowerID_ChangeFmlv,   #变更职位 2
FamilyPowerID_Broadcast,    #发布公告 3
FamilyPowerID_Kick,         #踢人 4
) = range(1, 1 + 4)
## 进入家族触发事件
#  @param curPlayer 当前玩家
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def __OnEnterFamily(curPlayer, tick):
    EventShell.EventResponse_OnFamily(curPlayer)
    __OnFamilyLVBuffChange(curPlayer, tick)
    __FamilyAffair_CheckReset(curPlayer)
    PlayerFamilyTech.Sync_PlayerFamilyTechLV(curPlayer)
    DelAddFamilyRecord(curPlayer)
    GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
    GameLogic_FamilyBoss.OnEnterFamily(curPlayer)
    PlayerFamilyRedPacket.CreatCacheRedPacktet(curPlayer)
    PlayerFamilyZhenfa.OnEnterFamily(curPlayer)
    return
#公会职位对应人数设置属性名
Def_FmlSetAttrName = {
                      IPY_PlayerDefine.fmlMember:"MemberMax",
                      IPY_PlayerDefine.fmlCounsellor:"EliteMax",
                      IPY_PlayerDefine.fmlViceLeader:"DeputyLeaderMax",
                      }
## 退出家族触发事件
#  @param curPlayer 当前玩家
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def __OnLeaveFamily(curPlayer, tick):
    #---清空家族相关信息---
    curPlayer.SetPerExp(0)
    curPlayer.SetFamilyHornor(0)
    curPlayer.SetFamilyActiveValue(0)
    curPlayer.SetLastWeekFamilyActiveValue(0)
    curPlayer.SetFamilyLV(0)
    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
    #触发玩家离开家族的任务
    EventShell.EventResponse_OnLeaveFamily(curPlayer)
    #设置领奖限制
    __SetForbidFamilyAward(curPlayer)
    FBLogic.OnLeaveFamily(curPlayer, tick)
    __OnFamilyLVBuffChange(curPlayer, tick)
    #清空仓库积分
    PlayerControl.SetPlayerCurrency(curPlayer, ShareDefine.TYPE_Price_FamilyStoreScore, 0)
    GameLogic_FamilyWar.DoCheckChampionFamilyTitle(curPlayer)
    PlayerFamilyZhenfa.OnLeaveFamily(curPlayer)
    return
def GetRenameTime(family): return family.GetExtra1()
def SetRenameTime(family, renameTime): return family.SetExtra1(renameTime)
## 家族等级加持buff变更处理(进、退家族时 及 家族等级变更时)
def __OnFamilyLVBuffChange(curPlayer, tick):
    return
    # 跨服服务器不处理,防止登录跨服服务器后无战盟导致战力下降
def OnMinute():
    #战力刷新在DBFamily.OnMinute
    if GameWorld.IsCrossServer():
        pass
    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
        return
    PlayerFamilyEmblem.CheckExpireEmblem()
    return
def FamilyCrossCenterOnHour():
    if GameWorld.IsCrossServer():
        __doFamilyOnHour()
    return
def FamilyOnHour():
    if not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
        GameWorld.DebugLog("非游戏服或本服已跨服互通了不处理 FamilyOnHour")
        return
    __doFamilyOnHour()
    return
def __doFamilyOnHour():
    familyManager = DBDataMgr.GetFamilyMgr()
    for zoneID in familyManager.GetZoneIDListThisServer():
        zoneMgr = familyManager.GetZoneFamilyMgr(zoneID)
        for i in range(0, zoneMgr.GetCount()):
            family = zoneMgr.GetAt(i)
            #自动传位
            __AutoChangeLeader(family)
    return
def FamilyCrossCenterOnDay():
    if GameWorld.IsCrossServer():
        __doFamilyOnDay()
    return
def FamilyOnDay():
    ## 本服时间自己触发的onday逻辑
    if not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
        GameWorld.DebugLog("非游戏服或本服已跨服互通了不处理 FamilyOnDay")
        return
    
    familyLVBuffSkillTypeID = ReadChConfig.GetEvalChConfig("FamilyLVBuff")
    if not familyLVBuffSkillTypeID:
    __doFamilyOnDay()
    DBFamily.CheckCrossFamilyTransData()
    return
def __doFamilyOnDay():
    familyManager = DBDataMgr.GetFamilyMgr()
    for zoneID in familyManager.GetZoneIDListThisServer():
        zoneMgr = familyManager.GetZoneFamilyMgr(zoneID)
        for i in range(0, zoneMgr.GetCount()):
            family = zoneMgr.GetAt(i)
            familyID = family.GetID()
            PlayerFamilyZhenbaoge.OnDay(family)
            PlayerFamilyTaofa.OnDay(family)
            for index in xrange(family.GetCount()):
                member = family.GetAt(index)
                # 重置成员日信息
                member.SetContribDay(0)
                member.SetDonateCntDay(0)
            Broadcast_FamilyInfo(familyID) # onday
    return
def PlayerCrossCenterOnDay(curPlayer):
    ## 处理跨服中心同步的onday玩家事件
    if not DBFamily.IsFamilyCross():
        GameWorld.DebugLog("公会还属于本服,不处理成员跨服过天", curPlayer.GetPlayerID())
        return
    # 先删后加
    playerSkillManager = curPlayer.GetSkillManager()
    BuffSkill.DelBuffBySkillID(curPlayer, familyLVBuffSkillTypeID, tick)
    playerSkillManager.DeleteSkillBySkillTypeID(familyLVBuffSkillTypeID)
    familyLV = curPlayer.GetFamilyLV()
    GameWorld.DebugLog("__OnFamilyLVBuffChange familyLV=%s,familyLVBuffSkillTypeID=%s" % (familyLV, familyLVBuffSkillTypeID))
    if familyLV > 0:
        curSkill = GameWorld.GetGameData().FindSkillByType(familyLVBuffSkillTypeID , familyLV)
        if not curSkill:
            return
        skillID = curSkill.GetSkillID()
        playerSkillManager.LVUPSkillByID(skillID)
        SkillCommon.AddBuffBySkillType(curPlayer, familyLVBuffSkillTypeID, tick, familyLV)
    curControl = PlayerControl.PlayerControl(curPlayer)
    #curControl.CalcPassiveBuffAttr()
    curControl.RefreshPlayerAttrByBuff()
    __doPlayerOnDay(curPlayer)
    return
def GetPlayerChangeFamilyPastHour(curPlayer):
    ## 获取玩家家族变更已经经过了多长时间
    curTime = int(time.time())
    changeTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyIDChangeTime)
    return (curTime - changeTime) / 3600
#
#//////////////////////////////////////////////////////////////
#//08 04 申请家族战#tagMRequestFamilyWar
#tagMRequestFamilyWar       *   GettagMRequestFamilyWar();
#
#class   IPY_MRequestFamilyWar
#{
#public:
#
#    int      GetFamilyID();
#
#    char *      GetFamilyName();
#
#    int      GetVSFamilyID();
#
#    char *      GetVSFamilyName();
#};
## 08 04 申请家族战#
#  @param index 玩家索引
#  @param tick 当前时间
#  @return None
#  @remarks 函数详细说明.
def GameServer_RequestFamilyWar(index, tick):
#===============================================================================
#    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
#    pack = IPY_GameWorld.IPY_MRequestFamilyWar()
#    familyID = pack.GetFamilyID()
#    familyName = pack.GetFamilyName()
#    vsFamilyID = pack.GetVSFamilyID()
#    vsFamilyName = pack.GetVSFamilyName()
#    FBLogic.OnRequestFamilyWar(curPlayer, familyID, familyName, vsFamilyID, vsFamilyName, tick)
#===============================================================================
    GameWorld.ErrLog('GameServer_RequestFamilyWar, 此接口废弃')
    return
#---------------------------------------------------------------------
##玩家金钱转换为家族金钱
# @param playerMoney 玩家金钱
# @return 家族金钱
# @remarks 玩家金钱转换为家族金钱
def GetPlayerMoney_Change_FamilyMoney(playerMoney):
    return int(playerMoney * ShareDefine.Def_PlayerMoney_Change_FamilyMoney_Rate)
#---------------------------------------------------------------------
##玩家活跃度转换为家族活跃度
# @param activeValue 玩家活跃度
# @return 家族活跃度
# @remarks 玩家活跃度转换为家族活跃度
def GetPlayerActiveValue_Change_ActiveValue(activeValue):
    return activeValue
#---------------------------------------------------------------------
##累加玩家的家族活跃度
# @param curPlayer 玩家实例
# @param addValue 添加的数值
# @param sendPackGameServer 是否通知GameServer累加家族活跃度
# @param reason 来源, 原因
# @return None
# @remarks 累加玩家的家族活跃度
def AddPlayerFamilyActiveValue(curPlayer, addValue, sendPackGameServer = False, reason = 0, isSysMsg=False):
    if addValue <= 0:
        #GameWorld.ErrLog("AddPlayerFamilyActiveValue Err = %s"%(addValue))
def PlayerOnDay(curPlayer):
    ## 本服时间的onday玩家事件
    if DBFamily.IsFamilyCross():
        GameWorld.DebugLog("公会已经跨服了,不处理成员本服过天", curPlayer.GetPlayerID())
        return
    curActiveValue = curPlayer.GetFamilyActiveValue()
    totalActiveValue = curPlayer.GetLastWeekFamilyActiveValue()
    __doPlayerOnDay(curPlayer)
    return
    #---累加玩家家族活跃度---
    if curActiveValue < ChConfig.Def_UpperLimit_DWord:
        #累计活跃度
        curPlayer.SetFamilyActiveValue(min(curActiveValue + addValue, ChConfig.Def_UpperLimit_DWord))
        curPlayer.SetLastWeekFamilyActiveValue(min(totalActiveValue + addValue, ChConfig.Def_UpperLimit_DWord))
    #增加贡献度
    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_Family_Contribution, addValue, isSysHint=isSysMsg)
    #流向记录:
    DataRecordPack.DR_AddPlayerFamilyActiveValue(curPlayer, addValue, curPlayer.GetFamilyActiveValue(), reason)
def __doPlayerOnDay(curPlayer):
    PlayerFamilyZhenbaoge.PlayerOnDay(curPlayer)
    PlayerFamilyTaofa.PlayerOnDay(curPlayer)
    Do_MapServer_PlayerOnDay(curPlayer)
    return
    #---通知世界服务器累加家族活跃度---
    #@warning: 刀剑笑项目, 玩家家族活跃度累加同时累加家族活跃度
    #          如果这边没有加上去在外面要处理一次累加家族活跃度
    if not sendPackGameServer:
        return True
    #刀剑笑要求, 玩家活跃度累加的同时累加家族活跃度 1活跃=1资金
    SendPack_GameServer_AddFamilyDetailEx(curPlayer, addValue, addValue, resion=reason)
    return True
#---------------------------------------------------------------------
##通知世界服务器累加家族属性
# @param curPlayer 玩家实例
# @param addFamilyHornor 添加家族兽粮
# @param addFamilyMoney 添加家族资金
# @param addFamilyActiveValue 增加家族活跃度
# @return 返回值无意义
# @remarks 通知世界服务器累加家族属性
def SendPack_GameServer_AddFamilyDetail(curPlayer, addFamilyHornor = 0, addFamilyMoney = 0, addFamilyActiveValue = 0, resion=0):
#    //增加家族荣誉
#    int      GetAddFamilyHornor();
#    //增加家族资金
#    int      GetAddFamilyMoney();
#    //增加家族活跃度
#    int      GetFamilyActiveValue();
#    GameWorld.Log('addFamilyHornor = %s, addFamilyMoney = %s, addFamilyActiveValue = %s'%
#                        (addFamilyHornor, addFamilyMoney, addFamilyActiveValue))
    #curPlayer.GameServer_AddFamilyDetail(addFamilyHornor, addFamilyMoney, addFamilyActiveValue)
    #改为PY包
    if not curPlayer.GetFamilyID():
def OnPlayerLogin(curPlayer, tick):
    Do_MapServer_PlayerLogin(curPlayer)
    if DBFamily.IsFamilyCross():
        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员登录逻辑 OnPlayerLogin", curPlayer.GetPlayerID())
        return
    sendPack = ChMapToGamePyPack.tagMGAddFamilyDetail()
    sendPack.PlayerID = curPlayer.GetPlayerID()
    sendPack.AddFamilyHornor = addFamilyHornor
    sendPack.AddFamilyMoney = addFamilyMoney
    sendPack.FamilyActiveValue = addFamilyActiveValue
    sendPack.AddResion = resion
    NetPackCommon.SendPyPackToGameServer(sendPack)
    GameWorld.DebugLog("AddFamilyDetail addFamilyHornor=%s,addFamilyMoney=%s,addFamilyActiveValue=%s, resion=%s"
                       % (addFamilyHornor, addFamilyMoney, addFamilyActiveValue, resion), curPlayer.GetPlayerID())
    return
#---------------------------------------------------------------------
##通知世界服务器累加家族属性
# @param curPlayer 玩家实例
# @param addFamilyActiveValue 增加玩家家族活跃度
# @param addFamilyHornor 添加家族荣誉
# @param addFamilyMoney 添加家族资金
# @return 返回值无意义
# @remarks 通知世界服务器累加家族属性
def SendPack_GameServer_AddFamilyDetailEx(curPlayer, addPlayerActiveValue, addFamilyMoney = 0, addFamilyHornor = 0, resion=0):
    #刀剑笑增加玩家活跃度默认新增家族活跃度
    addFamilyActiveValue = GetPlayerActiveValue_Change_ActiveValue(addPlayerActiveValue)
    SendPack_GameServer_AddFamilyDetail(curPlayer, addFamilyHornor, addFamilyMoney, addFamilyActiveValue, resion)
    return
#---------------------------------------------------------------------
##通知GameServer, 客户端开始创建家族
# @param curPlayer 玩家实例
# @return 返回值无意义
# @remarks 通知GameServer, 客户端开始创建家族
def SendPack_GameServer_InputFamilyName(curPlayer):
    #通知GameServer设置查看状态, 防外挂
    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_CreateFamily)
    #通知客户端打开输入家族名界面
    curPlayer.Frm_InputFamilyName()
    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
    OnCrossPlayerLogin(crossPlayer)
    return
##通知GameServer, 客户端开始浏览家族信息
# @param curPlayer 玩家实例
# @return 返回值无意义
# @remarks 通知GameServer, 客户端开始浏览家族信息
def SendPack_GameServer_ViewAllFamily(curPlayer):
    #通知GameServer设置查看状态, 防外挂
    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_FamilyWar)
    #通知GameServer给客户端家族信息
    curPlayer.GameServer_ViewAllFamily()
def OnCrossPlayerLogin(crossPlayer):
    ## 玩家上线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
    PlayerLoginRefreshFamily(crossPlayer)
    Sync_RequestAddFamilyInfo(crossPlayer, False)
    PlayerTalk.NotifyTalkCache(crossPlayer, [IPY_GameWorld.tcFamily]) # 公会聊天缓存
    #PlayerFamilyTaofa.OnPlayerLogin(curPlayer) 讨伐待修改
    return
##通知GameServer, 清除客户端浏览家族状态
# @param curPlayer 玩家实例
# @return 返回值无意义
# @remarks 通知GameServer, 通知GameServer, 清除客户端浏览家族状态
def SendPack_GameServer_ClearViewFamilyState(curPlayer):
    #MapServer告诉GameServer自己是否在创建家族状态
    #因为家族封包是在GameServer处理的, 防止被骗
    if curPlayer.GetViewFamilyState() in [ShareDefine.TViewFamilyType_None,
                                          ShareDefine.TViewFamilyType_CreateFamily]:
        #TViewFamilyType_CreateFamily状态在GameServer DoCreateFamily结束后解除
def OnPlayerLogout(curPlayer):
    if DBFamily.IsFamilyCross():
        GameWorld.DebugLog("公会已跨服不处理,由所属跨服服务器处理成员离线逻辑 OnPlayerLogout", curPlayer.GetPlayerID())
        return
    curPlayer.GameServer_SetPlayerViewFamilyState(ShareDefine.TViewFamilyType_None)
    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(curPlayer.GetPlayerID())
    OnCrossPlayerLogout(crossPlayer)
    return
def __SetForbidFamilyAward(curPlayer):
    ''' 设置家族相关活动领奖限制
     如果退出时今日已开始过对应活动,则设置领奖限制
def OnCrossPlayerLogout(crossPlayer):
    ## 玩家下线,游戏服跨服通用,流程上当做以前GameServer处理公会一样,处理后再通知地图(现在的游戏服)
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if not familyID:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    curMember.SetOffTime(int(time.time()))
    # 通知成员下线
    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID]) # 成员下线
    return
def SendToFamilyMapPlayer(crossPlayer, doType, doData):
    playerID = crossPlayer.GetPlayerID()
    dataMsg = {"doType":doType, "doData":doData}
    if GameWorld.IsCrossServer():
        CrossMsg.SendToClientServer(ShareDefine.C2S_FamilyMapPlayer, dataMsg, [crossPlayer.GetMainServerID()], playerID)
    else:
        C2S_FamilyMapPlayer(dataMsg, playerID)
    return
def MapServer_FamilyRefresh(crossPlayer, familyID, isVoluntarily=0):
    ''' 相当于GameServer调用 curPlayer.MapServer_FamilyRefresh()
    '''
    playerID = crossPlayer.GetPlayerID()
    FmLV = 0 # ְλ
    FamilyLV = 0 # 公会等级
    FamilyName = ""
    EmblemID, EmblemWord = 0, ""
    if familyID:
        familyMgr = DBDataMgr.GetFamilyMgr()
        curFamily = familyMgr.FindFamily(familyID)
        if curFamily:
            FamilyLV = curFamily.GetLV()
            FamilyName = curFamily.GetName()
            EmblemID = curFamily.GetEmblemID()
            EmblemWord = curFamily.GetEmblemWord()
            member = curFamily.FindMember(playerID)
            if member:
                FmLV = member.GetFmLV()
        else:
            familyID = 0
    crossPlayer.SetFamilyID(familyID)
    # 同步更新查看缓存
    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, familyID, FamilyName, EmblemID, EmblemWord)
    doData = {"FamilyID":familyID}
    if familyID:
        doData.update({"FmLV":FmLV, "FamilyLV":FamilyLV, "FamilyName":FamilyName, "EmblemID":EmblemID, "EmblemWord":EmblemWord})
    if isVoluntarily:
        doData["isVoluntarily"] = 1
    SendToFamilyMapPlayer(crossPlayer, "FamilyRefresh", doData)
    return
def PlayerLoginRefreshFamily(crossPlayer):
    ## 玩家登录时刷新家族
    playerID = crossPlayer.GetPlayerID()
    familyMgr = DBDataMgr.GetFamilyMgr()
    refreshFamilyID = familyMgr.GetPlayerFamilyID(playerID)
    GameWorld.DebugLog("PlayerLoginRefreshFamily playerID=%s,refreshFamilyID=%s" % (playerID, refreshFamilyID))
    crossPlayer.SetFamilyID(refreshFamilyID)
    MapServer_FamilyRefresh(crossPlayer, refreshFamilyID) # 登录
    familyID = refreshFamilyID
    if not familyID:
        return
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    curMember.SetOffTime(0) # 在线0,脱机1,>1离线时间
    curMember.RefreshMemberByID(playerID)
    Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
    # 广播成员在线
    Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员登录
    #通知招人
    if GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
        CrossPlayer.SendFakePack(crossPlayer, GetPack_FamilyReqJoinInfo(familyID))
    # 盟主上线处理
    #if curMember.GetFmLV() == IPY_PlayerDefine.fmlLeader:
    #    OnFamilyLeaderLogin(curPlayer)
    return
def RefreshFamilyMember(crossPlayer):
    ## 玩家成员相关属性变更时同步更新家族成员信息
    if GameWorld.IsCrossServer():
        pass
    elif not GameWorld.IsMainServer() or DBFamily.IsFamilyCross():
        return
    familyID = crossPlayer.GetFamilyID()
    if not familyID:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    playerID = crossPlayer.GetPlayerID()
    member = family.FindMember(playerID)
    if not member:
        return
    member.RefreshMemberByID(playerID)
    return
def FamilyPyPackForwarding(curPlayer, clientData, tick, funcName, needResult=False, reqCD=0.5, reqDataEx=None):
    ## 玩家请求公会功能包转发处理
    # @needResult: 转发跨服处理完毕是否需要回复状态,一般有消耗货币、物品相关的都要回复,且设置一个较长请求cd,防止重复处理
    # @param reqCD: 转发跨服请求cd,秒,支持小数
    
    playerID = curPlayer.GetPlayerID()
    openState = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_FamilyActivityDayState)
    forbidState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAward)
    GameWorld.DebugLog("退出家族处理家族活动奖励领取限制! openState=%s,forbidState=%s" % (openState, forbidState), playerID)
    
    updForbidState = forbidState
    for activityType in ShareDefine.Def_FamActivityList:
        if not openState&pow(2, activityType):
            GameWorld.DebugLog("    该家族活动今日未开启过! 不设置限制领奖! activityType=%s" % activityType, playerID)
            continue
        updForbidState = updForbidState|pow(2, activityType)
        GameWorld.DebugLog("    家族活动领奖限制 activityType=%s,updForbidState=%s" % (activityType, updForbidState), playerID)
    if updForbidState != forbidState:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_ForbidFamilyAward, updForbidState)
        GameWorld.DebugLog("    更新家族活动领奖限制: forbidState=%s,updForbidState=%s" % (forbidState, updForbidState), playerID)
    return
## 设置玩家是否可以参加家族活动
#  @param curPlayer 玩家实例
#  @param isForbid 是否限制
#  @return 返回值无意义
def SetForbidFamilyAction(curPlayer, isForbid):
    #===============================================================================================
    # if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAction) != isForbid:
    #    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_ForbidFamilyAction, isForbid)
    #
    #    # 通知玩家是否可以参加家族活动
    #    SendClientForbidFamilyAction(curPlayer)
    #===============================================================================================
    return
## 通知玩家是否可以参加家族活动
#  @param curPlayer 玩家实例
#  @return 返回值无意义
def SendClientForbidFamilyAction(curPlayer):
#    isForbid = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_ForbidFamilyAction)
#
#    outCardsCDTime = ChPyNetSendPack.tagPyFamilyActionForbid()
#    outCardsCDTime.Clear()
#
#    outCardsCDTime.IsForbid = isForbid
#    NetPackCommon.SendFakePack(curPlayer, outCardsCDTime)
    return
#===============================================================================
# //A6 01  向玩家申请加入家族 #tagCGRequestJoinFamilyByPlayer
#
# struct    tagCGRequestJoinFamilyByPlayer
#
# {
#    tagHead        Head;
#    DWORD        AddPlayerID;    //申请加入的玩家ID
# };
#===============================================================================
## 通过申请加入家族
#  @param index: 玩家索引
#  @param clientData: 封包结构体
#  @param tick: 时间戳
#  @return: None
def RequestAddFamilyByID(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    addPlayerID = clientData.AddPlayerID    #申请进入的家族ID
    addPlayer = GameWorld.GetPlayerManager().FindPlayerByID(addPlayerID)
    #对方下线
    if not addPlayer:
    crossServerID = DBDataMgr.GetFamilyMgr().GetCurCrossServerID()
    if crossServerID == -2:
        PlayerControl.NotifyCode(curPlayer, "FamilyInTransData")
        return
    
    addFamilyID = addPlayer.GetFamilyID()
    #对方没有家族
    if addFamilyID <= 0:
    if crossServerID < 0:
        GameWorld.ErrLog("公会功能异常! crossServerID=%s" % crossServerID)
        return
    
    #申请加入
    AddFamily(curPlayer, addFamilyID)
    #通知当前申请加入的哪些家族
    #Sync_RequestAddFamilyInfo(curPlayer)
    return
#===============================================================================
# //A6 02  申请加入家族#tagCGRequesJoinFamily
#
# struct    tagCGRequesJoinFamily
#
# {
#    tagHead        Head;
#    BYTE        Type;        //申请类型
#    DWORD        AddFamilyID;    //申请加入的家族
# };
#===============================================================================
## 申请加入家族
#  @param index: 玩家索引
#  @param clientData: 封包结构体
#  @param tick: 时间戳
#  @return: None
def RequestAddFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    addFamilyID = clientData.AddFamilyID  # 申请进入的家族ID
    requestType = clientData.Type   # 申请类型(申请/撤销)
    if requestType == 0:
        #申请加入
        AddFamily(curPlayer, addFamilyID)
    elif requestType == 1:
        DelAddFamilyRecord()
    #通知当前申请加入的哪些家族
    Sync_RequestAddFamilyInfo(curPlayer)
    return
def DelAddFamilyRecord(curPlayer):
    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
        #申请加入的家族ID
        requestAddFamilyID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index)
        if not requestAddFamilyID:
            continue
        #本版本因为客户端没有单个取消申请的操作,此处发一次包一次性全部取消
#            if addFamilyID != requestAddFamilyID:
#                continue
        #申请数量-1
        requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyCnt, requestAddFamilyCnt - 1)
        #清除撤销申请的家族ID记录
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyID%index, 0)
        #向GameServer发送请求删除该玩家的申请家族信息情况
        sendMsg = str(requestAddFamilyID)
        curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_DelFamilyAction,
                                     ShareDefine.Def_ActionType_FamilyAdd, '', sendMsg, len(sendMsg))
    Sync_RequestAddFamilyInfo(curPlayer)
    return
## 通知当前申请加入的哪些家族
#  @param curPlayer:玩家实例
#  @return:  None
def Sync_RequestAddFamilyInfo(curPlayer):
    requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
    packList = []
    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
        familyID = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index)
        if familyID == 0:
            continue
        requestAddFamilyInfo = ChPyNetSendPack.tagRequestJoinFamily()
        requestAddFamilyInfo.RequestFamilyID = familyID
        packList.append(requestAddFamilyInfo)
    requestAddFamily = ChPyNetSendPack.tagMCNotifyRequestJoinFamilyInfo()
    requestAddFamily.Clear()
    requestAddFamily.RequestCount = len(packList)
    requestAddFamily.RequestAddFamilyInfo = packList
    NetPackCommon.SendFakePack(curPlayer, requestAddFamily)
    return
## 申请加入帮会
#@param curPlayer 家族实例
#@param familyID 家族ID
#@return 返回值无意义
def AddFamily(curPlayer, familyID):
    requestAddFamilyCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyCnt)
    if requestAddFamilyCnt >= ChConfig.Def_Player_RequestAddFamilyMaxCnt:
        # jiazu_pan_21675 改为 jiazu_lhs_31379 最多同时向%s个帮会发出申请
        PlayerControl.NotifyCode(curPlayer, "jiazu_lhs_31379", [ChConfig.Def_Player_RequestAddFamilyMaxCnt])
    # 本服处理
    if crossServerID == 0 or crossServerID == GameWorld.GetGameWorld().GetServerID():
        isOK = CallPyPackFunc(playerID, clientData, funcName, reqDataEx=reqDataEx)
        if needResult:
            __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
        return
    #是否已经申请了
    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
        if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index) == familyID:
            GameWorld.DebugLog('已经在申请列表中')
            #已经在申请列表中
    ssServer = CrossMgr.GetSSServerMgr().GetSSServer(crossServerID)
    if not ssServer or not ssServer.IsServerOpen():
        PlayerControl.NotifyCode(curPlayer, "ServerNoOpen")
        return
    # 转发请求CD验证
    if reqCD:
        reqTick = curPlayer.GetDictByKey(funcName) # 根据函数名单独处理CD,防止相互影响
        if reqTick and (tick - reqTick) < reqCD * 1000:
            PlayerControl.NotifyCode(curPlayer, "RequestLater")
            return
    #请求记录申请进入家族信息
    sendMsg = cPickle.dumps([familyID, PlayerControl.GetFightPower(curPlayer)], 2)
    curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_AddFamilyAction,
                                        ShareDefine.Def_ActionType_FamilyAdd, '', sendMsg, len(sendMsg))
    #等GameServer反馈结果再处理
    return
    #记录个人申请加入的家族ID
    for index in range(0, ChConfig.Def_Player_RequestAddFamilyMaxCnt):
        if curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_RequestAddFamilyID%index) == 0:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyID%index, familyID)
            break
        curPlayer.SetDict(funcName, tick)
        
    #记录个人申请加入家族的个数
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_RequestAddFamilyCnt, requestAddFamilyCnt + 1)
    # 剩下的就是大于0
    dataMsg = {"funcName":funcName}
    if clientData:
        dataMsg["packBuff"] = clientData.GetBuffer()
    if reqDataEx:
        dataMsg["reqDataEx"] = reqDataEx
    if needResult:
        dataMsg["needResult"] = 1
    CrossMsg.SendToCrossServer(ShareDefine.S2C_FamilyPyPack, dataMsg, [crossServerID], playerID)
    return
def S2C_FamilyPyPack(dataMsg, fromServerID, playerID):
    funcName = dataMsg["funcName"]
    packBuff = dataMsg.get("packBuff")
    reqDataEx = dataMsg.get("reqDataEx")
    clientData = None
    if packBuff:
        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
        if not clientData:
            return
    isOK = CallPyPackFunc(playerID, clientData, funcName, fromServerID, reqDataEx)
    # 处理结束回复,无论成功与否
    if "needResult" in dataMsg:
        dataMsg.pop("needResult")
        dataMsg["isOK"] = isOK
        CrossMsg.SendToClientServer(ShareDefine.C2S_FamilyPyPackRet, dataMsg, [fromServerID], playerID)
    return
## 战盟玩家OnDay
#  @param curPlayer
#  @return None
def FamilyPlayerOnDay(curPlayer):
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
def C2S_FamilyPyPackRet(dataMsg, playerID):
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    #每日福利奖励
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDayAward, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyMoneyDonateCount, 0)
    Sync_FamilyDayRewardState(curPlayer)
    __FamilyAffair_Refresh(curPlayer, True)
    funcName = dataMsg["funcName"]
    packBuff = dataMsg.get("packBuff")
    
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeCut, 0)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy, 0)
    Sync_ZhenbaogeInfo(curPlayer)
    curPlayer.SetDict(funcName, 0) # 重置CD
    clientData = None
    if packBuff:
        clientData = NetPackCommon.ReadRecPyPackData(packBuff)
        if not clientData:
            return
    isOK = dataMsg.get("isOK")
    __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK)
    return
## 战盟玩家OnLogin
#  @param curPlayer
#  @return None
def FamilyPlayerOnLogin(curPlayer, tick):
    if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Family):
def CallPyPackFunc(playerID, clientData, funcName, fromServerID=0, reqDataEx=None):
    crossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(playerID)
    if not crossPlayer:
        GameWorld.ErrLog("找不到该CrossPlayer! playerID=%s" % playerID, playerID)
        return
    callFunc = GetCallFunc(funcName)
    if not callFunc:
        GameWorld.ErrLog("公会封包功能函数名不存在! %s" % funcName)
        return
    # 执行函数
    tick = GameWorld.GetGameWorld().GetTick()
    isOK = callFunc(crossPlayer, clientData, tick, fromServerID, reqDataEx)
    return isOK
    PlayerFamilyTech.Sync_PlayerFamilyTechLV(curPlayer)
    SyncFamilyActivityInfo(curPlayer)
    Sync_FamilyDayRewardState(curPlayer)
    __FamilyAffair_CheckReset(curPlayer)
    PlayerFamilyZhenfa.OnPlayerLogin(curPlayer)
    Sync_ZhenbaogeInfo(curPlayer)
def __doFamilyPyPackRet(curPlayer, clientData, funcName, isOK):
    ## 处理后额外处理,如成就、任务等
    funcName = "%s_Ret" % funcName
    callFunc = GetCallFunc(funcName)
    if not callFunc:
        # 结果额外处理函数允许不一定需要,根据功能自行决定
        return
    callFunc(curPlayer, clientData, isOK)
    return
def FamilyPlayerOnLoginCross(curPlayer):
    ## 登录跨服服务器
    crossFamilyMemberLV = curPlayer.GetReceivedSalary()
    if crossFamilyMemberLV:
        curPlayer.SetFamilyMemberLV(crossFamilyMemberLV)
        GameWorld.DebugLog("跨服登录设置仙盟职位等级: %s" % crossFamilyMemberLV, curPlayer.GetPlayerID())
def GetCallFunc(funcName):
    callFunc = None
    if "." in funcName:
        # 分割字符串,最后一个部分是函数名
        parts = funcName.split('.')
        if len(parts) == 2:
            moduleName, func_name = parts
            # 导入模块
            module = __import__(moduleName)
            # 获取函数
            if hasattr(module, func_name):
                func = getattr(module, func_name)
                if callable(func):
                    callFunc = func
    else:
        gDict = globals()
        if funcName in gDict and callable(gDict[funcName]):
            # 获取函数对象
            callFunc = gDict[funcName]
    return callFunc
#// A6 04 创建家族 #tagCMCreateFamily
#
#struct    tagCMCreateFamily
#{
#    tagHead        Head;
#    char        Name[33];
#    WORD        EmblemID; //选择徽章ID,解锁公会等级为1级的均为可选ID
#    char        EmblemWord[3];    //徽章文字
#};
def OnCreateFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    inputName = clientData.Name
    GameWorld.DebugLog("OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
    # 通用的前置验证,直接游戏服处理即可
    if not CheckInputFamilyName(curPlayer, inputName):
        return
    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
    if moneyType and needMoney:
        if not PlayerControl.HaveMoney(curPlayer, moneyType, needMoney):
            return
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnCreateFamily", True, 20)
    return
def __OnCreateFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    inputName = clientData.Name
    emblemID = clientData.EmblemID
    emblemWord = clientData.EmblemWord
    GameWorld.DebugLog("__OnCreateFamily: %s" % GameWorld.CodeToGbk(inputName))
    fullFamilyName = inputName
    playerID = crossPlayer.GetPlayerID()
    playerFamilyID = crossPlayer.GetFamilyID()
    familyMgr = DBDataMgr.GetFamilyMgr()
    if playerFamilyID:
        curFamily = familyMgr.FindFamily(playerFamilyID)
        if curFamily:
            if curFamily.FindMember(playerID):
                #玩家已经有家族, 创建失败
                CrossPlayer.NotifyCode(crossPlayer, "GeRen_chenxin_85890")
                return
    # 验证重名
    if CheckFamilyNameExists(crossPlayer, fullFamilyName, fromServerID):
        return
    serverID = crossPlayer.GetServerID()
    curFamily = familyMgr.AddFamily(fullFamilyName, serverID)
    if curFamily == None:
        GameWorld.ErrLog("创建家族失败", playerID)
        return
    newFamilyID = curFamily.GetID()
    zoneMgr = familyMgr.GetZoneFamilyMgrByFamilyID(newFamilyID)
    zoneID = zoneMgr.GetZoneID() if zoneMgr else -1
    curFamily.SetLV(1)
    emblemIDList = PlayerFamilyEmblem.GetDefaultFamilyEmblemIDList()
    if not emblemID or emblemID not in emblemIDList:
        emblemID = random.choice(emblemIDList) # 从默认徽章中随机选择一个
    GameWorld.Log("创建公会: familyID=%s,playerID=%s,emblemID=%s,serverID=%s,zoneID=%s" % (newFamilyID, playerID, emblemID, serverID, zoneID))
    curFamily.SetEmblemID(emblemID)
    curFamily.SetEmblemWord(emblemWord)
    # 扣除消耗
    needMoney = IpyGameDataPY.GetFuncCfg("CreateFamily", 1)
    moneyType = IpyGameDataPY.GetFuncCfg("CreateFamily", 2)
    if moneyType and needMoney:
        CrossPlayer.CostPlayerResources(crossPlayer, "CreateFamily", costMoneyDict={moneyType:needMoney})
    #-设置家族成员属性
    DoPlayerJionFamily(curFamily, playerID, crossPlayer, IPY_PlayerDefine.fmlLeader)
    if zoneMgr:
        zoneMgr.Sort()
    #XW_JZ_EstablishSud <n color="255,255,0">恭喜您,家族建立成功!</n>    25  -   -
    #CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_EstablishSud")
    #PlayerControl.WorldNotify(0, "jiazu_liubo_671654", [curPlayer.GetName(), fullFamilyName, newFamilyID])
    PlayerFamilyZhenbaoge.OnZhenbaogeReset(curFamily)
    return True
def CheckInputFamilyName(curPlayer, inputName):
    '''检查玩家输入的公会名是否合法,建盟、改名通用
    【注】该函数仅在游戏服验证名字的通用合法性,如长度、敏感词等,公会名重名请在公会所在数据服验证
    @return: None-不合法;非空-合法的公会全名
    '''
    #C++过滤空格
    familyName = GameWorld.GetGameWorld().GetCharTrim(inputName)
    serverID = GameWorld.GetPlayerServerID(curPlayer)
    maxServerID = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 3)
    if serverID > maxServerID or serverID <= 0:
        GameWorld.ErrLog("公会全名 serverID=%s error! maxServerID=%s, check FamilyNameFormat.txt" % (serverID, maxServerID))
        return
    maxLen = IpyGameDataPY.GetFuncCfg("FamilyNameFormat", 2)
    if len(familyName) > maxLen:
        GameWorld.ErrLog("公会全名 familyName=%s, len=%s > %s, check FamilyNameFormat.txt" % (familyName, len(familyName), maxLen))
        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [maxLen / 3, maxLen])
        return
    if DirtyList.IsWordForbidden(familyName):
        #XW_JZ_Family_NameNoLegality 对不起,家族名称中含有非法字符
        PlayerControl.NotifyCode(curPlayer, "NameSensitive")
        return
    if len(familyName) <= 0 or len(familyName) > Def_CreatFamily_MaxStr:
        PlayerControl.NotifyCode(curPlayer, "NameLenLimit", [Def_CreatFamily_MaxStr / 3, Def_CreatFamily_MaxStr])
        return
    return True
def CheckFamilyNameExists(crossPlayer, familyName, fromServerID=0):
    ## 重名在数据所在服务器验证
    familyMgr = DBDataMgr.GetFamilyMgr()
    zoneID = familyMgr.GetZoneIDInThisServer(fromServerID)
    if zoneID < 0:
        GameWorld.ErrLog("验证公会重名时分区异常也视为重名")
        return True
    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
    # 不同分区暂时允许重名
    if zoneMgr.FindFamilyByName(familyName):
        #XW_JZ_EstablishErr_Name    <n color="255,255,0">对不起,您输入的家族名已存在,建立家族失败!</n> 25  -   -
        CrossPlayer.NotifyCode(crossPlayer, "NameExists")
        return True
    return False
def DoPlayerJionFamily(family, playerID, crossPlayer, jionFamilySetLv=IPY_PlayerDefine.fmlMember, broadcastFamilyChange=True):
    '''加入家族,支持离线玩家加入
    @param crossPlayer: 如果是离线玩家则为None
    '''
    familyMgr = DBDataMgr.GetFamilyMgr()
    if isinstance(family, int):
        familyID = family
        curFamily = familyMgr.FindFamily(familyID)
    else:
        curFamily = family
    if not curFamily:
        return
    familyID = curFamily.GetID()
    member = curFamily.AddMember(playerID)
    member.SetFmLV(jionFamilySetLv)
    member.RefreshMemberByID(playerID)
    if jionFamilySetLv == IPY_PlayerDefine.fmlLeader:
        curFamily.SetLeaderID(playerID)
    if broadcastFamilyChange:
        # 广播其他在线成员
        Broadcast_FamilyInfo(familyID, changeMemIDList=[playerID], excludeIDList=[playerID]) # 成员加入
    familyMgr.DelPlayerReqJoinFamilyIDAll(playerID)
    #设置玩家身上保存的家族信息
    if crossPlayer:
        MapServer_FamilyRefresh(crossPlayer, familyID) # 加入
        Sync_FamilyInfo(crossPlayer) # 给自己同步完整的
        Sync_RequestAddFamilyInfo(crossPlayer)
        PlayerFamilyTaofa.OnCrossPlayerEnterFamily(crossPlayer)
    if jionFamilySetLv != IPY_PlayerDefine.fmlLeader:
        #通知所有家族成员, 这个人加入了家族
        CrossPlayer.FamilyNotify(familyID, "XW_JZ_EnterFamily", [member.GetPlayerName()], excludeIDList=[playerID])
        #if jionPlayer:
        #    PlayerControl.NotifyCode(jionPlayer, 'XW_JZ_EnterFamilyInfo', [family.GetName()])
    #记录加入事件
    tick = GameWorld.GetGameWorld().GetTick()
    AddFamilyActionNote(member.GetPlayerName(), familyID, ShareDefine.Def_ActionType_FamilyEvent,
                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_Join, jionFamilySetLv, 0], tick)
    return
def AddFamilyActionNote(curName, familyID, actionType, actionDataList, tick, isClearNone=True, useData=""):
    '''统一添加家族Action数据
    '''
    if actionType not in ShareDefine.Def_ActionTypeList:
        #记录行为类型错误
        GameWorld.ErrLog("记录家族行为类型:%s 错误")
        return False
    familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
    actionData = familyAction.AddAction()
    if not actionData:
        return False
    #---设置行为对象数据---
    actionData.SetName(curName)  # 设置名字
    actionData.SetUserData(useData) # 设置自定义数据
    for num, value in enumerate(actionDataList, 1):
        setFunc = getattr(actionData, "SetValue%s" % num)
        if setFunc:
            setFunc(value)
    return True
def GetFamilyMemberHasPow(member, powerID):
    ## 公会成员是否有该权限
    powerDict = IpyGameDataPY.GetFuncEvalCfg("FamilyPower", 1, {})
    if str(powerID) not in powerDict:
        return False
    needMemberLV = powerDict[str(powerID)]
    return member.GetFmLV() >= needMemberLV
def Sync_FamilyInfo(crossPlayer, syncMemIDList=None, isSyncMem=True):
    ## 通知指定玩家 // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
    familyID = crossPlayer.GetFamilyID()
    if not familyID:
        return
    clientPack = GetPack_FamilyInfo(familyID, syncMemIDList, isSyncMem)
    CrossPlayer.SendFakePack(crossPlayer, clientPack)
    return
def Broadcast_FamilyInfo(familyID, changeMemIDList=None, isSyncMem=True, excludeIDList=None):
    ## 广播给公会成员 // A5 20 玩家家族信息 #tagMCRoleFamilyInfo
    # @param changeMemIDList: 指定仅通知哪些变化成员信息,差异更新通知
    # @param isSyncMem: 是否需要通知成员信息
    clientPack = GetPack_FamilyInfo(familyID, changeMemIDList, isSyncMem)
    CrossPlayer.SendFakePackByFamily(familyID, clientPack, excludeIDList)
    return
def GetPack_FamilyInfo(familyID, changeMemIDList=None, isSyncMem=True):
    familyMgr = DBDataMgr.GetFamilyMgr()
    curFamily = familyMgr.FindFamily(familyID)
    if not curFamily:
        return
    clientPack = ChPyNetSendPack.tagMCRoleFamilyInfo()
    clientPack.FamilyID = familyID
    clientPack.FamilyName = curFamily.GetName()
    clientPack.FamilyLV = curFamily.GetLV()
    clientPack.FamilyLVExp = curFamily.GetExp()
    clientPack.JoinReview = curFamily.GetJoinReview()
    clientPack.JoinLVMin = curFamily.GetJoinLVMin()
    clientPack.ServerID = curFamily.GetServerID()
    clientPack.EmblemID = curFamily.GetEmblemID()
    clientPack.EmblemWord = curFamily.GetEmblemWord()
    clientPack.FightPower = curFamily.GetFightPower()
    clientPack.FightPowerEx = curFamily.GetFightPowerEx()
    clientPack.Broadcast = curFamily.GetBroadcast()
    clientPack.BroadcastLen = len(clientPack.Broadcast)
    clientPack.LeaderID = curFamily.GetLeaderID()
    clientPack.MemberList = []
    # ---------------
    # 测试用,同步全部,等前端同步修改后删除
    #isSyncMem = True
    #changeMemIDList = []
    # ---------------
    if isSyncMem:
        for index in xrange(curFamily.GetCount()):
            member = curFamily.GetAt(index)
            memID = member.GetPlayerID()
            if changeMemIDList and memID not in changeMemIDList:
                continue
            memInfo = ChPyNetSendPack.tagMCRoleFamilyMember()
            memInfo.PlayerID = member.GetPlayerID()
            memInfo.JoinTime = member.GetJoinTime()
            memInfo.Name = member.GetPlayerName()
            memInfo.NameLen = len(memInfo.Name)
            memInfo.LV = member.GetLV()
            memInfo.Job = member.GetJob()
            memInfo.RealmLV = member.GetRealmLV()
            memInfo.Face = member.GetFace()
            memInfo.FacePic = member.GetFacePic()
            memInfo.TitleID = member.GetTitleID()
            memInfo.FightPower = member.GetFightPower()
            memInfo.FightPowerEx = member.GetFightPowerEx()
            memInfo.FmLV = member.GetFmLV()
            memInfo.ServerID = member.GetServerID()
            memInfo.ContribTotal = member.GetContribTotal()
            memInfo.ContribDay = member.GetContribDay()
            memInfo.DonateCntTotal = member.GetDonateCntTotal()
            memInfo.DonateCntDay = member.GetDonateCntDay()
            memInfo.OffTime = member.GetOffTime()
            clientPack.MemberList.append(memInfo)
    clientPack.MemberCount = len(clientPack.MemberList)
    clientPack.Extra1 = curFamily.GetExtra1()
    return clientPack
def GetPack_FamilyDel(delPlayerID, playerName, delType=0):
    # @param delType: 0-踢出;1-主动退出
    clientPack = ChPyNetSendPack.tagSCFamilyMemDel()
    clientPack.Type = delType
    clientPack.PlayerID = delPlayerID
    clientPack.Name = playerName
    clientPack.NameLen = len(clientPack.Name)
    return clientPack
#// A6 01 向玩家申请加入家族 #tagCMRequestJoinFamilyByPlayer
#
#struct    tagCMRequestJoinFamilyByPlayer
#{
#    tagHead        Head;
#    DWORD        TagPlayerID;    //目标家族玩家ID
#};
def OnRequestJoinFamilyByPlayer(index, clientData, tick):
    #屏蔽,默认只使用 A6 02
    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    #tagPlayerID = clientData.TagPlayerID
    #tagPlayer = GameWorld.GetPlayerManager().FindPlayerByID(tagPlayerID)
    #if not tagPlayer:
    #    GameWorld.DebugLog("对方不在线! tagPlayerID=%s" % tagPlayerID)
    #    return
    #tagFamilyID = tagPlayer.GetFamilyID()
    #if tagFamilyID <= 0:
    #    GameWorld.DebugLog("对方没有家族! tagPlayerID=%s" % tagPlayerID)
    #    return
    #RequestJoinTagFamily(curPlayer, tagFamilyID)
    return
#// A6 02 申请加入家族#tagCMRequesJoinFamily
#
#struct    tagCMRequesJoinFamily
#{
#    tagHead        Head;
#    BYTE        Type;        //申请类型,0-申请;1-撤销
#    DWORD        TagFamilyID;    //目标家族ID,申请时为0代表一键申请家族任意家族
#};
def OnRequesJoinFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    requestType = clientData.Type
    if requestType == 0:
        if CheckInJoinCD(curPlayer):
            return
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnRequesJoinFamily", True)
    return
def __OnRequesJoinFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    tagFamilyID = clientData.TagFamilyID  # 申请进入的家族ID
    requestType = clientData.Type   # 申请类型(申请/撤销)
    GameWorld.DebugLog("__OnRequesJoinFamily: tagFamilyID=%s,requestType=%s" % (tagFamilyID, requestType))
    # 申请加入
    if requestType == 0:
        if not tagFamilyID:
            AutoJoinFamily(crossPlayer)
        else:
            RequestJoinTagFamily(crossPlayer, tagFamilyID)
    # 撤销申请
    elif requestType == 1:
        CancelJoinTagFamily(crossPlayer, tagFamilyID)
    return True
def __OnRequesJoinFamily_Ret(curPlayer, clientData, isOK):
    requestType = clientData.Type   # 申请类型(申请/撤销)
    # 申请加入
    if requestType == 0:
        PlayerTask.AddTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily, 1)
        
    return
def CheckInJoinCD(curPlayer):
    ## 检查是否加入公会CD中
    leaveFamilyTime = PlayerControl.GetLeaveFamilyTimeEx(curPlayer)
    if not leaveFamilyTime:
        return False
    leaveCnt, kickedCnt, lastVoluntarily = PlayerControl.GetLeaveFamilyInfo(curPlayer)
    joinCDMinute = 0
    if lastVoluntarily:
        if leaveCnt <= 0:
            return False
        joinCDMinuteList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 1)
        if joinCDMinuteList:
            joinCDMinute = joinCDMinuteList[leaveCnt - 1] if len(joinCDMinuteList) >= leaveCnt else joinCDMinuteList[-1]
    else:
        if kickedCnt <= 0:
            return False
        joinCDMinuteList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 2)
        if joinCDMinuteList:
            joinCDMinute = joinCDMinuteList[kickedCnt - 1] if len(joinCDMinuteList) >= kickedCnt else joinCDMinuteList[-1]
    if joinCDMinute:
        cdTimes = joinCDMinute * 60
        passTimes = int(time.time()) - leaveFamilyTime
        if passTimes < cdTimes:
            GameWorld.DebugLog("加入公会CD中: leaveCnt=%s,kickedCnt=%s,lastVoluntarily=%s,leaveFamilyTime=%s(%s),passTimes=%s < %s"
                   % (leaveCnt, kickedCnt, lastVoluntarily, leaveFamilyTime, GameWorld.ChangeTimeNumToStr(leaveFamilyTime), passTimes, cdTimes))
            return True
    return False
## 通知GameServer增加家族相关信息值
# @param curPlayer 玩家实例
# @param infoDict 信息字典
# @return
def Send_GameServer_PyAddFamilyInfoValue(curPlayer, infoDict={}):
    if not isinstance(infoDict, dict) or not infoDict:
        GameWorld.DebugLog("Send_GameServer_PyAddFamilyInfoValue infoDict=%s err!" % str(infoDict))
def AutoJoinFamily(crossPlayer):
    if crossPlayer.GetFamilyID():
        return
    playerID = crossPlayer.GetPlayerID()
    realmLV = crossPlayer.GetRealmLV()
    GameWorld.DebugLog("玩家一键自动加入家族! realmLV=%s" % realmLV, playerID)
    #if CheckInJoinCD(curPlayer):
    #    return
    mainServerID = crossPlayer.GetMainServerID()
    familyMgr = DBDataMgr.GetFamilyMgr()
    zoneID = familyMgr.GetZoneIDInThisServer(mainServerID)
    if zoneID < 0:
        return
    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
    indexList = range(zoneMgr.GetCount())
    random.shuffle(indexList) #打乱顺序
    for index in indexList:
        family = zoneMgr.GetAt(index)
        if not family:
            continue
        #familyID = family.GetID()
        lvMin = family.GetJoinLVMin()
        if lvMin and realmLV < lvMin:
            #GameWorld.DebugLog("    官职不足的不处理! familyID=%s,lvMin=%s" % (familyID, lvMin), playerID)
            continue
        if family.GetJoinReview():
            #GameWorld.DebugLog("    需要审核的不处理! familyID=%s" % familyID, playerID)
            continue
        MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
        if family.GetCount() >= MemberMax:
            #GameWorld.DebugLog("    成员已满的不处理! familyID=%s" % familyID, playerID)
            continue
        #直接加入
        DoPlayerJionFamily(family, playerID, crossPlayer)
        return
    
    sendMsg = '%s'%(infoDict)
    #GameWorld.DebugLog("Send_GameServer_PyAddFamilyInfoValue sendMsg=%s" % sendMsg)
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, 'PyAddFamilyInfoValue', sendMsg, len(sendMsg))
    # 可再扩展自动请求,暂时不处理
    GameWorld.DebugLog("没有可自动进入的公会!")
    CrossPlayer.NotifyCode(crossPlayer, "QuickEnterFamilyFail")
    return
#---------------------------------------------------------------------
## 添加家族事件记录
def AddFamilyEventNote(curPlayer, eventType, valueList):
    ##同步记录到GameServer, eventType默认为value1
    sendMsg = cPickle.dumps([eventType] + valueList, 2)
    curPlayer.GameServer_QueryPlayerByID(ChConfig.queryType_AddFamilyAction, ShareDefine.Def_ActionType_FamilyEvent, '', sendMsg, len(sendMsg))
    GameWorld.DebugLog("AddFamilyEventNote sendMsg=%s" % sendMsg)
    return
## --------------------------------- 仙盟活跃 -----------------------------------
def OnWeekEx(curPlayer):
    #重置
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
    for actionid in ShareDefine.FamilyActiveIDList:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
    SyncFamilyActivityInfo(curPlayer)
    return
## 领取仙盟活跃度奖励
#  @param curPlayer
#  @param awardIndex
#  @return None
def GetFamilyActivityAward(curPlayer, awardIndex):
    return
    familyActiveList = IpyGameDataPY.GetFuncEvalCfg('FamilyActive')
    familyActiveAwardList = IpyGameDataPY.GetFuncEvalCfg('FamilyActive', 2)
    if awardIndex < 0 or awardIndex >= len(familyActiveList):
        GameWorld.ErrLog("活跃度奖励索引不合法,index=%s,Len=%s" % (awardIndex, len(familyActiveList)))
        return
    # 判断是否已领取
    getAwardRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
    if getAwardRecord & pow(2, awardIndex):
        GameWorld.Log("已经领取过该活跃度奖励,index=%s" % (awardIndex))
        return
    needActivity = familyActiveList[awardIndex]
    totalActivity = __GetTotalFamilyActivity(curPlayer)#总活跃度
    # 判断活跃度
    if totalActivity < needActivity:
        GameWorld.Log("领奖活跃度不足,index=%s,needActivity=%s,totalActivity=%s"
                      % (awardIndex, needActivity, totalActivity))
        return
    activityValue = familyActiveAwardList[awardIndex]
    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, activityValue)
    # 更新已领取成功标记
    updAwardRecord = getAwardRecord | (1 << awardIndex)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityAwardRecord, updAwardRecord)
    SyncFamilyActivityInfo(curPlayer)
    GameWorld.DebugLog("领取仙盟活跃度奖励OK! index=%s,needActivity=%s,totalActivity=%s,awardRecord=%s"
                       % (awardIndex, needActivity, totalActivity, updAwardRecord))
    return
def AddFamilyActivity(curPlayer, actionid, addCnt=1):
    return
    ipyData = IpyGameDataPY.GetIpyGameData('FamilyActivity', actionid)
def GetFamilySetting(familyLV, fieldName):
    ## 获取公会等级表对应字段值
    if not fieldName:
        return 0
    ipyData = IpyGameDataPY.GetIpyGameData("Family", familyLV)
    if not ipyData:
        return 0
    attrName = "Get%s" % fieldName
    if not hasattr(ipyData, attrName):
        return 0
    return getattr(ipyData, attrName)()
def RequestJoinTagFamily(crossPlayer, familyID):
    ## 申请加入
    #if CheckInJoinCD(curPlayer):
    #    return
    playerID = crossPlayer.GetPlayerID()
    if crossPlayer.GetFamilyID():
        GameWorld.DebugLog('已经有公会不能再申请加入! familyID=%s' % crossPlayer.GetFamilyID(), playerID)
        return
    if not GameFuncComm.GetFuncCanUse(curPlayer, ipyData.GetUnLockFuncID()):
    familyMgr = DBDataMgr.GetFamilyMgr()
    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
    if playerID in reqFamilyIDList:
        GameWorld.DebugLog('已经在申请加入公会列表中! familyID=%s' % familyID, playerID)
        return
    finishCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
    maxCnt = ipyData.GetTotalActivityTime()
    if finishCnt >= maxCnt:
    maxReqFamilyCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 2)
    if len(reqFamilyIDList) >= maxReqFamilyCnt:
        GameWorld.DebugLog('已经达到最大申请加入公会数! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
        return
    newCnt = min(maxCnt, finishCnt+addCnt)
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, newCnt)
    SyncFamilyActivityInfo(curPlayer, actionid)
    GameWorld.DebugLog('    仙盟活跃完成次数 actionid=%s,newCnt=%s'%(actionid, newCnt))
    tagFamily = familyMgr.FindFamily(familyID)
    if not tagFamily:
        return
    lvMin = tagFamily.GetJoinLVMin()
    if crossPlayer.GetRealmLV() < lvMin:
        GameWorld.DebugLog('官职未达到该公会加入最低限制! realmLV=%s < %s' % (crossPlayer.GetRealmLV(), lvMin), playerID)
        return
    # 需要审核,满员后端不限制申请,由前端自行决定是否可申请
    if tagFamily.GetJoinReview():
        reqPlayerIDDict = tagFamily.GetReqJoinPlayerInfo()
        if playerID not in reqPlayerIDDict:
            maxReqPlayerCnt = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 1)
            if len(reqPlayerIDDict) >= maxReqPlayerCnt:
                GameWorld.DebugLog('目标公会申请加入数已满! %s, %s' % (len(reqFamilyIDList), reqFamilyIDList), playerID)
                CrossPlayer.NotifyCode(crossPlayer, "jiazu_pan_141056")
                return
        tagFamily.AddReqJoinPlayerID(playerID)
        # 广播给有招人权限的
        SendFamilyReqJoinInfo(familyID)
        #jiazu_pan_500807:申请入帮成功!请等待帮会管理人员审批!
        CrossPlayer.NotifyCode(crossPlayer, "jiazu_pan_500807")
        Sync_RequestAddFamilyInfo(crossPlayer)
        return
    # 不需要审核,自动加入
    memberMax = GetFamilySetting(tagFamily.GetLV(), "MemberMax")
    if tagFamily.GetCount() >= memberMax:
        GameWorld.DebugLog('目标公会成员已满! familyLV=%s,memberMax=%s' % (tagFamily.GetLV(), memberMax), playerID)
        return
    DoPlayerJionFamily(tagFamily, playerID, crossPlayer)
    return
def __GetTotalFamilyActivity(curPlayer):
    ##获取总仙盟活跃度
    totalPoint = 0
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for i in xrange(ipyDataMgr.GetFamilyActivityCount()):
        ipyData = ipyDataMgr.GetFamilyActivityByIndex(i)
        actionid = ipyData.GetID()
        curTimes = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
        totalPoint += curTimes/ipyData.GetSingleTimes()*ipyData.GetSingleActiveValue()
    return totalPoint
def SyncFamilyActivityInfo(curPlayer, syncActionid=-1):
    return
    #通知活跃领取情况
    syncActionIDList = ShareDefine.FamilyActiveIDList if syncActionid==-1 else [syncActionid]
    sendPack = ChPyNetSendPack.tagMCFamilyActivityInfo()
    sendPack.Clear()
    sendPack.AwardRecord = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityAwardRecord, 0)
    sendPack.InfoList = []
    for actionid in syncActionIDList:
        activityInfo = ChPyNetSendPack.tagMCFamilyActionCnt()
        activityInfo.ActionID = actionid
        activityInfo.FinishCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_FamilyActivityFinishCnt%actionid, 0)
        sendPack.InfoList.append(activityInfo)
    sendPack.Count = len(sendPack.InfoList)
    NetPackCommon.SendFakePack(curPlayer, sendPack)
def CancelJoinTagFamily(crossPlayer, familyID):
    # 撤销申请
    familyMgr = DBDataMgr.GetFamilyMgr()
    playerID = crossPlayer.GetPlayerID()
    tagFamily = familyMgr.FindFamily(familyID)
    if tagFamily:
        tagFamily.DelReqJoinPlayerID(playerID)
    familyMgr.DelPlayerReqJoinFamilyID(playerID, familyID)
    SendFamilyReqJoinInfo(familyID)
    Sync_RequestAddFamilyInfo(crossPlayer)
    return
#=================仙盟改名======================
def Sync_RequestAddFamilyInfo(crossPlayer, isForce=True):
    ## 通知当前申请加入的哪些家族
    playerID = crossPlayer.GetPlayerID()
    familyMgr = DBDataMgr.GetFamilyMgr()
    reqFamilyIDList = familyMgr.GetPlayerReqJoinFamilyIDList(playerID)
    # 非强制通知时没有申请记录的可不通知,如登录
    if not isForce and not reqFamilyIDList:
        return
    clientPack = ChPyNetSendPack.tagMCNotifyRequestJoinFamilyInfo()
    clientPack.Clear()
    clientPack.RequestJoinFamilyIDList = reqFamilyIDList
    clientPack.RequestCount = len(clientPack.RequestJoinFamilyIDList)
    CrossPlayer.SendFakePack(crossPlayer, clientPack)
    return
def IsFamilyNeedViewPlayer(playerID):
    ## 公会功能中查看玩家是否需要用到的
    # 公会成员已存储成员信息,所以成员不用判断,仅判断其他即可
    # 是否有请求加入某个公会
    familyMgr = DBDataMgr.GetFamilyMgr()
    if familyMgr.GetPlayerReqJoinFamilyIDList(playerID):
        return True
    return False
def SendFamilyReqJoinInfo(familyID):
    ## 广播给公会有招人权限的
    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyReqJoinInfo(familyID), None, GetFamilyMemberHasPow, FamilyPowerID_Call)
    return
def GetPack_FamilyReqJoinInfo(familyID):
    ## 获取 // A5 22 家族申请加入的玩家信息 #tagMCFamilyReqJoinInfo
    familyMgr = DBDataMgr.GetFamilyMgr()
    curFamily = familyMgr.FindFamily(familyID)
    if not curFamily:
        return
    reqPlayerIDDict = curFamily.GetReqJoinPlayerInfo()
    #没人申请也要通知
    #if not reqPlayerIDDict:
    #    return
    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
    clientPack = ChPyNetSendPack.tagMCFamilyReqJoinInfo()
    clientPack.ReqJoinList = []
    for playerID, reqTime in reqPlayerIDDict.items():
        crossPlayer = crossPlayerMgr.FindCrossPlayer(playerID)
        reqInfo = ChPyNetSendPack.tagMCFamilyReqJoinPlayer()
        reqInfo.PlayerID = playerID
        reqInfo.ReqTime = reqTime
        reqInfo.IsOnLine = 1 if crossPlayer else 0
        viewCache = PlayerViewCache.FindViewCache(playerID)
        if viewCache:
            reqInfo.Name = viewCache.GetPlayerName()
            reqInfo.NameLen = len(reqInfo.Name)
            reqInfo.LV = viewCache.GetLV()
            reqInfo.Job = viewCache.GetJob()
            reqInfo.RealmLV = viewCache.GetRealmLV()
            reqInfo.Face = viewCache.GetFace()
            reqInfo.FacePic = viewCache.GetFacePic()
            reqInfo.TitleID = viewCache.GetTitleID()
            reqInfo.FightPower = viewCache.GetFightPower()
            reqInfo.FightPowerEx = viewCache.GetFightPowerEx()
            reqInfo.ServerID = viewCache.GetServerID()
        clientPack.ReqJoinList.append(reqInfo)
        if len(clientPack.ReqJoinList) >= 100:
            break
    clientPack.ReqCnt = len(clientPack.ReqJoinList)
    return clientPack
#// A6 21 审核请求加入家族 #tagCMJoinFamilyReply
#
#struct tagCMJoinFamilyReply
#{
#    tagHead    Head;
#    DWORD    TagPlayerID;    //被审核玩家ID 0则代表全部
#    BYTE    IsOK;        //是否同意其加入
#};
def OnJoinFamilyReply(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnJoinFamilyReply")
    return
def __OnJoinFamilyReply(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    tagPlayerID = clientData.TagPlayerID
    isOK = clientData.IsOK
    GameWorld.DebugLog("__OnJoinFamilyReply: tagPlayerID=%s,isOK=%s" % (tagPlayerID, isOK))
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
        GameWorld.DebugLog("没有招人权限,无法审核人员入盟!", playerID)
        #PlayerControl.NotifyCode(curPlayer, "XW_JZ_InviteErr_Popedom")
        return
    GameWorld.DebugLog("审核入盟申请: tagPlayerID=%s,familyID=%s,isOK=%s" % (tagPlayerID, familyID, isOK), playerID)
    reqPlayerIDDict = family.GetReqJoinPlayerInfo()
    tagPlayerIDList = reqPlayerIDDict.keys()
    if tagPlayerID:
        if tagPlayerID not in reqPlayerIDDict:
            GameWorld.DebugLog("不存在该申请人员! tagPlayerID=%s" % tagPlayerID)
            return
        tagPlayerIDList = [tagPlayerID]
    if not tagPlayerIDList:
        GameWorld.DebugLog("没有申请人员!")
        return
    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
    # 拒绝
    if not isOK:
        for tagPlayerID in tagPlayerIDList:
            family.DelReqJoinPlayerID(tagPlayerID)
            tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
            if not tagCrossPlayer:
                continue
            Sync_RequestAddFamilyInfo(tagCrossPlayer)
            #jiazu_pan_592934:{%S}拒绝了您的入帮申请
            CrossPlayer.NotifyCode(tagCrossPlayer, "jiazu_pan_592934", [family.GetName()])
        SendFamilyReqJoinInfo(familyID)
        return
    # 处理同意
    offlinePlayerCanJoin = IpyGameDataPY.GetFuncCfg("FamilyReqJoin", 3)
    MemberMax = GetFamilySetting(family.GetLV(), "MemberMax")
    joinOKPlayerIDList = []
    for tagPlayerID in tagPlayerIDList:
        if family.GetCount() >= MemberMax:
            CrossPlayer.NotifyCode(crossPlayer, "jiazu_lhs_202580")
            break
        tagCrossPlayer = crossPlayerMgr.FindCrossPlayer(tagPlayerID)
        #申请目标不在线
        if not tagCrossPlayer:
            if not offlinePlayerCanJoin:
                GameWorld.DebugLog("离线玩家无法加入公会! tagPlayerID=%s" % tagPlayerID, playerID)
                CrossPlayer.NotifyCode(crossPlayer, "jiazu_hwj35_367906")
                continue
        family.DelReqJoinPlayerID(tagPlayerID) # 以下只要操作的都删除
        if family.FindMember(tagPlayerID):
            GameWorld.DebugLog("已经是本盟成员! tagPlayerID=%s" % tagPlayerID, playerID)
            CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_InviteErr_Repeat")
            continue
        tagFamilyID = familyMgr.GetPlayerFamilyID(tagPlayerID)
        if tagFamilyID:
            GameWorld.DebugLog("已经加入其他公会! tagPlayerID=%s,tagFamilyID=%s" % (tagPlayerID, tagFamilyID), playerID)
            CrossPlayer.NotifyCode(crossPlayer, "XW_JZ_InviteErr_Repeat")
            continue
        DoPlayerJionFamily(family, tagPlayerID, tagCrossPlayer, broadcastFamilyChange=False)
        joinOKPlayerIDList.append(tagPlayerID)
    #if not joinOKPlayerIDList:
    #    return
    SendFamilyReqJoinInfo(familyID)
    if joinOKPlayerIDList:
        Broadcast_FamilyInfo(familyID, changeMemIDList=joinOKPlayerIDList, excludeIDList=joinOKPlayerIDList) # 审核
    return
#// A6 22 修改收人方式 #tagCMChangeFamilyJoin
#
#struct    tagCMChangeFamilyJoin
#{
#    tagHead         Head;
#    BYTE        JoinReview;    //成员加入是否需要审核,默认0自动加入
#    WORD        JoinLVMin;    //限制最低可加入的玩家等级
#};
def OnChangeFamilyJoin(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyJoin")
    return
def __OnChangeFamilyJoin(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    joinReview = clientData.JoinReview
    joinLVMin = clientData.JoinLVMin # 官职
    GameWorld.DebugLog("__OnChangeFamilyJoin: joinReview=%s,joinLVMin=%s" % (joinReview, joinLVMin))
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Call):
        GameWorld.DebugLog("没有招人权限", playerID)
        return
    GameWorld.DebugLog("修改招人设置: familyID=%s,joinReview=%s,joinLVMin=%s" % (familyID, joinReview, joinLVMin), playerID)
    family.SetJoinReview(joinReview)
    family.SetJoinLVMin(joinLVMin)
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改招人
    return
#// A6 23 修改家族公告 #tagCMChangeFamilyBroadcast
#
#struct    tagCMChangeFamilyBroadcast
#{
#    tagHead        Head;
#    char        Msg[200];
#};
def OnChangeFamilyBroadcast(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyBroadcast")
    return
def __OnChangeFamilyBroadcast(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    broadcast = clientData.Msg
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Broadcast):
        GameWorld.DebugLog("没有修改公告权限", playerID)
        return
    family.SetBroadcast(broadcast)
    GameWorld.DebugLog('更改公会公告: Family=%s,公告=%s' % (GameWorld.CodeToGbk(family.GetName()), GameWorld.CodeToGbk(broadcast)), playerID)
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 修改公告
    return
#// A6 24 修改家族徽章 #tagCMChangeFamilyEmblem
#
#struct    tagCMChangeFamilyEmblem
#{
#    tagHead        Head;
#    BYTE        EmblemID;    // 更换的徽章ID
#    char        EmblemWord[3];    // 徽章文字
#};
def OnChangeFamilyEmblem(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyEmblem")
    return
def __OnChangeFamilyEmblem(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    changeEmblemID = clientData.EmblemID
    emblemWord = clientData.EmblemWord
    PlayerFamilyEmblem.OnChangeFamilyEmblem(crossPlayer, changeEmblemID, emblemWord)
    return
#// A6 25 修改家族成员职位 #tagCMChangeFamilyMemLV
#
#struct    tagCMChangeFamilyMemLV
#{
#    tagHead        Head;
#    DWORD        PlayerID; // 目标成员ID
#    BYTE        FmLV;  // 变更为xx职位
#};
def OnChangeFamilyMemLV(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnChangeFamilyMemLV")
    return
def __OnChangeFamilyMemLV(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    OnChangeFamilyMemberLV(crossPlayer, clientData.PlayerID, clientData.FmLV)
    return
def OnChangeFamilyMemberLV(crossPlayer, tagID, changeFmlv, isGMOP=False):
    '''变更成员职位
    @param curPlayer: 操作的玩家
    @param tagID: 目标成员ID
    @param changeFmlv: 修改为xx职位
    @param isGMOP: 是否是GM后台发起的,如果是GM发起的,一般curPlayer传入的为目标成员ID实例
    '''
    if not crossPlayer:
        return
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    if changeFmlv < 0 or changeFmlv > IPY_PlayerDefine.fmlLeader:
        GameWorld.DebugLog("不存在该职位等级! changeFmlv=%s" % changeFmlv)
        return
    # 非GM操作的需检查权限
    if not isGMOP:
        if not GetFamilyMemberHasPow(curMember, FamilyPowerID_ChangeFmlv):
            return
        if playerID == tagID:
            GameWorld.DebugLog("不能任免自己的家族职位", playerID)
            return
    tagMember = family.FindMember(tagID)
    if tagMember == None:
        GameWorld.DebugLog("更改家族成员职位时目标成员不存在! tagID=%s" % tagID, playerID)
        return
    if not isGMOP:
        if curMember.GetFmLV() != IPY_PlayerDefine.fmlLeader:
            if changeFmlv >= curMember.GetFmLV():
                GameWorld.DebugLog("修改的职位不能比自己高或平级! changeFmlv=%s" % changeFmlv, playerID)
                return
            if tagMember.GetFmLV() >= curMember.GetFmLV():
                GameWorld.DebugLog("修改的目标成员职位不能比自己高或平级! tagFmlv=%s" % tagMember.GetFmLV(), playerID)
                return
    changeMemIDList = [tagID]
    if changeFmlv == IPY_PlayerDefine.fmlLeader:
        ChangeFamilyLeader(family, tagMember)
        changeMemIDList.append(playerID)
    else:
        fmLVMemCnt = 0
        for index in range(family.GetCount()):
            familyMember = family.GetAt(index)
            if familyMember.GetFmLV() != changeFmlv:
                continue
            fmLVMemCnt += 1
        maxCnt = GetFamilySetting(family.GetLV(), Def_FmlSetAttrName.get(changeFmlv, ""))
        if fmLVMemCnt >= maxCnt:
            # jiazu_hwj35_272921 改为 jiazu_chenxin_31379
            CrossPlayer.NotifyCode(crossPlayer, "jiazu_chenxin_31379")
            GameWorld.DebugLog("目前该职位的人数已经达到上限! changeFmlv=%s,fmLVMemCnt=%s >= %s" % (changeFmlv, fmLVMemCnt, maxCnt))
            return
        ChangeFamilyMemberLv(tagMember, changeFmlv)
    if isGMOP:
        family.SetBroadcast("")
    Broadcast_FamilyInfo(familyID, changeMemIDList=changeMemIDList) # 修改职位
    return True
def ChangeFamilyLeader(family, newLeaderMem):
    ## 变更家族族长
    familyID = family.GetID()
    befLeaderID = family.GetLeaderID()
    newLeaderID = newLeaderMem.GetPlayerID()
    if befLeaderID == newLeaderID:
        return
    befLeaderMem = family.FindMember(befLeaderID)
    if befLeaderMem:
        #把原族长降为普通成员
        ChangeFamilyMemberLv(befLeaderMem, IPY_PlayerDefine.fmlMember)
    family.SetLeaderID(newLeaderID)
    ChangeFamilyMemberLv(newLeaderMem, IPY_PlayerDefine.fmlLeader)
    GameWorld.Log("家族设置新族长! familyID=%s,newLeaderID=%s,befLeaderID=%s" % (familyID, newLeaderID, befLeaderID))
    return
def ChangeFamilyMemberLv(tagMember, changeFamilyLV):
    ## 修改成员职位,只做修改逻辑,不做验证,验证由各调用入口自行验证
    familyID = tagMember.GetFamilyID()
    tagID = tagMember.GetPlayerID()
    memName = tagMember.GetPlayerName()
    befFamilyLV = tagMember.GetFmLV()
    tagMember.SetFmLV(changeFamilyLV)
    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagID)
    if tagCrossPlayer:
        MapServer_FamilyRefresh(tagCrossPlayer, familyID) # 修改职位
        if GetFamilyMemberHasPow(tagMember, FamilyPowerID_Call):
            CrossPlayer.SendFakePack(tagCrossPlayer, GetPack_FamilyReqJoinInfo(familyID))
    # 记录家族事件记录信息
    tick = GameWorld.GetGameWorld().GetTick()
    AddFamilyActionNote(memName, familyID, ShareDefine.Def_ActionType_FamilyEvent,
                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_FMLV, changeFamilyLV, befFamilyLV], tick)
    #xx被任命为xx
    CrossPlayer.FamilyNotify(familyID, "XW_JZ_AppointFamily", [memName, changeFamilyLV])
    return
def __AutoChangeLeader(curFamily):
    ## 自动传位
    leaderID = curFamily.GetLeaderID()
    leaderMem = curFamily.FindMember(leaderID)
    if not leaderMem:
        return
    offTime = leaderMem.GetOffTime()
    if not offTime:
        return
    familyID = curFamily.GetID()
    curTime = int(time.time())
    passTime = curTime - offTime
    passHours = passTime / 3600.0
    needHours = IpyGameDataPY.GetFuncCfg("FamilyLeaderAutoChange", 1)
    if passHours < needHours:
        GameWorld.DebugLogEx("盟主离线未超过限制小时,不处理自动传位!familyID=%s,leaderID=%s,offTime=%s,passHours=%s < %s",
                             familyID, leaderID, GameWorld.ChangeTimeNumToStr(offTime), passHours, needHours)
        return
    priorityHours = IpyGameDataPY.GetFuncCfg("FamilyLeaderAutoChange", 1) # 优先传给离线不超过x小时的成员,一样按优先级
    priorityList = []
    commList = []
    for i in range(0, curFamily.GetCount()):
        member = curFamily.GetAt(i)
        if member.GetFmLV() == IPY_PlayerDefine.fmlLeader:
            continue
        memOffTime = member.GetOffTime()
        if memOffTime:
            sortTime = memOffTime
            memPassTime = curTime - memOffTime
            memPassHours = memPassTime / 3600.0
        else:
            sortTime = curTime # 排序用的时间,越大越优先
            memPassTime = 0
            memPassHours = 0
        fmLV = member.GetFmLV() # ְλ
        contribTotal = member.GetContribTotal() # 总贡献
        commList.append([fmLV, sortTime, contribTotal, member])
        if priorityHours and memPassHours <= priorityHours:
            priorityList.append([fmLV, sortTime, contribTotal, member])
    if not priorityList and not commList:
        # 没有可传位的目标成员
        return
    toMember = None
    if priorityList:
        priorityList.sort(reverse=True)
        toMember = priorityList[0][-1]
    else:
        commList.sort(reverse=True)
        toMember = commList[0][-1]
    if not toMember:
        return
    newLeaderID = toMember.GetPlayerID()
    GameWorld.Log("公会自动传位: familyID=%s,leaderID=%s,offTime=%s,passHours=%s,newLeaderID=%s"
                  % (familyID, leaderID, GameWorld.ChangeTimeNumToStr(offTime), passHours, newLeaderID))
    ChangeFamilyLeader(curFamily, toMember)
    Broadcast_FamilyInfo(familyID, changeMemIDList=[leaderID, newLeaderID]) # 自动传位
    # 邮件通知
    toServerID = toMember.GetServerID()
    PlayerMail.SendMailByKey("FamilyLeaderAutoChange", newLeaderID, [], [curFamily.GetName()], toServerID=toServerID)
    return
#// A6 26 请求家族成员列表 #tagCMGetFamilyInfo
#
#struct    tagCMGetFamilyInfo
#{
#    tagHead        Head;
#};
def OnGetFamilyInfo(index, clientData, tick):
    #改为后端主动同步差异,不用再请求了
    #curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    #Sync_FamilyInfo(crossPlayer)
    return
#// A6 03 离开家族 #tagCMLeaveFamily
#
#struct    tagCMLeaveFamily
#{
#    tagHead        Head;
#};
def OnLeaveFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnMemLeaveFamily")
    return
def __OnMemLeaveFamily(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    familyLV = curMember.GetFmLV()  # ְλ
    if family.GetCount() > 1 and familyLV == IPY_PlayerDefine.fmlLeader:
        GameWorld.DebugLog("族长在成员人数大于1时不能直接退出家族", playerID)
        return
    # 功能限制退出公会
    # ...
    # 进出时间限制暂不做,等正式功能再补
    #PlayerControl.SetLeaveFamilyTime(curPlayer, updTime)
    # 执行退出
    GameWorld.DebugLog("离开家族! familyID=%s" % familyID, playerID)
    family.DeleteMember(playerID)
    AddFamilyActionNote(crossPlayer.GetPlayerName(), familyID, ShareDefine.Def_ActionType_FamilyEvent,
                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_Leave], tick)
    __DoPlayerLeaveFamilyByID(family, playerID, crossPlayer)
    MapServer_FamilyRefresh(crossPlayer, 0, 1) # 主动退出
    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyDel(playerID, crossPlayer.GetPlayerName(), 1))
    CrossPlayer.FamilyNotify(familyID, "XW_JZ_LeaveFamily", [crossPlayer.GetPlayerName()])
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 退出
    if family.GetCount() == 0:
        #玩家离开后, 家族没有人了 , 删除这个家族
        familyMgr.DelFamily(familyID)
        return
    return
#// A6 05 删除家族成员 #tagCMDeleteFamilyMember
#
#struct    tagCMDeleteFamilyMember
#{
#    tagHead        Head;
#    DWORD        MemberID;
#};
def OnDeleteFamilyMember(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnDeleteFamilyMember")
    return
def __OnDeleteFamilyMember(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    tagMemberID = clientData.MemberID
    GameWorld.DebugLog("__OnDeleteFamilyMember tagMemberID=%s" % tagMemberID)
    playerID = crossPlayer.GetPlayerID()
    if playerID == tagMemberID:
        return
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    if not GetFamilyMemberHasPow(curMember, FamilyPowerID_Kick):
        GameWorld.DebugLog("没有踢人权限!")
        return
    tagMember = family.FindMember(tagMemberID)
    if not tagMember:
        return
    curFmlv = curMember.GetFmLV()
    tagFmlv = tagMember.GetFmLV()
    if tagFmlv >= curFmlv:
        GameWorld.DebugLog("只能踢比自己职位低的成员! tagMemberID=%s,tagFmlv(%s) >= curFmlv(%s)" % (tagMemberID, tagFmlv, curFmlv), playerID)
        return
    # 功能限制踢人
    # ...
    tagPlayerName = tagMember.GetPlayerName()  # 被踢玩家名
    tagPlayerID = tagMember.GetPlayerID()  # 被踢玩家ID
    family.DeleteMember(tagPlayerID)
    AddFamilyActionNote(tagPlayerName, familyID, ShareDefine.Def_ActionType_FamilyEvent,
                        [ShareDefine.Def_FamilyActionEvent_MemberChange, ShareDefine.Def_FamilyMemberChange_KickOut], tick)
    #XW_JZ_LeaveFamily   <n color="0,190,255">{%S1%}</n><n color="255,255,0">退出了家族!</n>  25  -   -
    CrossPlayer.FamilyNotify(familyID, "XW_JZ_LeaveFamily", [tagPlayerName])
    #删除玩家
    tagCrossPlayer = CrossPlayer.GetCrossPlayerMgr().FindCrossPlayer(tagMemberID)
    __DoPlayerLeaveFamilyByID(family, tagPlayerID, tagCrossPlayer)
    if tagCrossPlayer:
        MapServer_FamilyRefresh(tagCrossPlayer, 0) # 被踢
        CrossPlayer.NotifyCode(tagCrossPlayer, "XW_JZ_LeaveFamilyKick", [curMember.GetPlayerName()])
    CrossPlayer.SendFakePackByFamily(familyID, GetPack_FamilyDel(tagMemberID, tagPlayerName, 0))
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 踢人
    return
def __DoPlayerLeaveFamilyByID(curFamily, leavePlayerID, crossPlayer=None):
    ## 有玩家离开家族处理,主要针对家族层级的,玩家个人的在 __OnLeaveFamily 处理
    PlayerFamilyTaofa.OnFamilyMemberLeave(curFamily, leavePlayerID)
    return
#// A6 11 家族改名 #tagCMRenameFamily
#
#struct tagCMRenameFamily
@@ -805,612 +1546,648 @@
#    BYTE        ItemIndex;  //改名物品在背包中的位置
#};
def UpdateFamilyName(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    if GameWorld.IsCrossServer():
        #itemuse_lzxkoy_0:不可在跨服环境下使用改名功能。
        PlayerControl.NotifyCode(curPlayer, "itemuse_lzxkoy_0")
        return
    if not curPlayer.GetFamilyID():
        return
    if curPlayer.GetDictByKey(ChConfig.Def_Player_Dict_UpdateFamilyName):
        #正在改名中
        GameWorld.Log("仙盟已经在改名中...", curPlayer.GetID())
        return
    #新名字
    newName = clientData.NewName
    itemIndex = clientData.ItemIndex
    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
    curItem = itemPack.GetAt(itemIndex)
    if not ItemCommon.CheckItemCanUse(curItem):
    if not CheckInputFamilyName(curPlayer, newName):
        GameWorld.DebugLog("名字验证不通过")
        return
    if curItem.GetType() != ChConfig.Def_ItemType_ChangeFamilyName:
    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
    if moneyType and moneyValue and not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
        return
    #改名物品在背包的位置
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex, itemIndex)
    #设置正在改名中...
    curPlayer.SetDict(ChConfig.Def_Player_Dict_UpdateFamilyName, 1)
    #通知GameServer正式改名
    newNameMsg = str(newName)
    GameWorld.Log("UpdateFamilyName 通知GameServer正式改名为:%s"%newNameMsg, curPlayer.GetID())
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(curPlayer.GetID(), 0, 0, 'UpdateFamilyName', newNameMsg, len(newNameMsg))
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__UpdateFamilyName", True, 20)
    return
## 扣除改名消耗
#  @param curPlayer: 玩家实例
#  @return: None
def PayUpdateFamilyNameCost(curPlayer):
    itemIndex = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex)
    #扣除物品
    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
    curItem = itemPack.GetAt(itemIndex)
    if curItem:
        ItemCommon.DelItem(curPlayer, curItem, 1, True, 'UpdateFamilyName')
def __UpdateFamilyName(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    newName = clientData.NewName
    #itemIndex = clientData.ItemIndex
    
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_UpdateFamilyNameItemIndex, 0)
    return
def GetFamilyDayAward(curPlayer):
    ##领取仙盟每日奖励 2小时脱机挂时间
    if not curPlayer.GetFamilyID():
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    hasGot = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDayAward)
    if hasGot:
        GameWorld.DebugLog('领取仙盟每日奖励 当日已领取!')
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(familyID)
    if not family:
        return
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDayAward, 1)
    #addTime = IpyGameDataPY.GetFuncCfg('FamilyDayAward')
    #PlayerTJG.AddTJGTime(curPlayer, addTime)
    #֪ͨ
    Sync_FamilyDayRewardState(curPlayer)
    #PlayerControl.NotifyCode(curPlayer, 'OfflinePlugSuccess', [addTime/60/60])
    curMember = family.FindMember(playerID)
    if not curMember:
        return
    
    return
    if curMember.GetFmLV() != IPY_PlayerDefine.fmlLeader:
        GameWorld.DebugLog("非盟主不可改名!", playerID)
        return
    curTime = int(time.time())
    cdHours = IpyGameDataPY.GetFuncCfg("FamilyRename", 2)
    if cdHours:
        cdSeconds = cdHours * 3600
        lastRenameTime = GetRenameTime(family)
        if lastRenameTime and (curTime - lastRenameTime) < cdSeconds:
            GameWorld.DebugLog("公会改名CD中! lastRenameTime=%s,cdHours=%s" % (GameWorld.ChangeTimeNumToStr(lastRenameTime), cdHours))
            return
    # 验证重名
    if CheckFamilyNameExists(crossPlayer, newName, fromServerID):
        return
    moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyRename", 1)
    if moneyType and moneyValue:
        CrossPlayer.CostPlayerResources(crossPlayer, "FamilyRename", costMoneyDict={moneyType:moneyValue})
    family.SetName(newName)
    if cdHours:
        SetRenameTime(family, curTime)
    crossPlayerMgr = CrossPlayer.GetCrossPlayerMgr()
    for index in xrange(family.GetCount()):
        member = family.GetAt(index)
        memID = member.GetPlayerID()
        memCrossPlayer = crossPlayerMgr.FindCrossPlayer(memID)
        if not memCrossPlayer:
            continue
        MapServer_FamilyRefresh(memCrossPlayer, familyID) # 改名
        #player.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 改名
    return True
def Sync_FamilyDayRewardState(curPlayer):
    clientPack = ChPyNetSendPack.tagMCFamilyDayAward()
    clientPack.GetState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDayAward)
    clientPack.MoneyDonateCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyMoneyDonateCount)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
#// A6 06 家族兑换活跃令 #tagCMFamilyActivityExchange
#// A6 19 查看目标公会 #tagCSViewTagFamily
#
#struct    tagCMFamilyActivityExchange
#struct tagCSViewTagFamily
#{
#    tagHead        Head;
#    BYTE        Count;        //材料所在背包索引的数量
#    WORD        IndexList[Count];    //材料所在背包索引列表
#    DWORD        ItemIDList[Count];    //材料所在背包物品ID列表
#    DWORD        FamilyID;
#    DWORD        DataServerID;    //数据所在服务器ID
#};
## 家族兑换活跃令
#  @param playerIndex 玩家索引
#  @param clientData 客户端封包
#  @param tick 时间
#  @return None
def OnFamilyActivityExchange(index, clientData, tick):
def OnViewTagFamily(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    if not clientData.Count:
        return
    playerID = curPlayer.GetID()
    eatIndexList = clientData.IndexList
    eatItemIDList = clientData.ItemIDList
    givePoint = 0 #分解得到活跃令
    familyDonateDict = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 1, {})
    familyDonateSpecialDict = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 2, {})
    itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
    for i, index in enumerate(eatIndexList):
        eatItem = itemPack.GetAt(index)
        if not ItemCommon.CheckItemCanUse(eatItem):
            GameWorld.DebugLog("物品不可用,无法兑换!itemIndex=%s" % index)
            continue
        eatItemID = eatItem.GetItemTypeID()
        if eatItemID != eatItemIDList[i]:
            GameWorld.Log('家族兑换活跃令 发的物品ID不对应index=%s eatItemID=%s,ItemIDList[i]=%s' % (index, eatItemID, eatItemIDList[i]), playerID)
            continue
        #if eatItem.GetIsBind():
        #    GameWorld.DebugLog("装备已绑定,无法兑换!itemIndex=%s" % index)
        #    continue
        if eatItem.GetEndureReduceType():
            GameWorld.DebugLog("有时效耐久物品,无法兑换!itemIndex=%s" % index)
            continue
        if eatItemID in familyDonateSpecialDict:
            addPoint = familyDonateSpecialDict[eatItemID]
        else:
            if not ItemCommon.CheckItemIsEquip(eatItem):
                GameWorld.DebugLog("非装备,无法兑换!itemIndex=%s" % index)
                continue
            itemColor = eatItem.GetItemColor()
            if str(itemColor) not in familyDonateDict:
                continue
            isSuite = eatItem.GetSuiteID()
            addPoint = familyDonateDict[str(itemColor)][1 if isSuite else 0]
        itemCnt = eatItem.GetCount()
        givePoint += addPoint * itemCnt
        ItemCommon.DelItem(curPlayer, eatItem, itemCnt, True, ChConfig.ItemDel_FamilyStore, {'addPoint':addPoint}, True)
    playerID = curPlayer.GetPlayerID()
    tagFamilyID = clientData.FamilyID
    dataServerID = clientData.DataServerID
    
    if not givePoint:
        GameWorld.DebugLog("家族兑换活跃令,没有装备可兑换!")
    # 本服或主服是本服
    if not dataServerID or dataServerID == GameWorld.GetGameWorld().GetServerID():
        NetPackCommon.SendFakePack(curPlayer, GetTagFamilyInfoPack(tagFamilyID))
        return
    PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, givePoint)
    
    #通知结果
    packData = ChPyNetSendPack.tagMCFamilyActivityExchangeResult()
    packData.Clear()
    packData.Point = givePoint
    NetPackCommon.SendFakePack(curPlayer, packData)
    CrossMsg.SendToServer(ShareDefine.S2S_ViewTagFamily, {"tagFamilyID":tagFamilyID}, [dataServerID], ShareDefine.dirType_All, playerID)
    return
def S2S_ViewTagFamily(dataMsg, fromServerID, playerID):
    tagFamilyID = dataMsg["tagFamilyID"]
    CrossPlayer.SendFakePackByID(playerID, GetTagFamilyInfoPack(tagFamilyID), fromServerID)
    return
def GetTagFamilyInfoPack(tagFamilyID):
    familyMgr = DBDataMgr.GetFamilyMgr()
    family = familyMgr.FindFamily(tagFamilyID)
    if not family:
        GameWorld.ErrLog("本服数据找不到目标公会! tagFamilyID=%s" % tagFamilyID)
        return
    clientPack = ChPyNetSendPack.tagSCTagFamilyInfo()
    clientPack.FamilyID = family.GetID()
    clientPack.FamilyName = family.GetName()
    clientPack.FamilyNameLen = len(clientPack.FamilyName)
    clientPack.LeaderID = family.GetLeaderID()
    leaderMember = family.FindMember(clientPack.LeaderID)
    if leaderMember:
        clientPack.LeaderName = leaderMember.GetPlayerName()
        clientPack.LeaderNameLen = len(clientPack.LeaderName)
        clientPack.LeaderServerID = leaderMember.GetServerID()
    clientPack.FamilyLV = family.GetLV()
    clientPack.ServerID = family.GetServerID()
    clientPack.EmblemID = family.GetEmblemID()
    clientPack.EmblemWord = family.GetEmblemWord()
    clientPack.FightPower = family.GetFightPower()
    clientPack.FightPowerEx = family.GetFightPowerEx()
    clientPack.Broadcast = family.GetBroadcast()
    clientPack.BroadcastLen = len(clientPack.Broadcast)
    clientPack.MemberCount = family.GetCount()
    clientPack.DataServerID = GameWorld.GetGameWorld().GetServerID()
    return clientPack
#// A6 20 搜索家族列表 #tagCMViewFamilyPage
#
#struct    tagCMViewFamilyPage
#{
#    tagHead        Head;
#    BYTE        MsgLen;        //模糊搜索家族,如果输入为空,则为不限制该条件
#    char        Msg[MsgLen];    //size = MsgLen
#    BYTE        PageIndex;    //查询第X页索引,0~n
#    BYTE        ShowCount;    //每页数量,前端可自行指定,最大50
#};
def OnViewFamilyPage(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnViewFamilyPage")
    return
def __OnViewFamilyPage(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    msg = clientData.Msg
    pageIndex = clientData.PageIndex
    showCount = min(clientData.ShowCount, 50)
    familyMgr = DBDataMgr.GetFamilyMgr()
    zoneID = familyMgr.GetZoneIDInThisServer(fromServerID)
    if zoneID < 0:
        GameWorld.ErrLog("找不到服务器ID在本服中的公会分区! fromServerID=%s" % fromServerID)
        return
    playerFamilyID = crossPlayer.GetFamilyID()
    zoneMgr = familyMgr.GetZoneFamilyMgr(zoneID)
    zoneMgr.Sort(True)
    familyCount = zoneMgr.GetCount()
    totalPage = 0
    if not msg:
        startIndex = pageIndex * showCount
        endIndex = startIndex + showCount - 1
        if familyCount > 0:
            totalPage = GameWorld.GetIntUpper(familyCount, showCount)
    # 有指定搜索内容的后端固定返回单页
    else:
        pageIndex = 0
        showCount = 20
        totalPage = 1
        startIndex = 0
        endIndex = familyCount - 1
    clientPack = ChPyNetSendPack.tagMCFamilyViewList()
    clientPack.Msg = msg
    clientPack.MsgLen = len(clientPack.Msg)
    clientPack.PageIndex = pageIndex
    clientPack.ShowCount = showCount
    clientPack.TotalPage = totalPage
    clientPack.Rank = zoneMgr.GetFamilyRank(playerFamilyID)
    clientPack.FamilyList = []
    for index in range(startIndex, endIndex + 1):
        if index >= familyCount:
            break
        family = zoneMgr.GetAt(index)
        if not family:
            continue
        if msg:
            if msg in family.GetName() or msg == str(family.GetID()):
                pass
            else:
                continue
        familyView = ChPyNetSendPack.tagMCFamilyView()
        familyView.Rank = index + 1
        familyView.FamilyID = family.GetID()
        familyView.FamilyName = family.GetName()
        familyView.FamilyNameLen = len(familyView.FamilyName)
        familyView.LeaderID = family.GetLeaderID()
        leaderMember = family.FindMember(familyView.LeaderID)
        familyView.LeaderName = leaderMember.GetPlayerName() if leaderMember else ""
        familyView.LeaderNameLen = len(familyView.LeaderName)
        familyView.LeaderServerID = leaderMember.GetServerID() if leaderMember else family.GetServerID()
        familyView.FamilyLV = family.GetLV()
        familyView.JoinReview = family.GetJoinReview()
        familyView.JoinLVMin = family.GetJoinLVMin()
        familyView.ServerID = family.GetServerID()
        familyView.EmblemID = family.GetEmblemID()
        familyView.EmblemWord = family.GetEmblemWord()
        familyView.FightPower = family.GetFightPower()
        familyView.FightPowerEx = family.GetFightPowerEx()
        familyView.MemberCount = family.GetCount()
        clientPack.FamilyList.append(familyView)
        clientPack.FamilyCount = len(clientPack.FamilyList)
        if clientPack.FamilyCount >= showCount:
            break
    CrossPlayer.SendFakePack(crossPlayer, clientPack)
    return
#// A6 12 家族捐献货币 #tagCMFamilyMoneyDonate
#
#struct     tagCMFamilyMoneyDonate
#{
#    tagHead        Head;
#    BYTE        MoneyType;    // 捐献货币类型
#    BYTE        DonateType;    // 捐献类型
#};
def OnFamilyMoneyDonate(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    moneyType = clientData.MoneyType
    playerID = curPlayer.GetPlayerID()
    donateType = clientData.DonateType
    
    dailyDonateCountMax = IpyGameDataPY.GetFuncCfg('FamilyDonate', 3)
    donateCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyMoneyDonateCount)
    if dailyDonateCountMax and donateCount >= dailyDonateCountMax:
        GameWorld.DebugLog("今日货币捐献次数已达上限. donateCount=%s" % donateCount, playerID)
    ipyData = IpyGameDataPY.GetIpyGameData("FamilyDonate", donateType)
    if not ipyData:
        return
    dailyCntMax = ipyData.GetDailyCnt()
    donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
    if donateCnt >= dailyCntMax:
        GameWorld.DebugLog("今日捐献次数已达上限! donateType=%s,donateCnt=%s >= %s" % (donateType, donateCnt, dailyCntMax), playerID)
        return
    
    donatePrizeInfo = IpyGameDataPY.GetFuncEvalCfg('FamilyDonate', 4, {})
    if str(moneyType) not in donatePrizeInfo:
        GameWorld.DebugLog("不存在该货币类型捐献: moneyType=%s" % moneyType, playerID)
    moneyType = ipyData.GetMoneyType()
    moneyValue = ipyData.GetMoneyValue()
    if not moneyType or not moneyValue:
        return
    needMoney, contribution, familyActivity = donatePrizeInfo[str(moneyType)]
    if not PlayerControl.PayMoney(curPlayer, moneyType, needMoney, "FamilyMoneyDonate"):
    if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
        return
    
    updDonateCount = donateCount + 1
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyMoneyDonateCount, updDonateCount)
    Sync_FamilyDayRewardState(curPlayer)
    GameWorld.DebugLog("货币捐献: moneyType=%s,needMoney=%s,updDonateCount=%s,contribution=%s,familyActivity=%s"
                       % (moneyType, needMoney, updDonateCount, contribution, familyActivity), playerID)
    if contribution > 0:
        AddPlayerFamilyActiveValue(curPlayer, contribution, True, ShareDefine.Def_AddFAVReason_FamilyDonateItem, True)
    if familyActivity > 0:
        PlayerControl.GiveMoney(curPlayer, ShareDefine.TYPE_Price_FamilyActivity, familyActivity)
    reqDataEx = {"donateCnt":donateCnt}
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyMoneyDonate", True, 20, reqDataEx=reqDataEx)
    return
##--------------------------------------- 仙盟事务 --------------------------------------------------
AffairState_None = 0 # 无
AffairState_Underway = 1 # 进行中
AffairState_Finish = 2 # 已完成
#// A6 13 家族事务操作 #tagCMFamilyAffairOP
#
#struct     tagCMFamilyAffairOP
#{
#    tagHead        Head;
#    BYTE        OPType;    // 操作类型:1-刷新事务;2-开始事务;3-领取事务奖励;
#    WORD        AffairID;    // 事务ID,可选
#};
def OnFamilyAffairOP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    opType = clientData.OPType
    affairID = clientData.AffairID
    if opType == 1:
        __FamilyAffair_Refresh(curPlayer)
    elif opType == 2:
        __FamilyAffair_Start(curPlayer, affairID)
    elif opType == 3:
        __FamilyAffair_GetAward(curPlayer, affairID)
    return
def __FamilyAffair_CheckReset(curPlayer):
    ## 检查任务重置,登录,进入仙盟触发
    info = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairInfo % 1)
    if not info:
        # 任务1还没分配,默认强制重置
        __FamilyAffair_Refresh(curPlayer, True)
    else:
        SyncFamilyAffairInfo(curPlayer)
    return
def __FamilyAffair_Refresh(curPlayer, isReset=False):
    ## 刷新事务
    playerID = curPlayer.GetPlayerID()
    moneyType, moneyValue = 0, 0
    dayRefreshFreeCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairRefreshFree)
    if not isReset:
        freeCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 2)
        if freeCountMax and dayRefreshFreeCount >= freeCountMax:
            moneyType, moneyValue = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 3)
            if not PlayerControl.HaveMoney(curPlayer, moneyType, moneyValue):
                return
    sendMailAffairList = []
    refreshAffairIDList = []
    affairCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 1)
    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
    maxStar = 0
    starWeightList = []
    for starStr, starInfo in affairStarDict.items():
        star = int(starStr)
        if star > maxStar:
            maxStar = star
        starWeightList.append([starInfo[0], star])
    for affairID in range(1, affairCountMax + 1):
        star, state = __GetAffairInfo(curPlayer, affairID)
        if isReset:
            # 重置时还在进行中的直接发奖励
            if state == AffairState_Underway:
                sendMailAffairList.append([affairID, star])
            refreshAffairIDList.append(affairID)
        else:
            # 非重置只处理没有状态非最高星的
            if state == AffairState_None and star < maxStar:
                refreshAffairIDList.append(affairID)
    GameWorld.DebugLog("刷新事务: isReset=%s,moneyType=%s,moneyValue=%s,dayRefreshFreeCount=%s"
                       % (isReset, moneyType, moneyValue, dayRefreshFreeCount), playerID)
    GameWorld.DebugLog("    sendMailAffairList=%s" % sendMailAffairList, playerID)
    for mailInfo in sendMailAffairList:
        affairID, star = mailInfo
        if str(star) not in affairStarDict:
            continue
        paramList = [affairID, star]
        addItemList = affairStarDict[str(star)][2]
        PlayerControl.SendMailByKey("FamilyAffairAward", [playerID], addItemList, paramList)
    if isReset:
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairRefreshFree, 0)
        for affairID in range(1, affairCountMax + 1):
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID, 0)
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID, 0)
    else:
        if moneyType and moneyValue:
            PlayerControl.PayMoney(curPlayer, moneyType, moneyValue, "FamilyAffair")
        else:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairRefreshFree, dayRefreshFreeCount + 1)
    GameWorld.DebugLog("    starWeightList=%s" % starWeightList, playerID)
    GameWorld.DebugLog("    refreshAffairIDList=%s" % refreshAffairIDList, playerID)
    for affairID in refreshAffairIDList:
        star = GameWorld.GetResultByWeightList(starWeightList, 1)
        __SetAffairInfo(curPlayer, affairID, star, AffairState_None)
        GameWorld.DebugLog("    随机事务:affairID=%s,star=%s" % (affairID, star), playerID)
    SyncFamilyAffairInfo(curPlayer)
    return
def __FamilyAffair_Start(curPlayer, affairID):
    ## 开始事务
    playerID = curPlayer.GetPlayerID()
    star, state = __GetAffairInfo(curPlayer, affairID)
    if not star:
def __OnFamilyMoneyDonate(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    donateType = clientData.DonateType
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    if state != AffairState_None:
        GameWorld.DebugLog("仙盟事务已经进行中或已完成,无法开始: affairID=%s,star=%s,state=%s" % (affairID, star, state), playerID)
    familyMgr = DBDataMgr.GetFamilyMgr()
    curFamily = familyMgr.FindFamily(familyID)
    if not curFamily:
        return
    startTime = int(time.time())
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID, startTime)
    __SetAffairInfo(curPlayer, affairID, star, AffairState_Underway)
    GameWorld.DebugLog("仙盟事务开始: affairID=%s,star=%s,startTime=%s" % (affairID, star, startTime), playerID)
    SyncFamilyAffairInfo(curPlayer, affairID)
    return
def __FamilyAffair_GetAward(curPlayer, affairID):
    ## 领取事务奖励
    playerID = curPlayer.GetPlayerID()
    curTime = int(time.time())
    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
    star, state = __GetAffairInfo(curPlayer, affairID)
    if state != AffairState_Underway:
        GameWorld.DebugLog("仙盟事务状态非进行中无法领取: affairID=%s,star=%s,state=%s" % (affairID, star, state), playerID)
        SyncFamilyAffairInfo(curPlayer, affairID)
        return
    startTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID)
    remainDuration = __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict)
    if remainDuration != 0:
        GameWorld.DebugLog("仙盟事务当前剩余时长未完成: affairID=%s,remainDuration=%s,startTime=%s"
                           % (affairID, remainDuration, startTime), playerID)
        SyncFamilyAffairInfo(curPlayer, affairID)
        return
    if str(star) not in affairStarDict:
        return
    addItemList = affairStarDict[str(star)][2]
    if not ItemCommon.GiveAwardItem(curPlayer, addItemList):
        return
    __SetAffairInfo(curPlayer, affairID, star, AffairState_Finish)
    GameWorld.DebugLog("仙盟事务领奖: affairID=%s,star=%s" % (affairID, star), playerID)
    SyncFamilyAffairInfo(curPlayer, affairID)
    return
def __GetAffairInfo(curPlayer, affairID):
    affairInfo = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID)
    star, state = affairInfo / 10, affairInfo % 10
    return star, state
def __SetAffairInfo(curPlayer, affairID, star, state):
    info = star * 10 + state
    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyAffairInfo % affairID, info)
    return
def __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict):
    ## -1-未开始;>=0-剩余时长
    startTime = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairStartTime % affairID)
    if not startTime:
        return -1
    starInfo = affairStarDict.get(str(star), [])
    needDuration = starInfo[1] if len(starInfo) > 1 else 0
    # 可扩展减时长属性
    speedPer = PlayerControl.GetAffairSpeedPer(curPlayer)
    if speedPer:
        needDuration = int(needDuration * max(10000 - speedPer, 0) / 10000.0)
        #GameWorld.DebugLog("事务加速: needDuration=%s,speedPer=%s" % (needDuration, speedPer), curPlayer.GetPlayerID())
    remainDuration = max(needDuration - (curTime - startTime), 0)
    return remainDuration
def SyncFamilyAffairInfo(curPlayer, affairID=None):
    if affairID == None:
        affairIDList = []
        affairCountMax = IpyGameDataPY.GetFuncCfg("FamilyAffair", 1)
        for affairID in range(1, affairCountMax + 1):
            affairIDList.append(affairID)
    else:
        affairIDList = [affairID]
    curTime = int(time.time())
    affairStarDict = IpyGameDataPY.GetFuncEvalCfg("FamilyAffair", 4)
    affairInfoList = []
    for affairID in affairIDList:
        star, state = __GetAffairInfo(curPlayer, affairID)
        remainDuration = __GetAffairRemainDuration(curPlayer, affairID, star, curTime, affairStarDict)
        affairInfo = ChPyNetSendPack.tagMCFamilyAffair()
        affairInfo.AffairID = affairID
        affairInfo.Star = star
        affairInfo.State = state
        affairInfo.RemainDuration = max(0, remainDuration)
        affairInfoList.append(affairInfo)
    if not affairInfoList:
        return
    clientPack = ChPyNetSendPack.tagMCFamilyAffairInfo()
    clientPack.Clear()
    clientPack.RefreshFreeCount = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyAffairRefreshFree)
    clientPack.AffairInfoList = affairInfoList
    clientPack.Count = len(clientPack.AffairInfoList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
##--------------------------------------------------------------------------------------------------
##--------------------------------------- 仙盟传功 --------------------------------------------------
#// A6 15 传功操作 #tagCMChuangongOP
#
#struct    tagCMChuangongOP
#{
#    tagHead        Head;
#    BYTE        OPType;        // 操作类型:1-邀请;2-回应;3-领奖;
#    DWORD        PlayerID;        // 目标玩家ID;回应时为邀请方玩家ID
#    BYTE        OPData;        // 操作数据,可选:回应时为是否同意
#};
def OnChuangongOP(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    opType = clientData.OPType
    tagPlayerID = clientData.PlayerID
    opData = clientData.OPData
    if tagPlayerID and curPlayer.GetPlayerID() == tagPlayerID:
        GameWorld.DebugLog("不能自己传功")
    curMember = curFamily.FindMember(playerID)
    if not curMember:
        return
    
    if opType == 1:
        __Chuangong_Invite(curPlayer, tagPlayerID)
    elif opType == 2:
        __Chuangong_Response(curPlayer, tagPlayerID, opData)
    elif opType == 3:
        __Chuangong_GetAward(curPlayer)
    return
def SendGameServer_FamilyChuangong(curPlayer, msgType, msgData):
    playerID = curPlayer.GetPlayerID()
    GameWorld.DebugLog("传功同步GameServer: msgType=%s,%s" % (msgType, msgData), playerID)
    msgInfo = str([msgType, msgData])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "FamilyChuangong", msgInfo, len(msgInfo))
    return
def __CheckCanChuangong(curPlayer):
    playerID = curPlayer.GetPlayerID()
    remainCnt = PlayerActivity.GetDailyActionrRemainCnt(curPlayer, ShareDefine.DailyActionID_FamilyChuanGong)
    if remainCnt <= 0:
        GameWorld.DebugLog("没有传功次数了", playerID)
    if not reqDataEx:
        return
    openServerDayLimit = IpyGameDataPY.GetFuncCfg("FamilyChuangong", 1)
    if openServerDayLimit:
        openServerDay = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_ServerDay) + 1
        if openServerDay > openServerDayLimit:
            GameWorld.DebugLog("当前开服天不能传功: openServerDay=%s > %s" % (openServerDay, openServerDayLimit), playerID)
            return
    if "donateCnt" not in reqDataEx:
        return
    donateCnt = reqDataEx["donateCnt"]
    ipyData = IpyGameDataPY.GetIpyGameData("FamilyDonate", donateType)
    if not ipyData:
        return
    moneyType = ipyData.GetMoneyType()
    moneyValue = ipyData.GetMoneyValue()
    if not moneyType or not moneyValue:
        return
    CrossPlayer.CostPlayerResources(crossPlayer, "FamilyMoneyDonate", costMoneyDict={moneyType:moneyValue})
    awardItemList = ipyData.GetAwardItemList()
    donateCnt += 1
    CrossPlayer.SetPlayerNomalDict(crossPlayer, {ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType:donateCnt}, isDayReset=True)
    #SyncDonateCntInfo(curPlayer)
    # 增加成员捐献次数记录
    memDonateCntDay = curMember.GetDonateCntDay() + 1
    memDonateCntTotal = min(curMember.GetDonateCntTotal() + 1, ChConfig.Def_UpperLimit_DWord)
    curMember.SetDonateCntDay(memDonateCntDay)
    curMember.SetDonateCntTotal(memDonateCntTotal)
    GameWorld.DebugLog("家族捐献: donateType=%s,donateCnt=%s,%s,memDonateCntDay=%s,memDonateCntDay=%s"
                       % (donateType, donateCnt, awardItemList, memDonateCntDay, memDonateCntTotal), playerID)
    CrossPlayer.GivePlayerResources(crossPlayer, awardItemList, eventName="FamilyMoneyDonate")
    return True
def __Chuangong_Invite(curPlayer, tagPlayerID):
    ## 传功邀请
    if not __CheckCanChuangong(curPlayer):
def __OnFamilyMoneyDonate_Ret(curPlayer, clientData, isOK):
    if not isOK:
        return
    SendGameServer_FamilyChuangong(curPlayer, "Invite", [tagPlayerID])
    SyncDonateCntInfo(curPlayer)
    return
def __Chuangong_Response(curPlayer, tagPlayerID, isOK):
    ## 传功回应
    SendGameServer_FamilyChuangong(curPlayer, "Response", [tagPlayerID, isOK])
def AddFamilyExp(curPlayer, addExp):
    ## 增加玩家家族经验
    clientData, tick = None, 0
    reqDataEx = {"addExp":addExp}
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__AddFamilyExp", reqCD=0, reqDataEx=reqDataEx)
    return
def __Chuangong_GetAward(curPlayer):
    ## 传功领奖
    if not __CheckCanChuangong(curPlayer):
def __AddFamilyExp(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    PlayerActivity.AddDailyActionFinishCnt(curPlayer, ShareDefine.DailyActionID_FamilyChuanGong, 1)
    familyMgr = DBDataMgr.GetFamilyMgr()
    curFamily = familyMgr.FindFamily(familyID)
    if not curFamily:
        return
    curLV = curFamily.GetLV()
    curExp = curFamily.GetExp()
    
    playerID = curPlayer.GetPlayerID()
    fromLV = curPlayer.GetLV()
    if not reqDataEx:
        return
    addExp = reqDataEx["addExp"]
    
    # 给经验
    totalExp = 0
    giveRound = IpyGameDataPY.GetFuncCfg("FamilyChuangong", 3)
    worldlv = GameWorld.GetGameWorld().GetGameWorldDictByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
    playerControl = PlayerControl.PlayerControl(curPlayer)
    for index in range(giveRound):
        reLV = curPlayer.GetLV()
        reExp = PlayerControl.GetPlayerReExp(curPlayer)
        giveExp = eval(IpyGameDataPY.GetFuncCompileCfg("FamilyChuangong", 2))
        finalAddExp = playerControl.AddExp(giveExp, ShareDefine.Def_ViewExpType_Chuangong)
        totalExp += finalAddExp
        GameWorld.DebugLog("传功经验: 轮次=%s,reLV=%s,reExp=%s,finalAddExp=%s, %s" % (index, reLV, reExp, finalAddExp, totalExp), playerID)
    updLV = curLV
    updExp = curExp + addExp
    GameWorld.DebugLog("增加公会经验: curLV=%s,curExp=%s,addExp=%s,updExp=%s" % (curLV, curExp, addExp, updExp), playerID)
    ipyData = IpyGameDataPY.GetIpyGameData("Family", curLV)
    lvUPExp = ipyData.GetNeedExp()
    while lvUPExp and updExp >= lvUPExp:
        ipyData = IpyGameDataPY.GetIpyGameDataNotLog("Family", updLV + 1)
        if not ipyData:
            break
        updLV += 1
        updExp -= lvUPExp
        lvUPExp = ipyData.GetNeedExp()
        GameWorld.DebugLog("    公会升级: updLV=%s,updExp=%s,lvUPExp=%s" % (updLV, updExp, lvUPExp), playerID)
        
    PlayerWeekParty.AddWeekPartyActionCnt(curPlayer, ChConfig.Def_WPAct_FamilyChuanGong, 1)
    PlayerTongTianLing.AddTongTianTaskValue(curPlayer, ChConfig.TTLTaskType_ChuanGong, 1)
    curFamily.SetLV(updLV)
    curFamily.SetExp(updExp)
    
    # 给物品
    giveItemList = IpyGameDataPY.GetFuncEvalCfg("FamilyChuangong", 4)
    ItemControler.GivePlayerItemOrMail(curPlayer, giveItemList)
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 公会经验
    return True
def AddFamilyContrib(curPlayer, addContribValue):
    ## 增加玩家累计家族贡献
    clientData, tick = None, 0
    reqDataEx = {"addContribValue":addContribValue}
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__AddFamilyContrib", reqCD=0, reqDataEx=reqDataEx)
    return
def __AddFamilyContrib(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if familyID <= 0:
        return
    familyMgr = DBDataMgr.GetFamilyMgr()
    curFamily = familyMgr.FindFamily(familyID)
    if not curFamily:
        return
    curMember = curFamily.FindMember(playerID)
    if not curMember:
        return
    
    # 同步结果
    syncItemList = []
    for itemID, itemCount, _ in giveItemList:
        syncItem = ChPyNetSendPack.tagMCChuangongItem()
        syncItem.Clear()
        syncItem.ItemID = itemID
        syncItem.ItemCount = itemCount
        syncItemList.append(syncItem)
        # 有协助感谢礼盒
        if IpyGameDataPY.GetIpyGameDataNotLog("AssistThanksGift", itemID):
            SendGameServer_FamilyChuangong(curPlayer, "ThanksGift", [itemID])
    clientPack = ChPyNetSendPack.tagMCChuangongResult()
    clientPack.Clear()
    clientPack.FromLV = fromLV
    clientPack.ToLV = curPlayer.GetLV()
    clientPack.Exp = totalExp % ChConfig.Def_PerPointValue
    clientPack.ExpPoint = totalExp / ChConfig.Def_PerPointValue
    clientPack.AwardItemList = syncItemList
    clientPack.ItemCount = len(clientPack.AwardItemList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    if not reqDataEx:
        return
    addContribValue = reqDataEx["addContribValue"]
    contribDay = curMember.GetContribDay() + addContribValue
    contribTotal = min(curMember.GetContribTotal() + addContribValue, ChConfig.Def_UpperLimit_DWord)
    curMember.SetContribDay(contribDay)
    curMember.SetContribTotal(contribTotal)
    GameWorld.DebugLog("增加成员贡献: familyID=%s,addContribValue=%s,contribDay=%s,contribTotal=%s" % (familyID, addContribValue, contribDay, contribTotal), playerID)
    Broadcast_FamilyInfo(familyID, isSyncMem=False) # 成员贡献
    return
##--------------------------------------------------------------------------------------------------
##----------------------------------------- 珍宝阁 --------------------------------------------------
#// A6 16 珍宝阁操作 #tagCMZhenbaogeOP
## ------------------------------------------------------------------------------------------------
#// A6 17 查询家族行为信息 #tagCMQueryFamilyAction
#
#struct    tagCMZhenbaogeOP
#struct    tagCMQueryFamilyAction
#{
#    tagHead     Head;
#    BYTE    OpType;    // 操作:0-砍价;1-购买
#    tagHead        Head;
#    BYTE        ActionType;        // 行为类型
#    DWORD        FamilyID;         // 家族ID,发0默认自己家族
#};
def OnZhenbaogeOP(index, clientData, tick):
def OnQueryFamilyAction(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    opType = clientData.OpType
    playerID = curPlayer.GetPlayerID()
    if not curPlayer.GetFamilyID():
        GameWorld.DebugLog("没有仙盟无法操作珍宝阁!", playerID)
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnQueryFamilyAction")
    return
def __OnQueryFamilyAction(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    actionType = clientData.ActionType
    familyID = clientData.FamilyID
    if not familyID:
        familyID = crossPlayer.GetFamilyID()
    SendFamilyActionInfo(crossPlayer, familyID, actionType)
    return
def SendFamilyActionInfo(crossPlayer, familyID, actionType):
    ## 发送家族行为
    # @param crossPlayer: 为None时通知该公会所有成员
    if not familyID:
        return
    familyAction = DBDataMgr.GetFamilyActionMgr().GetFamilyAction(familyID, actionType)
    
    # 砍价
    if opType == 0:
        leaveTimeEx = PlayerControl.GetLeaveFamilyTimeEx(curPlayer)
        cutState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut)
        if cutState and leaveTimeEx:
            cutCDTimes = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 3) * 60
            passTimes = int(time.time()) - leaveTimeEx
            if passTimes < cutCDTimes:
                GameWorld.DebugLog("今日已砍价变更仙盟砍价CD中! passTimes=%s < %s" % (passTimes, cutCDTimes), playerID)
                return
        SendGameServer_FamilyZhenbaoge(curPlayer, "Cut", [])
    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
    clientPack.FamilyID = familyID
    clientPack.ActionType = actionType
    clientPack.FamilyActionList = []
    for index in xrange(familyAction.Count()):
        familyActionData = familyAction.At(index)
        
    # 购买
    elif opType == 1:
        buyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
        if buyState:
            GameWorld.DebugLog("珍宝阁今日已购买!", playerID)
            return
        moneyType = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 1)
        playerMoneyValue = PlayerControl.GetMoney(curPlayer, moneyType)
        SendGameServer_FamilyZhenbaoge(curPlayer, "Buy", [playerMoneyValue])
        actionData = ChPyNetSendPack.tagMCFamilyAction()
        actionData.Time = familyActionData.GetTime()
        actionData.Name = familyActionData.GetName()
        actionData.NameLen = len(actionData.Name)
        actionData.Value1 = familyActionData.GetValue1()
        actionData.Value2 = familyActionData.GetValue2()
        actionData.Value3 = familyActionData.GetValue3()
        actionData.Value4 = familyActionData.GetValue4()
        actionData.Value5 = familyActionData.GetValue5()
        actionData.Value6 = familyActionData.GetValue6()
        actionData.UseData = familyActionData.GetUserData()
        actionData.UseDataLen = len(actionData.UseData)
        clientPack.FamilyActionList.append(actionData)
        
    return
def SendGameServer_FamilyZhenbaoge(curPlayer, msgType, msgData):
    playerID = curPlayer.GetPlayerID()
    tick = GameWorld.GetGameWorld().GetTick()
    if not GameWorld.SetPlayerTickTime(curPlayer, ChConfig.TYPE_Player_Tick_FamilyZhenbaoge, tick):
        GameWorld.DebugLog("请求CD中...", playerID)
    clientPack.Count = len(clientPack.FamilyActionList)
    if crossPlayer:
        CrossPlayer.SendFakePack(crossPlayer, clientPack)
        return
    GameWorld.DebugLog("珍宝阁同步GameServer: msgType=%s,%s" % (msgType, msgData), playerID)
    msgInfo = str([msgType, msgData])
    GameWorld.GetPlayerManager().GameServer_QueryPlayerResult(playerID, 0, 0, "FamilyZhenbaoge", msgInfo, len(msgInfo))
    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
    return
def GameServer_FamilyZhenbaogeRet(curPlayer, resultList):
    curPlayer.SetTickByType(ChConfig.TYPE_Player_Tick_FamilyZhenbaoge, 0)
def SendFamilyAction(actionDataList, crossPlayer=None):
    ## 同步指定公会action
    # @param actionDataList: 支持列表或指定actionData
    # @param crossPlayer: 为None时通知该公会所有成员
    if not isinstance(actionDataList, list):
        actionDataList = [actionDataList]
    if not actionDataList:
        return
    familyActionData = actionDataList[0]
    familyID = familyActionData.GetFamilyID()
    actionType = familyActionData.GetActionType()
    clientPack = ChPyNetSendPack.tagMCFamilyActionInfo()
    clientPack.FamilyID = familyID
    clientPack.ActionType = actionType
    clientPack.FamilyActionList = []
    for familyActionData in actionDataList:
        actionData = ChPyNetSendPack.tagMCFamilyAction()
        actionData.Time = familyActionData.GetTime()
        actionData.Name = familyActionData.GetName()
        actionData.NameLen = len(actionData.Name)
        actionData.Value1 = familyActionData.GetValue1()
        actionData.Value2 = familyActionData.GetValue2()
        actionData.Value3 = familyActionData.GetValue3()
        actionData.Value4 = familyActionData.GetValue4()
        actionData.Value5 = familyActionData.GetValue5()
        actionData.Value6 = familyActionData.GetValue6()
        actionData.UseData = familyActionData.GetUserData()
        actionData.UseDataLen = len(actionData.UseData)
        clientPack.FamilyActionList.append(actionData)
    clientPack.Count = len(clientPack.FamilyActionList)
    if crossPlayer:
        CrossPlayer.SendFakePack(crossPlayer, clientPack)
        return
    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
    return
def OnFamilyTalk(curPlayer, familyID, talkPack, tick):
    clientData, tick = None, 0
    reqDataEx = {"talkBuffer":talkPack.GetBuffer()}
    FamilyPyPackForwarding(curPlayer, clientData, tick, "__OnFamilyTalk", reqCD=0, reqDataEx=reqDataEx)
    return
def __OnFamilyTalk(crossPlayer, clientData, tick, fromServerID=0, reqDataEx=None):
    talkBuffer = reqDataEx["talkBuffer"]
    playerID = crossPlayer.GetPlayerID()
    familyID = crossPlayer.GetFamilyID()
    if not familyID or not talkBuffer:
        return
    clientPack = ChPyNetSendPack.tagMCTalk()
    clientPack.ReadData(talkBuffer)
    CrossPlayer.SendFakePackByFamily(familyID, clientPack)
    # 聊天缓存
    channelType = clientPack.ChannelType
    content = clientPack.Content
    bubbleBox = clientPack.BubbleBox
    PlayerTalk.DoTalkCache(channelType, playerID, content, bubbleBox, familyID)
    return
## -------------------------------------- 游戏服本服处理 --------------------------------------------
'''
为方便本服、跨服互通公会逻辑统一,公会相关数据处理统一使用 CrossPlayer,视为以前的GameServer处理,这样本服跨服的公会管理通用
本服的curPlayer仅处理以前类似MapServer的curPlayer相关逻辑
【注】 MapServer的逻辑不能再直接获取 family 实例进行逻辑处理,只能处理 curPlayer 可用的逻辑
'''
def C2S_FamilyMapPlayer(dataMsg, playerID):
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
    if not curPlayer:
        return
    doType = dataMsg["doType"]
    doData = dataMsg["doData"]
    if doType == "FamilyRefresh":
        Do_MapServer_FamilyRefresh(curPlayer, doData)
    return
def Do_MapServer_PlayerOnDay(curPlayer):
    ResetDailyDonateCnt(curPlayer)
    return
def Do_MapServer_PlayerLogin(curPlayer):
    DBFamily.Sync_FamilyCrossInfo(curPlayer)
    SyncDonateCntInfo(curPlayer)
    PlayerFamilyZhenbaoge.OnPlayerLogin(curPlayer)
    return
def Do_MapServer_FamilyRefresh(curPlayer, doData):
    tick = GameWorld.GetGameWorld().GetTick()
    playerID = curPlayer.GetPlayerID()
    msgType, _ = resultList[:2]
    retData = resultList[2:]
    GameWorld.Log("仙盟珍宝阁GameServer返回: %s" % str(resultList), playerID)
    if msgType == "Cut":
        cutPrice = retData[0]
        if not cutPrice:
            # 砍价失败不处理后续
            return
        PlayerControl.NotifyCode(curPlayer, "ZhenbaogeCut", [cutPrice])
        if not curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut):
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeCut, 1)
            Sync_ZhenbaogeInfo(curPlayer)
    refreshFamilyID = doData["FamilyID"]
    refreshFmLV = doData.get("FmLV", 0)
    refreshFamilyLV = doData.get("FamilyLV", 0)
    refreshFamilyName = doData.get("FamilyName", "")
    refreshEmblemID = doData.get("EmblemID", 0)
    refreshEmblemWord = doData.get("EmblemWord", "")
    PlayerViewCache.UpdPlayerViewFamilyInfo(playerID, refreshFamilyID, refreshFamilyName, refreshEmblemID, refreshEmblemWord)
    lastFamilyID = curPlayer.GetFamilyID()
    lastFamilyLV = curPlayer.GetFamilyLV() # 公会等级,非职位等级
    lastFmLV = PlayerControl.GetFamilyMemberLV(curPlayer)
    if lastFamilyID != refreshFamilyID:
        curPlayer.SetFamilyID(refreshFamilyID)
        
    elif msgType == "Buy":
        isOK = retData[0]
        if not isOK:
            return
        buyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
        if buyState:
            #一天只能买一次,防止变更仙盟刷
            return
        nowPrice, giveItemList = retData[1:]
    if curPlayer.GetFamilyName() != refreshFamilyName:
        curPlayer.SetFamilyName(refreshFamilyName)
        #curPlayer.Notify_FamilyNameRefresh() #//04 36    周围玩家家族名刷新#tagPlayerFamilyNameRefresh
        
        moneyType = IpyGameDataPY.GetFuncCfg("Zhenbaoge", 1)
        if nowPrice > 0:
            if not PlayerControl.PayMoney(curPlayer, moneyType, nowPrice, "Zhenbaoge"):
                GameWorld.ErrLog("珍宝阁购买货币不足! nowPrice=%s" % nowPrice, playerID)
                return
        elif nowPrice < 0:
            PlayerControl.GiveMoney(curPlayer, moneyType, -nowPrice, "Zhenbaoge")
        else: # 0不处理
    if lastFmLV != refreshFmLV:
        PlayerControl.SetFamilyMemberLV(curPlayer, refreshFmLV)
    if lastFamilyLV != refreshFamilyLV:
        isLVUP = False
        if lastFamilyLV and refreshFamilyLV > 0:
            #触发家族升级事件
            isLVUP = True
        curPlayer.SetFamilyLV(refreshFamilyLV)
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyLV, refreshFamilyLV)
        #触发家族升级事件, 在SetFamilyLV之后,任务可以取等级判定
        if isLVUP:
            pass
        
        if giveItemList:
            ItemControler.GivePlayerItemOrMail(curPlayer, giveItemList, event=["Zhenbaoge", False, {}], isNotifyAward=False)
        ItemControler.NotifyGiveAwardInfo(curPlayer, giveItemList, "Zhenbaoge", moneyInfo={moneyType:0 if nowPrice > 0 else -nowPrice})
    if lastFamilyID != 0 and curPlayer.GetFamilyID() == 0:
        #玩家离开家族
        isVoluntarily = doData.get("isVoluntarily", 0)
        __OnLeaveFamily(curPlayer, isVoluntarily, tick)
        
        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy, 1)
        Sync_ZhenbaogeInfo(curPlayer)
    elif lastFamilyID == 0 and curPlayer.GetFamilyID() != 0:
        #刚进家族并为族长,触发建家族事件
        if curPlayer.GetFamilyMemberLV() == IPY_GameWorld.fmlLeader:
            pass
            
        #进入家族触发事件
        __OnEnterFamily(curPlayer, tick)
    #---通知客户端刷新属性---
    curPlayer.View_FamilyInfoRefresh() #//04 30 玩家家族名字职位等信息刷新#tagPlayerInFamilyInfoRefresh
    return
def Sync_ZhenbaogeInfo(curPlayer):
    clientPack = ChPyNetSendPack.tagMCFamilyZhenbaogeInfo()
    clientPack.CutState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeCut)
    clientPack.BuyState = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyZhenbaogeBuy)
def __OnEnterFamily(curPlayer, tick):
    ## 进入家族触发事件
    PlayerTask.UpdTaskValue(curPlayer, ChConfig.TaskType_ReqOrJoinFamily)
    return
def __OnLeaveFamily(curPlayer, isVoluntarily, tick):
    ## 退出家族触发事件
    #---清空家族相关信息---
    curPlayer.SetPerExp(0)
    curPlayer.SetFamilyHornor(0)
    curPlayer.SetFamilyActiveValue(0)
    curPlayer.SetLastWeekFamilyActiveValue(0)
    curPlayer.SetFamilyLV(0)
    PlayerControl.SetLeaveFamilyTimeEx(curPlayer, int(time.time()))
    leaveCnt, kickedCnt, _ = PlayerControl.GetLeaveFamilyInfo(curPlayer)
    GameWorld.DebugLog("__OnLeaveFamily: isVoluntarily=%s,leaveCnt=%s,kickedCnt=%s" % (isVoluntarily, leaveCnt, kickedCnt))
    delMoneyType, delMoneyPer = IpyGameDataPY.GetFuncCfg("FamilyLeave", 3), 0
    if isVoluntarily:
        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 4)
        if delMoneyPerList:
            delMoneyPer = delMoneyPerList[leaveCnt] if len(delMoneyPerList) > leaveCnt else delMoneyPerList[-1]
        leaveCnt += 1
        GameWorld.DebugLog("    增加主动离开次数: leaveCnt=%s" % (leaveCnt))
    else:
        delMoneyPerList = IpyGameDataPY.GetFuncEvalCfg("FamilyLeave", 5)
        if delMoneyPerList:
            delMoneyPer = delMoneyPerList[kickedCnt] if len(delMoneyPerList) > kickedCnt else delMoneyPerList[-1]
        kickedCnt += 1
        GameWorld.DebugLog("    增加被踢离开次数: kickedCnt=%s" % (kickedCnt))
    PlayerControl.SetLeaveFamilyInfo(curPlayer, leaveCnt, kickedCnt, isVoluntarily)
    if delMoneyType and delMoneyPer:
        nowMoney = PlayerControl.GetMoney(curPlayer, delMoneyType)
        delMoney = int(nowMoney * delMoneyPer / 100.0)
        GameWorld.DebugLog("    扣除货币: delMoneyType=%s,delMoneyPer=%s,nowMoney=%s,delMoney=%s" % (delMoneyType, delMoneyPer, nowMoney, delMoney))
        PlayerControl.PayMoney(curPlayer, delMoneyType, delMoney, "LeaveFamily")
    PlayerFamilyTaofa.OnPlayerLeaveFamily(curPlayer)
    FBLogic.OnLeaveFamily(curPlayer, tick)
    return
def ResetDailyDonateCnt(curPlayer):
    isReset = False
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetFamilyDonateCount()):
        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
        donateType = ipyData.GetDonateType()
        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
        if donateCnt:
            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType, 0)
            isReset = True
    if isReset:
        SyncDonateCntInfo(curPlayer)
    return
def SyncDonateCntInfo(curPlayer):
    donateCntList = []
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in range(ipyDataMgr.GetFamilyDonateCount()):
        ipyData = ipyDataMgr.GetFamilyDonateByIndex(index)
        donateType = ipyData.GetDonateType()
        donateCnt = curPlayer.NomalDictGetProperty(ChConfig.Def_Player_Dict_FamilyDonateCnt % donateType)
        donateCntList.append(donateCnt)
    if not donateCntList:
        return
    clientPack = ChPyNetSendPack.tagSCDonateCntInfo()
    clientPack.DonateCntList = donateCntList
    clientPack.Count = len(clientPack.DonateCntList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
##--------------------------------------------------------------------------------------------------