ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerViewCache.py
@@ -16,123 +16,73 @@
# @change: "2016-05-18 16:00" hxp 战盟有官员成员缓存不清除
#---------------------------------------------------------------------
#"""Version = 2016-05-18 16:00"""
#------------------------------------------------------------------------------
#---------------------------------------------------------------------
import GameWorld
import ChPyNetSendPack
import IPY_PlayerDefine
import PlayerFamilyAction
import GameWorship
import PlayerControl
import NetPackCommon
import ReadChConfig
import PlayerFamily
import IPY_GameServer
import GameWorldArena
import ChPyNetSendPack
import PlayerFBHelpBattle
import GameWorldSkyTower
import CrossChampionship
import CrossBattlefield
import PyGameDataStruct
import IpyGameDataPY
import PyDataManager
import ShareDefine
import ChConfig
import time
import json
import time
ViewCacheMgr = GameWorld.GameWorldData.GetPlayerViewCacheMgr()
# 优先取缓存数据,后取可保存数据
def GetItemData(curCache):
    result = curCache.GetItemDataNoSave()
    if not result:
        return curCache.GetItemData()
    return result
def GetPlusData(curCache):
    result = curCache.GetPlusDataNoSave()
    if not result:
        return curCache.GetPlusData()
    return result
##更新缓存数据
#  @param PlayerID, PropData, ItemData, PlusData, isSaveDB
#  @return None
def UpdatePlayerCache(PlayerID, PropData, ItemData, PlusData, isSaveDB=False):
    curCache = ViewCacheMgr.FindCache(PlayerID)
    if not curCache:
        curCache = ViewCacheMgr.AddNewCache(PlayerID)
    curCache.SetUpdateTime(GameWorld.GetCurrentDataTimeStr())
    GameWorld.DebugLog('ViewCache### UpdatePlayerCache PlayerID %s, \
                        PropData(len %s): %s, \
                        ItemData(len %s):  %s, \
                        PlusData(len %s):  %s'%
           (PlayerID, len(PropData), PropData,len(ItemData), ItemData, len(PlusData), PlusData)
                        )
    #2017-12-21 由于保存数据库的数据(realtime)过多根据需求分SetItemData和SetItemDataNoSave
    # 关服尽量只保存基础数据,默认使用不保存接口 SetItemDataNoSave
    curCache.SetPropData(PropData,len(PropData))
    if curCache.GetItemData() or (isSaveDB and IsNeedSaveViewCacheAllInfo(PlayerID)):
        curCache.SetItemData(ItemData,len(ItemData))
        curCache.SetPlusData(PlusData,len(PlusData))
        curCache.SetItemDataNoSave("",0) # 避免占用内存
        curCache.SetPlusDataNoSave("",0)
    else:
        # 低级号不处理保存
        curCache.SetItemDataNoSave(ItemData,len(ItemData))
        curCache.SetPlusDataNoSave(PlusData,len(PlusData))
    curCache.SetNeedSaveDB(isSaveDB) #设置需要保存到数据库
    # 同步更新助战信息
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
        PropDataDict = eval(PropData)
        fightPower = PropDataDict.get("FightPower", 0)
        familyID = PropDataDict.get("FamilyID", 0)
        playerName = PropDataDict.get("Name", "")
        PlayerFBHelpBattle.UpdateCheckInPlayerInfo(PlayerID, fightPower, familyID, playerName)
    #暂时关闭
    #===========================================================================
    # FamilyIDKey = "FamilyID"
    # if FamilyIDKey in PropData:
    #    PropDataDict = eval(PropData)
    #    familyID = PropDataDict[FamilyIDKey]
    #    if familyID > 0:
    #        PlayerFamilyAction.UpdFamilyOfficerModelEquip(familyID, PlayerID)
    #===========================================================================
    return
##玩家下线缓存数据
#  @param PlayerID, PlayerLV, PropData, ItemData, PlusData
#  @return None
def OnPlayerLogout(PlayerID, PlayerLV, PropData, ItemData, PlusData):
    #不需要保存离线数据的,直接删除缓存数据
    if not IsNeedSaveLogoutPlayer(PlayerID, PlayerLV):
        ViewCacheMgr.DeleteCache(PlayerID)
        return
    #更新数据,并设置需要保存数据库
    UpdatePlayerCache(PlayerID, PropData, ItemData, PlusData, True)
def DoOnDayEx():
    DelOutofTimeViewCacheData()
    return
## 根据规则判定是否需要继续保存离线玩家数据
#  @param PlayerID, PlayerLV
#  @return None
def IsNeedSaveLogoutPlayer(PlayerID, PlayerLV):
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
def IsSaveDBViewCache(playerID, playerLV):
    ## 是否保存基本的缓存数据
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
        return True
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    if CrossBattlefield.IsBattlefieldCallPlayer(playerID):
        return True
    if CrossChampionship.IsChampionshipPlayer(playerID):
        return True
    if GameWorship.IsWorshipPlayer(playerID):
        return True
    if GameWorldSkyTower.IsSkyTowerPassPlayer(playerID):
        return True
    
    SaveDBLimitLV = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 1)
    #校验玩家等级
    if PlayerLV < SaveDBLimitLV:
    if playerLV < SaveDBLimitLV:
        return False
    
    return True
# 上榜用户
def IsNeedSaveViewCacheAllInfo(PlayerID):
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(PlayerID):
def IsSaveAllViewCache(playerID):
    ## 是否保存所有缓存数据
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
        return True
    if GameWorldArena.IsArenaBattlePlayer(playerID):
        return True
    if CrossBattlefield.IsBattlefieldCallPlayer(playerID):
        return True
    if CrossChampionship.IsChampionshipPlayer(playerID):
        return True
    NeedCheckBillBoardType = IpyGameDataPY.GetFuncEvalCfg("PlayerViewCache", 2)
    #校验玩家是否上排行榜
    billboardMgr = GameWorld.GetBillboard()
@@ -140,116 +90,195 @@
        curBillboard = billboardMgr.FindBillboard(BillBoardType)
        if not curBillboard:
            continue
        if curBillboard.FindByID(PlayerID):
        if curBillboard.FindByID(playerID):
            return True
    #跨服榜单上的默认保留
    if GameWorld.IsCrossServer():
        billboardMgr = PyDataManager.GetCrossBillboardManager()
        for billboardType in ShareDefine.CrossBillboardTypeList:
            groupList = billboardMgr.GetBillboardGroupList(billboardType)
            for billboardType, groupValue1, groupValue2 in groupList:
                billboardObj = billboardMgr.GetCrossBillboard(billboardType, groupValue1, groupValue2)
                if billboardObj.FindByID(playerID):
                    return True
    return False
#    #校验玩家是否上排行榜
#    billboardMgr = GameWorld.GetBillboard()
#    for BillBoardType in NeedCheckBillBoardType:
#        curBillboard = billboardMgr.FindBillboard(BillBoardType)
#        if not curBillboard:
#            continue
#        if curBillboard.FindByID(PlayerID):
#            return True
#
#    #校验玩家竞技场是否进入排名
#    hightLadderMgr = GameWorld.GetHightLadderMgr()
#    hightLadderData = hightLadderMgr.FindPlayerData(PlayerID)
#    if hightLadderData:
#        if hightLadderData.GetOrder() < HighLadderLimitOrder:
#            return True
#
#    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(PlayerID)
#    if curPlayer:
#        # 非普通成员需保存
#        if PlayerFamily.GetPlayerFamilyMemberLV(curPlayer) != IPY_GameServer.fmlMember:
#            return True
#
#    return False
## //04 01 地图同步玩家数据到GameServer#tagMGUpdatePlayerCache
#  @param routeIndex, mapID, curPackData, tick
#  @return None
def OnMGUpdatePlayerCache(routeIndex, mapID, curPackData, tick):
    GameWorld.DebugLog('ViewCache### OnMGUpdatePlayerCache in %s'%curPackData.OutputString())
    PlayerID = curPackData.PlayerID
    PlayerLV = curPackData.PlayerLV
    if curPackData.IsLogouting:
        OnPlayerLogout(PlayerID, PlayerLV, \
                       curPackData.PropData, curPackData.ItemData, curPackData.PlusData)
    else:
        # 此处保存设置为True是为安全防范,比如突然断电,宕机等情况 导致误以为不保存,故等级可设置高一点
        UpdatePlayerCache(PlayerID, curPackData.PropData, \
                          curPackData.ItemData, curPackData.PlusData, True if PlayerLV > 150 else False)
    GameWorld.DebugLog('ViewCache### OnMGUpdatePlayerCache out')
def DelOutofTimeViewCacheData():
    ## 删除过期的查看缓存数据
    curTime = int(time.time())
    MaxTime = IpyGameDataPY.GetFuncCfg("PlayerViewCache", 3) * 3600 * 24
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    for playerID, viewCache in playerViewCachePyDict.items():
        passTime = curTime - viewCache.OffTime
        if passTime < MaxTime:
            continue
        if IsSaveAllViewCache(playerID):
            continue
        playerViewCachePyDict.pop(playerID)
    return
## //04 02 地图查询玩家缓存数据#tagMGQueryPlayerCache
#  @param routeIndex, mapID, curPackData, tick
#  @return None
def DeleteViewCache(playerID):
    ## 删除玩家缓存
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    playerViewCachePyDict.pop(playerID, None)
    GameWorld.DebugLog("删除查看缓存!", playerID)
    return
def FindViewCache(playerID, isAddNew=False, newPropData={}):
    ## 查找玩家缓存
    # @param newPropData: 新数据初始PropData {}, key: LV,RealmLV,Job,VIPLV,Name,FamilyID,FamilyName,FightPower
    curCache = None
    pyViewCacheMgr = PyDataManager.GetPlayerViewCachePyManager()
    playerViewCachePyDict = pyViewCacheMgr.playerViewCachePyDict
    if playerID in playerViewCachePyDict:
        curCache = playerViewCachePyDict[playerID]
    elif isAddNew:
        curCache = PyGameDataStruct.tagPlayerViewCachePy()
        curCache.PlayerID = playerID
        if newPropData:
            curCache.PropData = json.dumps(newPropData, ensure_ascii=False).replace(" ", "")
            curCache.PropDataSize = len(curCache.PropData)
        playerViewCachePyDict[playerID] = curCache
    return curCache
def GetCachePropDataDict(curCache):
    ## 获取缓存基础属性字典信息
    if not curCache:
        return {}
    if not hasattr(curCache, "PropDataDict"):
        curCache.PropDataDict = {}
    if not curCache.PropDataDict and curCache.PropData:
        curCache.PropDataDict = eval(curCache.PropData)
    return curCache.PropDataDict
#//04 01 地图同步玩家缓存数据到GameServer#tagMGUpdatePlayerCache
#
#struct    tagMGUpdatePlayerCache
#{
#    tagHead        Head;
#    DWORD        PlayerID;        //玩家ID
#    WORD        PlayerLV;        //玩家等级
#    BYTE        IsLogouting;        //本次是否为下线同步
#    DWORD        OffTime;        // 下线时间戳
#    WORD        PropDataSize;
#    char        PropData[PropDataSize];    //属性记录
#    WORD        PlusDataSize;
#    char        PlusData[PlusDataSize];    //扩展记录
#    WORD        ItemDataSize1;
#    char        ItemData1[ItemDataSize1];    //1阶装备数据
#    ...         ...
#    WORD        ItemDataSize20;
#    char        ItemData20[ItemDataSize20];
#};
def OnMGUpdatePlayerCache(routeIndex, mapID, curPackData, tick):
    playerID = curPackData.PlayerID
    playerLV = curPackData.PlayerLV
    isLogout = curPackData.IsLogouting
    GameWorld.DebugLog('ViewCache### OnMGUpdatePlayerCache isLogout=%s' % isLogout, playerID)
    isSaveAll = True # 是否保存所有数据
    if isLogout:
        #不需要保存离线数据的,直接删除缓存数据
        if not IsSaveDBViewCache(playerID, playerLV):
            DeleteViewCache(playerID)
            return
        isSaveAll = IsSaveAllViewCache(playerID)
        GameWorld.DebugLog("    isSaveAll=%s" % isSaveAll, playerID)
    curCache = FindViewCache(playerID, True)
    if not curCache:
        return
    curCache.LV = curPackData.PlayerLV
    curCache.OffTime = curPackData.OffTime
    if isLogout:
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if curPlayer:
            curCache.GeTuiID = curPlayer.GetGeTuiClientID()
            curCache.GeTuiIDSize = len(curCache.GeTuiID)
    curCache.PropDataDict = {} # 每次更新数据时,重置字典缓存,下次获取时重新eval缓存
    curCache.PropData = curPackData.PropData
    curCache.PropDataSize = curPackData.PropDataSize
    curCache.PlusData = curPackData.PlusData
    curCache.PlusDataSize = curPackData.PlusDataSize
    #GameWorld.DebugLog("    更新Prop数据: size=%s, %s" % (curCache.PropDataSize, curCache.PropData), playerID)
    #GameWorld.DebugLog("    更新Plus数据: size=%s, %s" % (curCache.PlusDataSize, curCache.PlusData), playerID)
    # 装备数据存储,不保存装备数据的话则清空
    for classLV in xrange(1, 20 + 1):
        if not isSaveAll:
            itemDataSize = 0
            itemData = ""
        else:
            itemDataSize = getattr(curPackData, "ItemDataSize%s" % classLV)
            if not itemDataSize:
                continue
            itemData = getattr(curPackData, "ItemData%s" % classLV)
        setattr(curCache, "ItemData%s" % classLV, itemData)
        setattr(curCache, "ItemDataSize%s" % classLV, itemDataSize)
        #GameWorld.DebugLog("    更新Item数据: classLV=%s,size=%s, %s" % (classLV, itemDataSize, itemData), playerID)
    #GameWorld.DebugLog("    %s" % curCache.outputString())
    # 同步更新助战信息
    if PlayerFBHelpBattle.IsInHelpBattleCheckInList(playerID):
        PropDataDict = GetCachePropDataDict(curCache)
        fightPower = PropDataDict.get("FightPower", 0)
        familyID = PropDataDict.get("FamilyID", 0)
        playerName = PropDataDict.get("Name", "")
        PlayerFBHelpBattle.UpdateCheckInPlayerInfo(playerID, fightPower, familyID, playerName)
    return
#//04 02 地图查询玩家缓存数据#tagMGQueryPlayerCache
#struct tagMGQueryPlayerCache
#{
#    tagHead        Head;
#    DWORD        PlayerID;        //玩家ID
#    DWORD        FindPlayerID;    //要查询的玩家ID
#    BYTE        EquipClassLV;    //大于0为查看指定境界阶装备信息,  0为查看默认信息
#};
def OnMGQueryPlayerCache(routeIndex, mapID, curPackData, tick):
    GameWorld.DebugLog('ViewCache### OnMGQueryPlayerCache in')
    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(curPackData.PlayerID)
    findPlayerID = curPackData.FindPlayerID
    curCache = ViewCacheMgr.FindCache(findPlayerID)
    equipClassLV = curPackData.EquipClassLV
    curCache = FindViewCache(findPlayerID)
    if not curCache:
        PlayerControl.NotifyCode(curPlayer, "ViewPlayer_OffLine")
        return
    Sync_PlayerCache(curPlayer, curCache, equipClassLV)
    return
def Sync_PlayerCache(curPlayer, curCache, equipClassLV=0):
    ## 同步玩家缓存
    if equipClassLV:
        itemData = ""
        if hasattr(curCache, "ItemDataSize%s" % equipClassLV):
            itemData = getattr(curCache, "ItemData%s" % equipClassLV)
        sendPack = ChPyNetSendPack.tagSCPlayerEquipCacheResult()
        sendPack.PlayerID = curCache.PlayerID
        sendPack.EquipClassLV = equipClassLV
        sendPack.ItemData = itemData
        sendPack.ItemDataSize = len(sendPack.ItemData)
        NetPackCommon.SendFakePack(curPlayer, sendPack)
        return
    
    #回包客户端
    sendPack = ChPyNetSendPack.tagSCQueryPlayerCacheResult()
    sendPack.PlayerID = findPlayerID
    if not curCache:
        #失败回包 空数据
        GameWorld.DebugLog("PlayerCache## OnMGQueryPlayerCache %s no found"%findPlayerID)
        sendPack.PropData = ""
        sendPack.PropDataSize = len(sendPack.PropData)
        sendPack.ItemData = ""
        sendPack.ItemDataSize = len(sendPack.ItemData)
        sendPack.PlusData = ""
        sendPack.PlusDataSize = len(sendPack.PlusData)
    else:
        #成功回包 缓存数据
        sendPack.PropData = curCache.GetPropData()
        sendPack.PropDataSize = len(sendPack.PropData)
        sendPack.ItemData = GetItemData(curCache)
        sendPack.ItemDataSize = len(sendPack.ItemData)
        sendPack.PlusData = GetPlusData(curCache)
        sendPack.PlusDataSize = len(sendPack.PlusData)
    sendPack.PlayerID = curCache.PlayerID
    sendPack.PropData = curCache.PropData
    sendPack.PropDataSize = len(sendPack.PropData)
    sendPack.PlusData = curCache.PlusData
    sendPack.PlusDataSize = len(sendPack.PlusData)
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    GameWorld.DebugLog('ViewCache### OnMGQueryPlayerCache out')
    return
## 获取玩家缓存模型装备信息
def GetPlayerCacheEquipView(findPlayerID):
    curCache = ViewCacheMgr.FindCache(findPlayerID)
    if not curCache:
        return
    itemData = GetItemData(curCache)
    if not itemData:
        return
    playerEquipList = []
    equipItemList = eval(itemData)
    for equipItemDict in equipItemList:
        equipIndex = equipItemDict["ItemIndex"]
        if equipIndex not in ShareDefine.RoleEquipType:
            continue
        itemID = equipItemDict["ItemID"]
        if not itemID:
            continue
        playerEquipList.append([itemID, equipIndex, equipItemDict.get("StarLV", 0), 0])
    return playerEquipList
#===============================================================================
# //B3 06 查询玩家的简短信息 #tagCGViewPlayerShortInfo
@@ -265,7 +294,7 @@
    answerPack = ChPyNetSendPack.tagGCAnswerPlayerShortInfo()
    answerPack.Clear()
    if not tagPlayer:
        curCache = ViewCacheMgr.FindCache(clientPack.PlayerID)
        curCache = FindViewCache(clientPack.PlayerID)
        if not curCache:
            # 实在找不到设置为初始化数据
            answerPack.PlayerID = clientPack.PlayerID
@@ -274,9 +303,9 @@
            answerPack.LV = 1
            answerPack.RealmLV = 1
            answerPack.OnlineType = ChConfig.Def_Offline
            answerPack.ServerGroupID = 0
        else:
            cacheDict = eval(curCache.GetPropData())
            cacheDict = GetCachePropDataDict(curCache)
            answerPack.PlayerID = clientPack.PlayerID
            answerPack.PlayerName = cacheDict["Name"]
            answerPack.Job = cacheDict["Job"]
@@ -284,6 +313,10 @@
            answerPack.RealmLV = cacheDict["RealmLV"]
            answerPack.OnlineType = ChConfig.Def_Offline
            
            if GameWorld.IsCrossServer():
                answerPack.ServerGroupID = cacheDict.get("ServerGroupID", 0)
            else:
                answerPack.ServerGroupID = GameWorld.GetServerGroupID()
    else:
        answerPack.PlayerID = clientPack.PlayerID
        answerPack.PlayerName = tagPlayer.GetName()
@@ -292,6 +325,7 @@
        answerPack.RealmLV = tagPlayer.GetOfficialRank()
        answerPack.OnlineType = ChConfig.Def_Online
        answerPack.IsInTeam = tagPlayer.GetTeamID() > 0
        answerPack.ServerGroupID = PlayerControl.GetPlayerServerGroupID(tagPlayer)
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    NetPackCommon.SendFakePack(curPlayer, answerPack)
@@ -299,17 +333,14 @@
def OnPlayerFamilyChange(playerID, familyID, familyName):
    GameWorld.DebugLog("ViewCache->OnPlayerFamilyChange", playerID)
    curCache = ViewCacheMgr.FindCache(playerID)
    curCache = FindViewCache(playerID)
    if not curCache:
        return
    PropData = eval(curCache.GetPropData())
    PropData = GetCachePropDataDict(curCache)
    PropData["FamilyID"] = familyID
    PropData["FamilyName"] = familyName
    playerLV = PropData["LV"]
    PropData = json.dumps(PropData, ensure_ascii=False)
    ItemData = curCache.GetItemData()
    PlusData = curCache.GetPlusData()
    UpdatePlayerCache(playerID, PropData, ItemData, PlusData, True if playerLV > 150 else False)
    PropData = json.dumps(PropData, ensure_ascii=False).replace(" ", "")
    curCache.PropData = PropData
    curCache.PropDataSize = len(curCache.PropData)
    return