ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -21,15 +21,69 @@
#---------------------------------------------------------------------
import GameWorldBoss
import PlayerFamilyBoss
import PlayerHorsePetBoss
import GameWorldFamilyWar
import PlayerControl
import PyGameData
import IpyGameDataPY
import PlayerDBGSEvent
import PlayerTeam
import GameWorld
import ChConfig
import CrossRealmPlayer
import CrossRealmMsg
import ShareDefine
import CrossBoss
import random
#---------------------------------------------------------------------
## 跨服地图动态分配的功能线路,如果有人数上限的,则同分区同地图玩法的可能同时存在多个相同功能线路的数据
class CrossFuncLineInfo():
    def __init__(self):
        self.mapID = 0
        self.copyMapID = 0
        self.funcLineDataCache = None # 功能线路自定义缓存数据
        return
    def OnCopyMapClose(self, funcLineDataCache):
        self.mapID = 0
        self.copyMapID = 0
        self.funcLineDataCache = funcLineDataCache
        return
## 跨服地图动态分配的虚拟线路信息
class CrossCopyMapInfo():
    def __init__(self, zoneID, funcLineID):
        self.zoneID = zoneID
        self.funcLineID = funcLineID
        self.openState = 0
        self.fbPlayerDict = {} # 副本中的玩家信息 {playerID:serverGroupID, ...}
        self.waitPlayerDict = {} # 等待进入的玩家信息 {playerID:[serverGroupID, tick], ...}
        return
    def OnRequestEnterCrossCopyMap(self, playerID, serverGroupID, tick, copyMapPlayerMax):
        # 已经在请求队列里,可进入
        if playerID in self.waitPlayerDict or not copyMapPlayerMax:
            self.waitPlayerDict[playerID] = [serverGroupID, tick]
            return True
        # 移除请求进入超时的玩家
        for waitPlayerID, playerInfo in self.waitPlayerDict.items():
            serverGroupID, requestTick = playerInfo
            if tick - requestTick > 60000: # 请求进入时间保留1分钟
                self.waitPlayerDict.pop(waitPlayerID)
        # 判断是否超过人数上限
        fbPlayerCount, waitPlayerCount = len(self.fbPlayerDict), len(self.waitPlayerDict)
        if fbPlayerCount + waitPlayerCount >= copyMapPlayerMax:
            return False
        self.waitPlayerDict[playerID] = [serverGroupID, tick]
        return True
#---------------------------------------------------------------------
def GetFBLineIpyData(mapID, lineID, isDefaultLine=True):
    mapID = GetRecordMapID(mapID)
@@ -68,6 +122,265 @@
            return dataMapID
    return mapID
def ClientServerMsg_EnterFB(serverGroupID, msgData, tick):
    ## 收到子服请求进入动态分配的跨服副本
    playerID = msgData["PlayerID"]
    dataMapID = msgData["DataMapID"]
    funcLineID = msgData["FuncLineID"]
    zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByServerGroupID(dataMapID, serverGroupID)
    if not zoneIpyData:
        return
    zoneID = zoneIpyData.GetZoneID()
    copyMapPlayerMax = 0 # 0为不限制人数,默认不限制
    if dataMapID == ChConfig.Def_FBMapID_CrossDemonKing:
        bossID = msgData["BossID"]
        if not CrossBoss.GetCrossBossIsAliveOrCanReborn(zoneID, bossID):
            GameWorld.DebugLog("当前跨服妖王死亡状态,不可进入! serverGroupID=%s,funcLineID=%s,zoneID=%s,bossID=%s" % (serverGroupID, funcLineID, zoneID, bossID))
            return
    elif dataMapID in [ChConfig.Def_FBMapID_CrossGrasslandLing, ChConfig.Def_FBMapID_CrossGrasslandXian]:
        copyMapPlayerMax = 10
    else:
        return
    mapCopyLineInfo = __GetCrossDynamicLineInfo(playerID, serverGroupID, dataMapID, funcLineID, zoneID, copyMapPlayerMax, tick)
    if not mapCopyLineInfo:
        return
    mapID, copyMapID, openState = mapCopyLineInfo
    if not openState:
        return
    playerIDList = [playerID]
    retInfo = [playerIDList, dataMapID, mapID, copyMapID]
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
    return
def CrossServerMsg_EnterFBRet(msgData, tick):
    ## 收到跨服服务器动态分配的跨服副本进入信息
    playerIDList, dataMapID, mapID, copyMapID = msgData
    dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1)
    if dataMapID not in dynamicLineMapDict:
        return
    mapPosInfo = dynamicLineMapDict[dataMapID][0]
    posX, posY = mapPosInfo[:2]
    dist = mapPosInfo[2] if len(mapPosInfo) > 2 else 0
    if dist > 0:
        posX, posY = random.randint(posX - dist, posX + dist), random.randint(posY - dist, posY + dist)
    for playerID in playerIDList:
        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
        if not curPlayer:
            continue
        CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, mapID, dataMapID, copyMapID, posX, posY)
    return
def __GetCrossDynamicLineInfo(playerID, serverGroupID, dataMapID, funcLineID, zoneID, copyMapPlayerMax, tick):
    '''获取跨服分区对应动态分配的副本地图虚拟线路信息, 由于需要支持多地图分流,所以直接由GameServer管理分配
            每个功能线路支持按人数分流,超过最大人数后可开启一条相同功能线路的虚拟线路进行分流,所以同一分区同一地图的功能线路ID可能对应多条虚拟线路
    '''
    zoneLineKey = (zoneID, funcLineID)
    if dataMapID not in PyGameData.g_crossDynamicLineInfo:
        PyGameData.g_crossDynamicLineInfo[dataMapID] = {}
    zoneLineDict = PyGameData.g_crossDynamicLineInfo[dataMapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...}
    if zoneLineKey not in zoneLineDict:
        zoneLineDict[zoneLineKey] = []
    funcLineObjList = zoneLineDict[zoneLineKey]
    newFuncLineNum = None
    newFuncLineObj = None
    for index, funcLineObj in enumerate(funcLineObjList, 1):
        mapID, copyMapID = funcLineObj.mapID, funcLineObj.copyMapID
        if not mapID:
            newFuncLineNum, newFuncLineObj = index, funcLineObj
            break
        key = (mapID, copyMapID)
        if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
            GameWorld.ErrLog("已经分配的虚拟线路不存在缓存对应关系里!zoneID=%s,funcLineID=%s,mapID=%s,copyMapID=%s"
                             % (zoneID, funcLineID, mapID, copyMapID))
            continue
        copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
        openState = copyMapObj.openState
        canEnter = copyMapObj.OnRequestEnterCrossCopyMap(playerID, serverGroupID, tick, copyMapPlayerMax)
        if canEnter:
            #GameWorld.DebugLog("可进入动态分布的虚拟线路! mapID=%s,copyMapID=%s,openState=%s" % (mapID, copyMapID, openState))
            #GameWorld.DebugLog("    副本中的玩家ID: %s" % copyMapObj.fbPlayerDict)
            #GameWorld.DebugLog("    等待中的玩家ID: %s" % copyMapObj.waitPlayerDict)
            return mapID, copyMapID, openState
    dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1)
    if dataMapID not in dynamicLineMapDict:
        return
    mapIDList = dynamicLineMapDict[dataMapID][1]
    openMapID, openCopyMapID = 0, 0
    for mapID in mapIDList:
        maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(mapID, 0)
        for copyMapID in xrange(maxCopyMapCount):
            if (mapID, copyMapID) not in PyGameData.g_crossDynamicLineCopyMapInfo:
                openMapID, openCopyMapID = mapID, copyMapID
                break
        if openMapID:
            break
    if not openMapID:
        GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本!dataMapID=%s, funcLineID=%s, zoneID=%s, mapIDList=%s"
                         % (dataMapID, funcLineID, zoneID, mapIDList))
        return
    if newFuncLineObj == None:
        newFuncLineObj = CrossFuncLineInfo()
        funcLineObjList.append(newFuncLineObj)
        newFuncLineNum = len(funcLineObjList)
    mapID, copyMapID = openMapID, openCopyMapID
    newFuncLineObj.mapID = mapID
    newFuncLineObj.copyMapID = copyMapID
    funcLineDataCache = newFuncLineObj.funcLineDataCache
    key = (mapID, copyMapID)
    copyMapObj = CrossCopyMapInfo(zoneID, funcLineID)
    PyGameData.g_crossDynamicLineCopyMapInfo[key] = copyMapObj
    copyMapObj.waitPlayerDict[playerID] = [serverGroupID, tick]
    openState = copyMapObj.openState
    propertyID = int("%d%03d%d" % (zoneID, funcLineID, newFuncLineNum))
    GameWorld.DebugLog("不存在该分区功能线路ID,重新分配: zoneID=%s,funcLineID=%s,mapID=%s,copyMapID=%s,propertyID=%s"
                       % (zoneID, funcLineID, mapID, copyMapID, propertyID))
    # 通知地图开启新的地图虚拟分线
    msgInfo = str([copyMapID, propertyID, funcLineDataCache])
    GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, mapID, "OpenFB", msgInfo, len(msgInfo))
    return mapID, copyMapID, openState
def GetCrossDynamicLineZoneID(dataMapID, mapID, copyMapID):
    ## 获取跨服动态分配的虚拟线路对应分区ID
    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {})
    for key, funcLineObjList in zoneLineDict.items():
        for funcLineObj in funcLineObjList:
            if funcLineObj.mapID == mapID and funcLineObj.copyMapID == copyMapID:
                zoneID = key[0]
                return zoneID
    return 0
def OnCrossDynamicLineOpen(mapID, copyMapID):
    ## 动态分配线路的地图虚拟线路启动成功
    key = (mapID, copyMapID)
    if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
        return
    copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
    copyMapObj.openState = 1
    # 通知子服等待中的玩家可以进入副本
    serverPlayerIDListDict = {}
    for playerID, playerInfo in copyMapObj.waitPlayerDict.items():
        serverGroupID = playerInfo[0]
        if serverGroupID not in serverPlayerIDListDict:
            serverPlayerIDListDict[serverGroupID] = []
        playerIDList = serverPlayerIDListDict[serverGroupID]
        playerIDList.append(playerID)
    dataMapID = GetRecordMapID(mapID)
    GameWorld.Log("动态分配虚拟线路启动成功,通知子服等待玩家可进入: dataMapID=%s,mapID=%s,copyMapID=%s,serverPlayerIDListDict=%s"
                  % (dataMapID, mapID, copyMapID, serverPlayerIDListDict))
    for serverGroupID, playerIDList in serverPlayerIDListDict.items():
        retInfo = [playerIDList, dataMapID, mapID, copyMapID]
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo)
    return
def OnCrossDynamicLineClose(mapID, copyMapID, funcLineDataCache):
    ## 动态分配线路的地图虚拟线路关闭
    dataMapID = GetRecordMapID(mapID)
    GameWorld.Log("动态分配虚拟线路关闭 dataMapID=%s,mapID=%s,copyMapID=%s" % (dataMapID, mapID, copyMapID))
    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {})
    for key, funcLineObjList in zoneLineDict.items():
        for funcLineObj in funcLineObjList:
            if funcLineObj.mapID == mapID and funcLineObj.copyMapID == copyMapID:
                funcLineObj.OnCopyMapClose(funcLineDataCache)
                zoneID, funcLineID = key
                GameWorld.Log("    分区对应功能线路虚拟分线关闭: zoneID=%s,dataMapID%s,funcLineID=%s" % (zoneID, dataMapID, funcLineID))
                break
    key = (mapID, copyMapID)
    PyGameData.g_crossDynamicLineCopyMapInfo.pop(key, {})
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo)
    return
def OnCrossDynamicMapReset(mapID, copyMapCount):
    ## 动态分配线路的地图重置
    dataMapID = GetRecordMapID(mapID)
    GameWorld.Log("动态分配虚拟线路地图重置 dataMapID=%s,mapID=%s,copyMapCount=%s" % (dataMapID, mapID, copyMapCount))
    PyGameData.g_crossMapCopyMapCountDict[mapID] = copyMapCount
    zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {})
    for key, funcLineObjList in zoneLineDict.items():
        for funcLineObj in funcLineObjList:
            if funcLineObj.mapID == mapID:
                funcLineObj.OnCopyMapClose(None)
    for key in PyGameData.g_crossDynamicLineCopyMapInfo.keys():
        if key[0] == mapID:
            PyGameData.g_crossDynamicLineCopyMapInfo.pop(key)
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)
    #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo)
    return
def PlayerLoginLoadCrossMapOK(curPlayer):
    ## 玩家登录跨服地图
    mapID, copyMapID = curPlayer.GetRealMapID(), curPlayer.GetFBID()
    key = (mapID, copyMapID)
    if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
        return
    copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
    playerID = curPlayer.GetPlayerID()
    serverGroupID = PlayerControl.GetPlayerServerGroupID(curPlayer)
    copyMapObj.waitPlayerDict.pop(playerID, None)
    copyMapObj.fbPlayerDict[playerID] = serverGroupID
    #GameWorld.DebugLog("玩家登录动态分配的跨服地图: GetMapID=%s,GetRealMapID=%s,GetFBID()=%s,serverGroupID=%s"
    #                   % (curPlayer.GetMapID(), mapID, copyMapID, serverGroupID), playerID)
    #GameWorld.DebugLog("    副本中的玩家ID: %s" % copyMapObj.fbPlayerDict)
    #GameWorld.DebugLog("    等待中的玩家ID: %s" % copyMapObj.waitPlayerDict)
    return
def OnPlayerDisconnectCrossServer(curPlayer):
    ## 玩家离开跨服服务器
    mapID, copyMapID = curPlayer.GetRealMapID(), curPlayer.GetFBID()
    key = (mapID, copyMapID)
    if key not in PyGameData.g_crossDynamicLineCopyMapInfo:
        return
    copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
    playerID = curPlayer.GetPlayerID()
    copyMapObj.waitPlayerDict.pop(playerID, None)
    copyMapObj.fbPlayerDict.pop(playerID, None)
    #GameWorld.DebugLog("玩家退出动态分配的跨服地图: GetMapID=%s,GetRealMapID=%s,GetFBID()=%s"
    #                   % (curPlayer.GetMapID(), mapID, copyMapID), playerID)
    #GameWorld.DebugLog("    副本中的玩家ID: %s" % copyMapObj.fbPlayerDict)
    #GameWorld.DebugLog("    等待中的玩家ID: %s" % copyMapObj.waitPlayerDict)
    return
##--------------------------------------------------------------------------------------------------
## 请求进入副本分线
#  @param curPlayer: 请求玩家
#  @param queryCallName: 请求回调名
@@ -103,7 +416,7 @@
        return
    
    #封魔坛副本判断里面的BOSS是否到了刷新时间
    if tagMapID == ChConfig.Def_FBMapID_SealDemon:
    if tagMapID in ChConfig.WorldBossFBMapIDList:
        bossID = mapInfo[2]
        if not GameWorldBoss.GetBossIsAliveOrCanReborn(bossID):
            return
@@ -121,7 +434,22 @@
        if curPlayer.GetFamilyID() in PyGameData.g_swrhJoinRecord:
            PlayerControl.NotifyCode(curPlayer, "TheEmperor1")
            return
    #多仙盟BOSS 是否已结束
    elif tagMapID == ChConfig.Def_FBMapID_AllFamilyBoss:
        if not PlayerFamilyBoss.IsInAllFamilyBoss(tagLineID):
            #活动未开启
            return
        if PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_AllFamilyBossTime):
            #BOSS已被击杀
            return
    #骑宠BOSS 是否已结束
    elif tagMapID == ChConfig.Def_FBMapID_HorsePetBoss:
        if not PlayerHorsePetBoss.IsInHorsePetBoss():
            #活动未开启
            return
        if PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_HorsePetBossTime % tagLineID):
            #BOSS已被击杀
            return
    # MapServer_QueryPlayer(int srcPlayerID, int queryType, int queryID, int mapID, char *callName, char *cmd,WORD cmdLen, int RouteServerIndex)
    playerManager.MapServer_QueryPlayer(curPlayer.GetPlayerID(), ChConfig.queryType_EnterFB, 0, tagMapID,
                queryCallName, sendCMD, len(sendCMD), curPlayer.GetRouteServerIndex())