From ee4705284b8064b4f3964dfd13c22386b7d5c20f Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期三, 26 二月 2025 15:48:24 +0800 Subject: [PATCH] 10407 【越南】【英语】【BT】【GM】【砍树】周末BUG汇总(修复排行榜功能开启时同步开服活动榜数据异常bug;) --- ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py | 745 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 740 insertions(+), 5 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py index dd9aa79..db4f559 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py @@ -30,8 +30,102 @@ import PlayerTeam import GameWorld import ChConfig +import IPY_PlayerDefine +import CrossBattlefield +import CrossRealmPlayer +import CrossChampionship +import PlayerPackData +import DataRecordPack +import CrossRealmMsg +import PyDataManager +import ShareDefine +import CrossBoss +import time + +DynamicShuntType_No = 0 # 不分流 +DynamicShuntType_Fill = 1 # 填满式分流,按存在的线路人数多的优先填充,都满后开启新线路 +DynamicShuntType_Equally = 2 # 均摊式分流,按存在的线路人数少的优先填充,都满后开启新线路 #--------------------------------------------------------------------- +## 跨服地图动态分配的功能线路,如果有人数上限的,则同分区同地图玩法的可能同时存在多个相同功能线路的数据 +## {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...} +class CrossFuncLineInfo(): + + def __init__(self): + self.realMapID = 0 + self.copyMapID = 0 + self.newFuncLineNum = 0 + self.funcLineDataCache = None # 功能线路自定义缓存数据 + return + + def OnCopyMapClose(self): + self.realMapID = 0 + self.copyMapID = 0 + return + +## 跨服地图动态分配的虚拟线路信息 {(mapID, copyMapID):CrossCopyMapInfo, ...} +class CrossCopyMapInfo(): + + def __init__(self, zoneID, funcMapID, funcLineID): + self.zoneID = zoneID + self.funcMapID = funcMapID + self.funcLineID = funcLineID + self.newFuncLineNum = 0 + self.realMapID = 0 + self.copyMapID = 0 + self.openState = IPY_PlayerDefine.fbosClosed + self.fbPlayerDict = {} # 副本中的玩家信息 {playerID:serverGroupID, ...} + self.waitPlayerDict = {} # 等待进入的玩家信息 {playerID:[serverGroupID, tick], ...} + self.offlinePlayerDict = {} # 掉线的玩家信息,非主动退出的 {playerID:serverGroupID, ...} + self.enterPlayerIDList = [] # 有进入过此分线的玩家ID列表,不会清除 [playerID, ...] + return + + def GetCopyMapPlayerCount(self, includeOffline, tick): + ## 获取该分线玩家数 + # @param includeOffline: 是否包含离线玩家 + + # 移除请求进入超时的玩家 + for waitPlayerID, playerInfo in self.waitPlayerDict.items(): + _, requestTick = playerInfo + if tick - requestTick > 60000: # 请求进入时间保留1分钟 + self.waitPlayerDict.pop(waitPlayerID) + + # 判断是否超过人数上限 + fbPlayerCount, waitPlayerCount = len(self.fbPlayerDict), len(self.waitPlayerDict) + totalPlayerCount = fbPlayerCount + waitPlayerCount + if includeOffline: + totalPlayerCount += len(self.offlinePlayerDict) + + return totalPlayerCount + + def IsMustCopyMapPlayer(self, playerID, checkTeam=True): + ## 是否必定在此分线的玩家, 在请求队列里 或 曾经进入到该分线的,都强制认为属于该分线的玩家 + if playerID in self.waitPlayerDict or playerID in self.enterPlayerIDList: + return True + if self.openState != IPY_PlayerDefine.fbosOpen: + return False + if not checkTeam: + return False + # 队友强制在一起 + funcTeamMgr = PyDataManager.GetDBPyFuncTeamManager() + teamID = funcTeamMgr.GetPlayerTeamID(playerID, self.funcMapID) + if not teamID: + return False + funcTeam = funcTeamMgr.GetFuncTeam(teamID) + if not funcTeam: + return False + for memID in funcTeam.GetMemberIDList(): + if memID in self.waitPlayerDict or memID in self.enterPlayerIDList: + GameWorld.DebugLog("强制和队友在一条线路! funcMapID=%s,memID=%s,realMapID=%s,copyMapID=%s" + % (self.funcMapID, memID, self.realMapID, self.copyMapID), playerID) + return True + return False + + def OnRequestEnterCrossCopyMap(self, playerID, tick, copyMapPlayerMax, includeOffline): + if not copyMapPlayerMax or self.IsMustCopyMapPlayer(playerID): + return True + return self.GetCopyMapPlayerCount(includeOffline, tick) < copyMapPlayerMax + #--------------------------------------------------------------------- def GetFBLineIpyData(mapID, lineID, isDefaultLine=True): mapID = GetRecordMapID(mapID) @@ -70,13 +164,598 @@ return dataMapID return mapID +def ClientServerMsg_EnterFB(serverGroupID, msgData, tick): + ## 收到子服请求进入动态分配的跨服副本 + playerID = msgData["PlayerID"] + mapID = msgData["MapID"] + funcLineID = msgData["FuncLineID"] + playerLV = msgData["LV"] + + if mapID == ChConfig.Def_FBMapID_CrossChampionship: + #CrossChampionship.OnRequestChampionshipVSRoom(playerID, serverGroupID) + return + + zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByServerGroupID(mapID, serverGroupID) + if not zoneIpyData: + return + zoneID = zoneIpyData.GetZoneID() + + openHour, openMinute = None, None + dynamicShuntType = DynamicShuntType_Fill + includeOffline = False + tagCopyMapObj = None + + # 基础验证是否可进入等 及 数据准备 + if mapID == ChConfig.Def_FBMapID_CrossDemonKing: + bossID = msgData["BossID"] + if not CrossBoss.GetCrossBossIsAliveOrCanReborn(zoneID, bossID): + GameWorld.ErrLog("当前跨服妖王死亡状态,不可进入! funcLineID=%s,zoneID=%s,bossID=%s" % (funcLineID, zoneID, bossID), playerID) + return + + elif mapID in [ChConfig.Def_FBMapID_CrossGrasslandLing, ChConfig.Def_FBMapID_CrossGrasslandXian]: + pass + + elif mapID == ChConfig.Def_FBMapID_CrossBattlefield: + openTimeInfo = CrossBattlefield.GetCrossBattlefieldOpenTime(serverGroupID, zoneID, playerID) + if not openTimeInfo: + #GameWorld.ErrLog("非活动时间或未开启! funcLineID=%s,zoneID=%s" % (funcLineID, zoneID), playerID) + return + dynamicShuntType = DynamicShuntType_Equally + isCallBattle, openHour, openMinute = openTimeInfo + if isCallBattle: + # 召集场次默认 funcLineID 为0,不分等级,不分流 + funcLineID = 0 + dynamicShuntType = DynamicShuntType_No + includeOffline = True + else: + return + + dynamicLineMaxPlayerCountDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 2) + copyMapPlayerMin, copyMapPlayerMax = dynamicLineMaxPlayerCountDict.get(mapID, [0, 0]) # 0为不限制人数,默认不限制 + + # 除个别地图外,最优先进入上次进入的未关闭分线 + if mapID not in []: + for _, copyMapObj in PyGameData.g_crossDynamicLineCopyMapInfo.items(): + if copyMapObj.IsMustCopyMapPlayer(playerID): + tagCopyMapObj = copyMapObj + break + + # 如果没有进入过,则按功能看是否有特殊指定规则 + if tagCopyMapObj == None: + if mapID == ChConfig.Def_FBMapID_CrossBattlefield: + if isCallBattle: + copyMapPlayerMax = IpyGameDataPY.GetFuncCfg("CrossBattlefieldCall", 2) + tagCopyMapObj = CrossBattlefield.GetCallPlayerCopymapObj(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, includeOffline, tick) + + # 如果还没有取到对应的分流线,则按默认规则处理 + if tagCopyMapObj == None and dynamicShuntType: + # 非特殊动态规则,走常规逻辑 + dynamicLineLVRangeDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 4) + if mapID in dynamicLineLVRangeDict: + lvRangeList = dynamicLineLVRangeDict[mapID] + for lvFuncLineID, lvRange in enumerate(lvRangeList): + if lvRange[0] <= playerLV <= lvRange[1]: + funcLineID = lvFuncLineID + copyMapPlayerMin, copyMapPlayerMax = lvRange[2], lvRange[3] + GameWorld.DebugLog("进入跨服地图等级自动适配功能线路ID: mapID=%s,playerLV=%s,funcLineID=%s,copyMapPlayerMin=%s,copyMapPlayerMax=%s" + % (mapID, playerLV, funcLineID, copyMapPlayerMin, copyMapPlayerMax)) + break + + shuntPlayerMax = copyMapPlayerMax + + minCountTimeDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 3) # 分流下限人数有效时间配置,单位秒,{dataMapID:秒, ...} + if mapID in minCountTimeDict: + playerMinTimeSet = minCountTimeDict[mapID] + curTime = GameWorld.GetServerTime() + if openHour == None or openMinute == None: + GameWorld.ErrLog("副本开启时间未知! mapID=%s,funcLineID=%s,zoneID=%s" % (mapID, funcLineID, zoneID), playerID) + return + + openDateTimeStr = "%d-%02d-%02d %02d:%02d:00" % (curTime.year, curTime.month, curTime.day, openHour, openMinute) + openDateTime = GameWorld.ChangeStrToDatetime(openDateTimeStr) + passTime = curTime - openDateTime + ''' + 在线(包含请求中) <= 单场下限值 + 在线(包含请求中)+ 离线 <= 单场上限值 + + 前X秒大于 单场下限值 开新一场 + 任意时刻大于 单场上限值 必开新一场 + ''' + if passTime.seconds <= playerMinTimeSet: + shuntPlayerMax = copyMapPlayerMin + includeOffline = False + else: + shuntPlayerMax = copyMapPlayerMax + includeOffline = True + + tagCopyMapObj = __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, + shuntPlayerMax, copyMapPlayerMax, includeOffline, tick, dynamicShuntType) + + if not tagCopyMapObj: + PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBFull") + GameWorld.ErrLog("找不到可分流的副本线路! mapID=%s,funcLineID=%s,zoneID=%s" % (mapID, funcLineID, zoneID), playerID) + return + + realMapID, copyMapID, openState = tagCopyMapObj.realMapID, tagCopyMapObj.copyMapID, tagCopyMapObj.openState + + if openState >= IPY_PlayerDefine.fbosWaitForClose: + PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose") + GameWorld.ErrLog("分流的副本线路关闭中! mapID=%s,funcLineID=%s,zoneID=%s,realMapID=%s,copyMapID=%s,openState=%s" + % (mapID, funcLineID, zoneID, realMapID, copyMapID, openState), playerID) + return + + tagCopyMapObj.waitPlayerDict[playerID] = [serverGroupID, tick] + GameWorld.DebugLog(" 分配进入跨服场景: realMapID=%s, copyMapID=%s, openState=%s" % (realMapID, copyMapID, openState), playerID) + if openState == IPY_PlayerDefine.fbosOpen: + funcLineID = tagCopyMapObj.funcLineID + playerIDList = [playerID] + # 分流地图的地图数据ID直接使用场景ID,因为分流地图实际上是两张不同的地图,所以直接使用场景ID,不然会导致上传跨服玩家数据时坐标为0 + retInfo = [playerIDList, mapID, realMapID, realMapID, copyMapID, funcLineID] + CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID]) + + dataDict = {} + dataDict.update(msgData) + dataDict.update({"mapID":mapID, "realMapID":realMapID, "copyMapID":copyMapID, "realFuncLineID":funcLineID, "openState":openState}) + DataRecordPack.SendEventPack("CrossFBRequest", dataDict) + return tagCopyMapObj + +def CrossServerMsg_EnterFBRet(msgData, tick): + ## 收到跨服服务器动态分配的跨服副本进入信息 + playerIDList, dataMapID, mapID, realMapID, copyMapID, funcLineID = msgData + + for playerID in playerIDList: + curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID) + if not curPlayer: + continue + CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, realMapID, mapID, copyMapID, lineID=funcLineID) + + return + +def __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, shuntPlayerMax, copyMapPlayerMax, includeOffline, tick, dynamicShuntType): + '''获取跨服分区对应动态分配的副本地图虚拟线路信息, 由于需要支持多地图分流,所以直接由GameServer管理分配 + 每个功能线路支持按人数分流,超过最大人数后可开启一条相同功能线路的虚拟线路进行分流,所以同一分区同一地图的功能线路ID可能对应多条虚拟线路 + 分流方式: + DynamicShuntType_Fill = 1 # 填满式分流,按存在的线路人数多的优先填充,都满后开启新线路 + DynamicShuntType_Equally = 2 # 均摊式分流,按存在的线路人数少的优先填充,都满后开启新线路 + 分流规则: + 时间仅决定分流人数,不影响常规分配逻辑 + 1. 优先分配到人数小于分流人数的场次 + 2. 超过分流人数的场次依次往人数少的场次分配 + 3. 当当前已开放的场次都达到人数分流人数,则开启新场次,没有空闲的场,则往未达到人数上限的场次依次分配,直到达到所有场次上限 + + 关于 shuntPlayerMax 的选择: 可根据副本规则制定 + 如前X分钟内可设定一个小于 copyMapPlayerMax 的分流人数值快速铺满各分流场次 + 当大于X分钟后则可设置 shuntPlayerMax = copyMapPlayerMax 进行饱和分流 + + 当所有分流场次达到 shuntPlayerMax 后,可尝试开启新分流线路,进行分流 + shuntPlayerMax < copyMapPlayerMax 的情况,如果没有办法开启新分流线路,则可继续强制根据分流类型分配线路,只要未达到 copyMapPlayerMax 人数,还是可以进入副本的 + shuntPlayerMax >= copyMapPlayerMax 的情况,如果没有办法开启新分流线路,则标识副本所有线路已达到饱和状态,不能再进入副本了 + + 当 shuntPlayerMax 为 0 时,达标不限制人数上限,及不分流,都在同一条线路,一般跨服副本不建议设置为0,人数太多,不合理 + + @param shuntPlayerMax: 分流最大人数限制 + @param copyMapPlayerMax: 实际最大可容纳的人数限制,一般大于等于分流人数限制 + @param includeOffline: 是否包含本线路离线玩家 + @param dynamicShuntType: 分流类型,可选择 填满式分流 或 均摊式分流 + ''' + + zoneLineKey = (zoneID, funcLineID) + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + funcLineObjList = zoneLineDict.get(zoneLineKey, []) + isPlayerFullMax = (shuntPlayerMax >= copyMapPlayerMax) + + GameWorld.DebugLog("获取动态分流线路: serverGroupID=%s,mapID=%s,funcLineID=%s,zoneID=%s,shuntPlayerMax=%s,copyMapPlayerMax=%s,includeOffline=%s,dynamicShuntType=%s" + % (serverGroupID, mapID, funcLineID, zoneID, shuntPlayerMax, copyMapPlayerMax, includeOffline, dynamicShuntType), playerID) + #GameWorld.DebugLog(" funcLineObjList=%s" % funcLineObjList, playerID) + + canUseShuntLine = False # 是否直接使用分流线路,如果否的话,当人数未达到真正饱和时,则还可直接分配对应分流类型的线路 + minPlayerCount, maxPlayerCount = 0, 0 + minCopyMapObj, maxCopyMapObj = None, None + for _, funcLineObj in enumerate(funcLineObjList, 1): + realMapID, copyMapID = funcLineObj.realMapID, funcLineObj.copyMapID + #GameWorld.DebugLog(" realMapID=%s, copyMapID=%s" % (realMapID, copyMapID)) + if not realMapID: + continue + + key = (realMapID, copyMapID) + if key not in PyGameData.g_crossDynamicLineCopyMapInfo: + GameWorld.ErrLog("已经分配的虚拟线路不存在缓存对应关系里!zoneID=%s,funcLineID=%s,realMapID=%s,copyMapID=%s" + % (zoneID, funcLineID, realMapID, copyMapID)) + continue + + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] + openState = copyMapObj.openState + if openState >= IPY_PlayerDefine.fbosWaitForClose: + # 没有限制分流人数的情况,代表都在同一场,这种情况下当副本已经在关闭的状态下,则代表已经结束了,不可再进入 + if not shuntPlayerMax: + PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose") + return + #GameWorld.DebugLog(" 虚拟线路等待关闭中! realMapID=%s,copyMapID=%s" % (realMapID, copyMapID)) + continue + + if not shuntPlayerMax or copyMapObj.IsMustCopyMapPlayer(playerID): + return copyMapObj + + playerCount = copyMapObj.GetCopyMapPlayerCount(includeOffline, tick) + if minCopyMapObj == None or playerCount < minPlayerCount: + minPlayerCount = playerCount + minCopyMapObj = copyMapObj + + if maxCopyMapObj == None or playerCount > maxPlayerCount: + maxPlayerCount = playerCount + maxCopyMapObj = copyMapObj + + # 存在线路未达到规定的分流人数,则可直接使用分流线路 + if playerCount < shuntPlayerMax: + canUseShuntLine = True + + #GameWorld.DebugLog(" isPlayerFullMax=%s,canUseShuntLine=%s" % (isPlayerFullMax, canUseShuntLine)) + dynamicShuntCopyMap = None # 分流类型决定的分流线路 + + # 均摊式 + if dynamicShuntType == DynamicShuntType_Equally: + dynamicShuntCopyMap = minCopyMapObj + # 填满式 + elif dynamicShuntType == DynamicShuntType_Fill: + dynamicShuntCopyMap = maxCopyMapObj + else: + return + + shuntCopyMap = None + if canUseShuntLine: + shuntCopyMap = dynamicShuntCopyMap + + #GameWorld.DebugLog(" shuntCopyMap=%s" % shuntCopyMap) + if not shuntCopyMap: + isLog = isPlayerFullMax + shuntCopyMap = __OpenNewFuncLine(mapID, zoneID, funcLineID, isLog) + + # 即 shuntPlayerMax < copyMapPlayerMax 的情况 + if not shuntCopyMap and not isPlayerFullMax: + shuntCopyMap = dynamicShuntCopyMap + + if not shuntCopyMap: + return + + shuntCopyMap.waitPlayerDict[playerID] = [serverGroupID, tick] + + return shuntCopyMap + +def __OpenNewFuncLine(mapID, zoneID, funcLineID, isLog=True): + ## 新开功能线路分流 + + if mapID not in PyGameData.g_crossDynamicLineInfo: + PyGameData.g_crossDynamicLineInfo[mapID] = {} + zoneLineDict = PyGameData.g_crossDynamicLineInfo[mapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...} + zoneLineKey = (zoneID, funcLineID) + if zoneLineKey not in zoneLineDict: + zoneLineDict[zoneLineKey] = [] + funcLineObjList = zoneLineDict[zoneLineKey] + + dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1) + dynamicMapIDList = dynamicLineMapDict.get(mapID, [mapID]) + + openMapID, openCopyMapID = 0, 0 + maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(dynamicMapIDList[0], 0) + # 外层为虚拟线路总数遍历,内层为分流地图,这样可以均匀分流到各个分流地图,减少单地图压力 + for copyMapID in xrange(maxCopyMapCount): + for realMapID in dynamicMapIDList: + if copyMapID >= PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0): + continue + if (realMapID, copyMapID) not in PyGameData.g_crossDynamicLineCopyMapInfo: + openMapID, openCopyMapID = realMapID, copyMapID + break + if openMapID: + break + + if not openMapID: + if isLog: + GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本! mapID=%s,zoneID=%s,funcLineID=%s,dynamicMapIDList=%s" + % (mapID, zoneID, funcLineID, dynamicMapIDList)) + return + + realMapID, copyMapID = openMapID, openCopyMapID + + newFuncLineObj = None + for funcLineObj in funcLineObjList: + if not funcLineObj.realMapID: + newFuncLineObj = funcLineObj + break + + if newFuncLineObj == None: + newFuncLineObj = CrossFuncLineInfo() + funcLineObjList.append(newFuncLineObj) + + newFuncLineNum = 1 + lineNumList = [lineObj.newFuncLineNum for lineObj in funcLineObjList] + for num in xrange(1, len(lineNumList) + 1): + if num not in lineNumList: + newFuncLineNum = num + break + GameWorld.DebugLog(" lineNumList=%s,newFuncLineNum=%s" % (lineNumList, newFuncLineNum)) + + newFuncLineObj.realMapID = realMapID + newFuncLineObj.copyMapID = copyMapID + newFuncLineObj.newFuncLineNum = newFuncLineNum + + copyMapObj = CrossCopyMapInfo(zoneID, mapID, funcLineID) + copyMapObj.realMapID = realMapID + copyMapObj.copyMapID = copyMapID + copyMapObj.newFuncLineNum = newFuncLineNum + + key = (realMapID, copyMapID) + PyGameData.g_crossDynamicLineCopyMapInfo[key] = copyMapObj + + propertyID = int("%d%03d%02d" % (zoneID, funcLineID, newFuncLineNum)) + GameWorld.Log(" 新开分区动态副本功能线路: zoneID=%s,funcLineID=%s,newFuncLineNum=%s,realMapID=%s,copyMapID=%s,propertyID=%s" + % (zoneID, funcLineID, newFuncLineNum, realMapID, copyMapID, propertyID)) + + # 通知地图开启新的地图虚拟分线 + funcLineDataCache = newFuncLineObj.funcLineDataCache + msgInfo = str([copyMapID, propertyID, funcLineDataCache]) + GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, realMapID, "OpenFB", msgInfo, len(msgInfo)) + return copyMapObj + +def SendMapOpenFBEx(realMapID, copyPropertyList): + ## 通知地图开启副本线路 + # @param realMapID: 地图ID + # @param copyPropertyList: [[copyMapID, propertyID], ...] + msgInfo = str(copyPropertyList) + GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, realMapID, "OpenFBEx", msgInfo, len(msgInfo)) + GameWorld.Log("SendMapOpenFBEx: realMapID=%s,msgInfo=%s" % (realMapID, msgInfo)) + return + +def OpenCrossDynamicLineBySys(zoneID, mapID, funcLineIDList, checkExist): + ## 系统开启跨服动态线路 + + GameWorld.Log(" 系统开启跨服动态线路: zoneID=%s, mapID=%s, funcLineIDList=%s, checkExist=%s" % (zoneID, mapID, funcLineIDList, checkExist)) + + for funcLineID in funcLineIDList: + + if checkExist: + fincLineObj = None + zoneLineKey = (zoneID, funcLineID) + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + funcLineObjList = zoneLineDict.get(zoneLineKey, []) + for funcLineObj in funcLineObjList: + if funcLineObj.realMapID: + fincLineObj = funcLineObj + break + + if fincLineObj: + GameWorld.ErrLog("已经存在开放中的线路,不重复开启动态副本线路! mapID=%s, funcLineID=%s, zoneID=%s, realMapID=%s, copyMapID=%s" + % (mapID, funcLineID, zoneID, funcLineObj.realMapID, funcLineObj.copyMapID)) + continue + + __OpenNewFuncLine(mapID, zoneID, funcLineID) + + return + +def GetCrossDynamicLineZoneID(mapID, realMapID, copyMapID): + ## 获取跨服动态分配的虚拟线路对应分区ID + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + for key, funcLineObjList in zoneLineDict.items(): + for funcLineObj in funcLineObjList: + if funcLineObj.realMapID == realMapID and funcLineObj.copyMapID == copyMapID: + zoneID = key[0] + return zoneID + return 0 + +def OnCrossDynamicLineStateChange(msgList): + mapID, realMapID, copyMapID, state = msgList[:4] + + if state == IPY_PlayerDefine.fbosWaitForClose: + funcLineDataCache = msgList[3] + OnCrossDynamicLineWaitForClose(realMapID, copyMapID, funcLineDataCache) + elif state == IPY_PlayerDefine.fbosClosed: + OnCrossDynamicLineClose(realMapID, copyMapID) + elif state == IPY_PlayerDefine.fbosOpen: + OnCrossDynamicLineOpen(mapID, realMapID, copyMapID) + else: + key = (realMapID, copyMapID) + if key in PyGameData.g_crossDynamicLineCopyMapInfo: + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] + copyMapObj.openState = state + return + +def OnCrossDynamicLineOpen(mapID, realMapID, copyMapID): + ## 动态分配线路的地图虚拟线路启动成功 + + key = (realMapID, copyMapID) + if key not in PyGameData.g_crossDynamicLineCopyMapInfo: + return + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] + copyMapObj.openState = IPY_PlayerDefine.fbosOpen + funcLineID = copyMapObj.funcLineID + + # 通知子服等待中的玩家可以进入副本 + serverPlayerIDListDict = {} + for playerID, playerInfo in copyMapObj.waitPlayerDict.items(): + serverGroupID = playerInfo[0] + if serverGroupID not in serverPlayerIDListDict: + serverPlayerIDListDict[serverGroupID] = [] + playerIDList = serverPlayerIDListDict[serverGroupID] + playerIDList.append(playerID) + + recordMapID = GetRecordMapID(realMapID) + GameWorld.Log("动态分配虚拟线路启动成功,通知子服等待玩家可进入: recordMapID=%s,mapID=%s,realMapID=%s,copyMapID=%s,serverPlayerIDListDict=%s" + % (recordMapID, mapID, realMapID, copyMapID, serverPlayerIDListDict)) + for serverGroupID, playerIDList in serverPlayerIDListDict.items(): + retInfo = [playerIDList, recordMapID, mapID, realMapID, copyMapID, funcLineID] + 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 OnCrossDynamicLineWaitForClose(realMapID, copyMapID, funcLineDataCache): + ## 动态分配线路的地图虚拟线路关闭 + + mapID = GetRecordMapID(realMapID) + GameWorld.Log("动态分配虚拟线路等待关闭 mapID=%s,realMapID=%s,copyMapID=%s,funcLineDataCache=%s" % (mapID, realMapID, copyMapID, funcLineDataCache)) + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + for key, funcLineObjList in zoneLineDict.items(): + for funcLineObj in funcLineObjList: + if funcLineObj.realMapID == realMapID and funcLineObj.copyMapID == copyMapID: + funcLineObj.funcLineDataCache = funcLineDataCache + zoneID, funcLineID = key + GameWorld.Log(" 分区对应功能线路虚拟分线等待关闭: zoneID=%s,mapID=%s,funcLineID=%s" % (zoneID, mapID, funcLineID)) + break + + key = (realMapID, copyMapID) + if key in PyGameData.g_crossDynamicLineCopyMapInfo: + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] + copyMapObj.openState = IPY_PlayerDefine.fbosWaitForClose + + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo) + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo) + return + +def OnCrossDynamicLineClose(mapID, copyMapID): + ## 动态分配线路的地图虚拟线路关闭 + + 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.realMapID == mapID and funcLineObj.copyMapID == copyMapID: + funcLineObj.OnCopyMapClose() + zoneID, funcLineID = key + GameWorld.Log(" 分区对应功能线路虚拟分线关闭: zoneID=%s,dataMapID%s,funcLineID=%s" % (zoneID, dataMapID, funcLineID)) + if not funcLineObj.funcLineDataCache: + funcLineObjList.remove(funcLineObj) + break + + key = (mapID, copyMapID) + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo.pop(key, None) + if not copyMapObj: + return + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo) + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo) + + playerCount = 0 + zoneID = copyMapObj.zoneID + funcLineID = copyMapObj.funcLineID + playerCountInfo = [playerCount] + SyncClientServerCrossFBFuncLinePlayerCount(zoneID, mapID, funcLineID, playerCountInfo) + + #如果虚拟分线关闭时,有掉线的玩家,则通知子服重置这些玩家的跨服状态 + for playerID, serverGroupID in copyMapObj.offlinePlayerDict.items(): + CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_ExitCrossServer, playerID, [serverGroupID]) + + return + +def OnCrossDynamicMapReset(msgList): + ## 动态分配线路的地图重置 + realMapID, copyMapCount = msgList + mapID = GetRecordMapID(realMapID) + GameWorld.Log("动态分配虚拟线路地图重置 mapID=%s,realMapID=%s,copyMapCount=%s" % (mapID, realMapID, copyMapCount)) + PyGameData.g_crossMapCopyMapCountDict[realMapID] = copyMapCount + + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + for key, funcLineObjList in zoneLineDict.items(): + for funcLineObj in funcLineObjList: + if funcLineObj.realMapID == realMapID: + funcLineObj.OnCopyMapClose() + + for key in PyGameData.g_crossDynamicLineCopyMapInfo.keys(): + if key[0] == realMapID: + 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.offlinePlayerDict.pop(playerID, None) + copyMapObj.fbPlayerDict[playerID] = serverGroupID + if playerID not in copyMapObj.enterPlayerIDList: + copyMapObj.enterPlayerIDList.append(playerID) + + #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) + #GameWorld.DebugLog(" 离线中的玩家ID: %s" % copyMapObj.offlinePlayerDict) + + playerCount = len(copyMapObj.fbPlayerDict) # 等待进入的暂时不算 + zoneID = copyMapObj.zoneID + funcLineID = copyMapObj.funcLineID + playerCountInfo = [playerCount] + SyncClientServerCrossFBFuncLinePlayerCount(zoneID, mapID, funcLineID, playerCountInfo) + return + +def SyncClientServerCrossFBFuncLinePlayerCount(zoneID, mapID, funcLineID, playerCountInfo): + ## 同步子服跨服副本功能线路人数 + ## 注意: 此人数不是一个精确人数值,只是一个大概人数值,不用很精确,暂时只玩家进入时同步人数信息,玩家退出暂不处理 + mapID = GetRecordMapID(mapID) + if mapID not in ChConfig.Def_NeedCountFBFuncLinePlayerCrossMap: + return + zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByZoneID(mapID, zoneID) + if not zoneIpyData: + return + serverGroupIDList = zoneIpyData.GetServerGroupIDList() + playerCountInfo = [mapID, funcLineID, playerCountInfo] + CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_FBPlayerCount, playerCountInfo, serverGroupIDList) + 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) + + crossMapID = PlayerControl.GetCrossMapID(curPlayer) + # 不是主动退出的 + if crossMapID: + copyMapObj.offlinePlayerDict[playerID] = PlayerControl.GetPlayerServerGroupID(curPlayer) + + #GameWorld.DebugLog("玩家退出动态分配的跨服地图: GetMapID=%s,GetRealMapID=%s,GetFBID()=%s,crossMapID=%s" + # % (curPlayer.GetMapID(), mapID, copyMapID, crossMapID), playerID) + #GameWorld.DebugLog(" 副本中的玩家ID: %s" % copyMapObj.fbPlayerDict) + #GameWorld.DebugLog(" 等待中的玩家ID: %s" % copyMapObj.waitPlayerDict) + #GameWorld.DebugLog(" 离线中的玩家ID: %s" % copyMapObj.offlinePlayerDict) + return + +def CrossServerMsg_FBPlayerCount(msgData): + ## 收到跨服服务器同步的副本功能线路人数信息 + + mapID, funcLineID, playerCountInfo = msgData + if mapID not in PyGameData.g_crossFBFuncLinePlayerCountInfo: + PyGameData.g_crossFBFuncLinePlayerCountInfo[mapID] = {} + fbLinePlayerInfoDict = PyGameData.g_crossFBFuncLinePlayerCountInfo[mapID] + fbLinePlayerInfoDict[funcLineID] = playerCountInfo + return + +##-------------------------------------------------------------------------------------------------- + ## 请求进入副本分线 # @param curPlayer: 请求玩家 # @param queryCallName: 请求回调名 # @param sendCMD: 请求的命令 根据请求类型和请求命令来决定最终操作 # @return None def EnterFBLine(curPlayer, queryCallName, sendCMD, tick): - GameWorld.Log("EnterFBLine()...queryCallName=%s,sendCMD=%s" % (queryCallName, sendCMD), curPlayer.GetPlayerID()) + playerID = curPlayer.GetPlayerID() + GameWorld.Log("EnterFBLine()...queryCallName=%s,sendCMD=%s" % (queryCallName, sendCMD), playerID) playerManager = GameWorld.GetPlayerManager() try: mapInfo = eval(sendCMD) @@ -91,6 +770,7 @@ #if mapInfo and len(mapInfo) == 2: tagMapID = mapInfo[0] tagLineID = mapInfo[1] + extendValue1 = mapInfo[2] if len(mapInfo) > 2 else None fbLineIpyData = GetFBLineIpyData(tagMapID, tagLineID) sceneMapID = tagMapID if not fbLineIpyData else fbLineIpyData.GetMapID() @@ -99,8 +779,21 @@ GameWorld.ErrLog("目标副本地图不存在!tagMapID=%s,sceneMapID=%s" % (tagMapID, sceneMapID), curPlayer.GetPlayerID()) return + if isinstance(extendValue1, dict): + if extendValue1.get("msgType", "") == "MirrorBattle": + PlayerPackData.OnMGReuestPlayerPackData(extendValue1) + playerManager.MapServer_QueryPlayer(curPlayer.GetPlayerID(), ChConfig.queryType_EnterFB, 0, sceneMapID, + queryCallName, sendCMD, len(sendCMD), curPlayer.GetRouteServerIndex()) + return + # 组队副本, 有队伍的情况才验证其他队员可否进入,否则代表单人进入 if gameMap.GetMapFBType() == ChConfig.fbtTeam: + if tagMapID == ChConfig.Def_FBMapID_Love: + onlyDoubleTeam = IpyGameDataPY.GetFuncCfg("LoveFB", 1) + if onlyDoubleTeam: + if PlayerTeam.CheckTeamOnLineCount(curPlayer.GetTeam(), includeTJG=False) != 2: + PlayerControl.NotifyCode(curPlayer, "OnlyTwoMemTeamCanEnter", [tagMapID]) + return PlayerTeam.OnEnterFBTeamAsk(curPlayer, PlayerTeam.TeamFBAskType_Enter, tagMapID, tagLineID, tick) return @@ -114,10 +807,6 @@ if not GameWorldFamilyWar.CheckPlayerCanEnterFamilyWarFBMap(curPlayer): return - elif tagMapID == ChConfig.Def_FBMapID_FamilyBossMap: - if not PlayerFamilyBoss.CheckIsFamilyBossFBOpen(curPlayer.GetFamilyID(), tagMapID): - GameWorld.Log("EnterFBLine mapID=%s is familyBossFB, but is not open!" % tagMapID) - return #守卫人皇 是否已参加 elif tagMapID == ChConfig.Def_FBMapID_FamilyInvade: if curPlayer.GetFamilyID() in PyGameData.g_swrhJoinRecord: @@ -143,3 +832,49 @@ playerManager.MapServer_QueryPlayer(curPlayer.GetPlayerID(), ChConfig.queryType_EnterFB, 0, tagMapID, queryCallName, sendCMD, len(sendCMD), curPlayer.GetRouteServerIndex()) return + +def Send_CrossServerMsg_EnterVSRoomRet(vsRoomDict, serverGroupIDList=None): + ## 发送子服跨服对战房间请求进入结果 + # @param vsRoomDict: {roomID:{playerID:playerInfo, ...}, } + # playerInfo key + # serverGroupID 所属服务器分组ID + # regMapInfo 传送跨服注册信息 [registerMap, mapID, dataMapID, copyMapID, posX, posY] + CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterVSRoomRet, vsRoomDict, serverGroupIDList) + return + +def CrossServerMsg_EnterVSRoomRet(msgData, tick): + ## 跨服对战房间请求进入结果 + + curServerGroupID = GameWorld.GetServerGroupID() + GameWorld.DebugLog("=== 跨服PK对战房间请求进入结果 === curServerGroupID=%s" % curServerGroupID) + vsRoomDict = msgData + for roomID, playerDict in vsRoomDict.items(): + GameWorld.DebugLog(" roomID=%s,playerDict=%s" % (roomID, playerDict)) + for playerID, playerInfo in playerDict.items(): + if "serverGroupID" in playerInfo: + serverGroupID = playerInfo["serverGroupID"] + if serverGroupID != curServerGroupID: + GameWorld.DebugLog(" 不是本服玩家,不处理!playerID=%s,serverGroupID=%s" % (playerID, serverGroupID)) + continue + + player = GameWorld.GetPlayerManager().FindPlayerByID(playerID) + if not player: + GameWorld.DebugLog(" 玩家不在线, playerID=%s" % (playerID)) + continue + if PlayerControl.GetIsTJG(player): + GameWorld.DebugLog(" 玩家脱机中, playerID=%s" % (playerID)) + continue + + if "regMapInfo" not in playerInfo: + continue + regMapInfo = playerInfo["regMapInfo"] + if len(regMapInfo) != 6: + continue + registerMap, mapID, dataMapID, copyMapID, posX, posY = regMapInfo + + PlayerControl.SetVsRoomId(player, roomID, True) + # 通知地图玩家匹配成功, 上传数据, 准备进入跨服服务器 + CrossRealmPlayer.SendCrossRealmReg(player, registerMap, mapID, dataMapID, copyMapID, posX, posY) + + return + -- Gitblit v1.8.0