From 64053673d311feed12814ba9ad921d9448f9a07c Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期六, 28 九月 2024 15:37:01 +0800 Subject: [PATCH] 5416 【工具】兑换码优化(兑换码邮件内容文件json支持不同的appID独立文件配置;) --- ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py | 540 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 460 insertions(+), 80 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py index 286a645..3298d23 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py @@ -31,20 +31,29 @@ import GameWorld import ChConfig import IPY_PlayerDefine +import CrossBattlefield import CrossRealmPlayer +import CrossChampionship +import DataRecordPack import CrossRealmMsg +import PyDataManager import ShareDefine import CrossBoss +import time -import random +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 @@ -53,36 +62,68 @@ self.copyMapID = 0 return -## 跨服地图动态分配的虚拟线路信息 +## 跨服地图动态分配的虚拟线路信息 {(mapID, copyMapID):CrossCopyMapInfo, ...} class CrossCopyMapInfo(): - def __init__(self, zoneID, funcLineID): + 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 OnRequestEnterCrossCopyMap(self, playerID, serverGroupID, tick, copyMapPlayerMax): - # 已经在请求队列里,可进入 - if playerID in self.waitPlayerDict or not copyMapPlayerMax: - self.waitPlayerDict[playerID] = [serverGroupID, tick] - return True + def GetCopyMapPlayerCount(self, includeOffline, tick): + ## 获取该分线玩家数 + # @param includeOffline: 是否包含离线玩家 # 移除请求进入超时的玩家 for waitPlayerID, playerInfo in self.waitPlayerDict.items(): - serverGroupID, requestTick = playerInfo + _, requestTick = playerInfo if tick - requestTick > 60000: # 请求进入时间保留1分钟 self.waitPlayerDict.pop(waitPlayerID) # 判断是否超过人数上限 fbPlayerCount, waitPlayerCount = len(self.fbPlayerDict), len(self.waitPlayerDict) - if fbPlayerCount + waitPlayerCount >= copyMapPlayerMax: + 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 - self.waitPlayerDict[playerID] = [serverGroupID, tick] - return True + 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): @@ -127,71 +168,192 @@ 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() - dynamicLineMaxPlayerCountDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 2) - copyMapPlayerMax = dynamicLineMaxPlayerCountDict.get(mapID, 0) # 0为不限制人数,默认不限制 + 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.DebugLog("当前跨服妖王死亡状态,不可进入! serverGroupID=%s,funcLineID=%s,zoneID=%s,bossID=%s" % (serverGroupID, funcLineID, 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 - mapCopyLineInfo = __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, tick) - if not mapCopyLineInfo: + 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 = mapCopyLineInfo - if openState != IPY_PlayerDefine.fbosOpen: + + 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 - playerIDList = [playerID] - retInfo = [playerIDList, mapID, realMapID, copyMapID, funcLineID] - CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID]) - 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, copyMapID, funcLineID = msgData + playerIDList, dataMapID, mapID, realMapID, copyMapID, funcLineID = msgData for playerID in playerIDList: curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID) if not curPlayer: continue - CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, mapID, dataMapID, copyMapID, lineID=funcLineID) + CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, realMapID, mapID, copyMapID, lineID=funcLineID) return -def __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, copyMapPlayerMax, tick): +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) - if mapID not in PyGameData.g_crossDynamicLineInfo: - PyGameData.g_crossDynamicLineInfo[mapID] = {} - zoneLineDict = PyGameData.g_crossDynamicLineInfo[mapID] # 跨服动态线路信息 {dataMapID:{(zoneID, funcLineID):[CrossFuncLineInfo, CrossFuncLineInfo, ...], ...}, ...} - if zoneLineKey not in zoneLineDict: - zoneLineDict[zoneLineKey] = [] - funcLineObjList = zoneLineDict[zoneLineKey] + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {}) + funcLineObjList = zoneLineDict.get(zoneLineKey, []) + isPlayerFullMax = (shuntPlayerMax >= copyMapPlayerMax) - newFuncLineNum = None - newFuncLineObj = None - for index, funcLineObj in enumerate(funcLineObjList, 1): + 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: - if not newFuncLineObj: - newFuncLineNum, newFuncLineObj = index, funcLineObj continue key = (realMapID, copyMapID) @@ -202,60 +364,171 @@ copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] openState = copyMapObj.openState - if openState == IPY_PlayerDefine.fbosWaitForClose: - if not copyMapPlayerMax: - PlayerControl.CrossNotifyCode(serverGroupID, playerID, "HazyRegionClose") + if openState >= IPY_PlayerDefine.fbosWaitForClose: + # 没有限制分流人数的情况,代表都在同一场,这种情况下当副本已经在关闭的状态下,则代表已经结束了,不可再进入 + if not shuntPlayerMax: + PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose") return - #GameWorld.DebugLog(" 虚拟线路等待关闭中! index=%s,realMapID=%s,copyMapID=%s" % (index, realMapID, copyMapID)) + #GameWorld.DebugLog(" 虚拟线路等待关闭中! realMapID=%s,copyMapID=%s" % (realMapID, copyMapID)) continue - canEnter = copyMapObj.OnRequestEnterCrossCopyMap(playerID, serverGroupID, tick, copyMapPlayerMax) - if canEnter: - #GameWorld.DebugLog("可进入动态分布的虚拟线路! realMapID=%s,copyMapID=%s,openState=%s" % (realMapID, copyMapID, openState)) - #GameWorld.DebugLog(" 副本中的玩家ID: %s" % copyMapObj.fbPlayerDict) - #GameWorld.DebugLog(" 等待中的玩家ID: %s" % copyMapObj.waitPlayerDict) - return realMapID, copyMapID, openState + 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 - for realMapID in dynamicMapIDList: - maxCopyMapCount = PyGameData.g_crossMapCopyMapCountDict.get(realMapID, 0) - for copyMapID in xrange(maxCopyMapCount): + 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: - GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本!mapID=%s, funcLineID=%s, zoneID=%s, dynamicMapIDList=%s" - % (mapID, funcLineID, zoneID, dynamicMapIDList)) + 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 = len(funcLineObjList) - realMapID, copyMapID = openMapID, openCopyMapID + + 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 - funcLineDataCache = newFuncLineObj.funcLineDataCache + newFuncLineObj.newFuncLineNum = newFuncLineNum + + copyMapObj = CrossCopyMapInfo(zoneID, mapID, funcLineID) + copyMapObj.realMapID = realMapID + copyMapObj.copyMapID = copyMapID + copyMapObj.newFuncLineNum = newFuncLineNum key = (realMapID, 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,realMapID=%s,copyMapID=%s,propertyID=%s" - % (zoneID, funcLineID, realMapID, copyMapID, propertyID)) + 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 realMapID, copyMapID, openState + 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 @@ -268,7 +541,7 @@ return 0 def OnCrossDynamicLineStateChange(msgList): - realMapID, copyMapID, state = msgList[:3] + mapID, realMapID, copyMapID, state = msgList[:4] if state == IPY_PlayerDefine.fbosWaitForClose: funcLineDataCache = msgList[3] @@ -276,11 +549,15 @@ elif state == IPY_PlayerDefine.fbosClosed: OnCrossDynamicLineClose(realMapID, copyMapID) elif state == IPY_PlayerDefine.fbosOpen: - OnCrossDynamicLineOpen(realMapID, copyMapID) - + 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(realMapID, copyMapID): +def OnCrossDynamicLineOpen(mapID, realMapID, copyMapID): ## 动态分配线路的地图虚拟线路启动成功 key = (realMapID, copyMapID) @@ -299,11 +576,11 @@ playerIDList = serverPlayerIDListDict[serverGroupID] playerIDList.append(playerID) - mapID = GetRecordMapID(realMapID) - GameWorld.Log("动态分配虚拟线路启动成功,通知子服等待玩家可进入: mapID=%s,realMapID=%s,copyMapID=%s,serverPlayerIDListDict=%s" - % (mapID, realMapID, copyMapID, serverPlayerIDListDict)) + 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, mapID, realMapID, copyMapID, funcLineID] + retInfo = [playerIDList, recordMapID, mapID, realMapID, copyMapID, funcLineID] CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID]) #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo) @@ -345,13 +622,27 @@ 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) - PyGameData.g_crossDynamicLineCopyMapInfo.pop(key, {}) - + 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): @@ -387,12 +678,36 @@ 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): @@ -408,10 +723,26 @@ copyMapObj.waitPlayerDict.pop(playerID, None) copyMapObj.fbPlayerDict.pop(playerID, None) - #GameWorld.DebugLog("玩家退出动态分配的跨服地图: GetMapID=%s,GetRealMapID=%s,GetFBID()=%s" - # % (curPlayer.GetMapID(), mapID, copyMapID), playerID) + 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 ##-------------------------------------------------------------------------------------------------- @@ -422,6 +753,7 @@ # @param sendCMD: 请求的命令 根据请求类型和请求命令来决定最终操作 # @return None def EnterFBLine(curPlayer, queryCallName, sendCMD, tick): + playerID = curPlayer.GetPlayerID() GameWorld.Log("EnterFBLine()...queryCallName=%s,sendCMD=%s" % (queryCallName, sendCMD), curPlayer.GetPlayerID()) playerManager = GameWorld.GetPlayerManager() try: @@ -447,6 +779,12 @@ # 组队副本, 有队伍的情况才验证其他队员可否进入,否则代表单人进入 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 @@ -460,10 +798,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: @@ -489,3 +823,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