From 3dd9789a269c121b2f6490aa1845fd2a192be370 Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期一, 15 四月 2019 16:35:21 +0800 Subject: [PATCH] 6459 【后端】【2.0】缥缈仙域开发单(进入动态分布线路的跨服地图分配规则优化,功能对应虚拟线路支持人数控制分流) --- ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py | 271 ++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 200 insertions(+), 71 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py index e9da661..ff3f03e 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py @@ -38,6 +38,52 @@ 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) @@ -76,31 +122,35 @@ return dataMapID return mapID -def ClientServerMsg_EnterFB(serverGroupID, msgData): +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: - zoneIpyData = CrossRealmPlayer.GetCrossZoneIpyDataByServerGroupID(dataMapID, serverGroupID) - if not zoneIpyData: - return - zoneID = zoneIpyData.GetZoneID() bossID = msgData["BossID"] if not CrossBoss.GetCrossBossIsAliveOrCanReborn(zoneID, bossID): GameWorld.DebugLog("当前跨服妖王死亡状态,不可进入! serverGroupID=%s,funcLineID=%s,zoneID=%s,bossID=%s" % (serverGroupID, funcLineID, zoneID, bossID)) return - mapCopyLineInfo = __GetCrossDynamicLineInfo(dataMapID, funcLineID, zoneID) - if not mapCopyLineInfo: - return - mapID, copyMapID, isOpenNew = mapCopyLineInfo + elif dataMapID in [ChConfig.Def_FBMapID_CrossGrasslandLing, ChConfig.Def_FBMapID_CrossGrasslandXian]: + copyMapPlayerMax = 10 - # 如果是等待线路启动中的直接返回,等启动好后再通知可进入 - if __AddWaitCrossFBOpenPlayer(mapID, copyMapID, isOpenNew, playerID, serverGroupID): - return 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] @@ -130,32 +180,57 @@ return -def __GetCrossDynamicLineInfo(dataMapID, funcLineID, zoneID): - ## 获取跨服分区对应动态分配的副本地图虚拟线路信息 - - isOpenNew = False +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):[mapID, copyMapID], ...}, ...} - if zoneLineKey in zoneLineDict: - mapID, copyMapID = zoneLineDict[zoneLineKey] - GameWorld.DebugLog("已存在该分区功能线路ID,不需要重新分配: zoneID=%s,funcLineID=%s,mapID=%s,copyMapID=%s" % (zoneID, funcLineID, mapID, copyMapID)) - return mapID, copyMapID, isOpenNew + 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 + if dataMapID == ChConfig.Def_FBMapID_CrossDemonKing: mapIDList = IpyGameDataPY.GetFuncEvalCfg("CrossDemonKingMap", 1) - + elif dataMapID == ChConfig.Def_FBMapID_CrossGrasslandLing: + mapIDList = [dataMapID] + elif dataMapID == ChConfig.Def_FBMapID_CrossGrasslandXian: + mapIDList = [dataMapID] # 其他地图待扩展 else: return - - usedMapCopyList = zoneLineDict.values() # 已经使用中的地图虚拟线路 + 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 usedMapCopyList: + if (mapID, copyMapID) not in PyGameData.g_crossDynamicLineCopyMapInfo: openMapID, openCopyMapID = mapID, copyMapID break if openMapID: @@ -164,50 +239,54 @@ GameWorld.ErrLog("没有空余的虚拟线路,无法动态开启跨服副本!dataMapID=%s, funcLineID=%s, zoneID=%s, mapIDList=%s" % (dataMapID, funcLineID, zoneID, mapIDList)) return - isOpenNew = True - mapID, copyMapID = openMapID, openCopyMapID - zoneLineDict[zoneLineKey] = [mapID, copyMapID] - propertyID = zoneID * 1000 + funcLineID + 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]) + msgInfo = str([copyMapID, propertyID, funcLineDataCache]) GameWorld.GetPlayerManager().MapServer_QueryPlayer(0, 0, 0, mapID, "OpenFB", msgInfo, len(msgInfo)) - return mapID, copyMapID, isOpenNew + return mapID, copyMapID, openState -def __AddWaitCrossFBOpenPlayer(mapID, copyMapID, isOpenNew, playerID, serverGroupID): - ## 添加跨服玩家进入等待动态副本虚拟线路开启队列 - - if mapID not in PyGameData.g_crossDynamicLineOpeningInfo: - PyGameData.g_crossDynamicLineOpeningInfo[mapID] = {} - openingMapCopyIDDict = PyGameData.g_crossDynamicLineOpeningInfo[mapID] # 跨服动态线路正在开启中的线路信息 {mapID:{copyMapID:{playerID:serverGroupID, ...}, ...}, ...} - - if isOpenNew or copyMapID in openingMapCopyIDDict: - if copyMapID not in openingMapCopyIDDict: - openingMapCopyIDDict[copyMapID] = {} - waitingPlayerDict = openingMapCopyIDDict[copyMapID] - waitingPlayerDict[playerID] = serverGroupID - GameWorld.Log("添加玩家进入等待跨服动态副本虚拟线路开启队列: mapID=%s,copyMapID=%s,isOpenNew=%s,playerID=%s,serverGroupID=%s" - % (mapID, copyMapID, isOpenNew, playerID, serverGroupID)) - GameWorld.Log(" PyGameData.g_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo) - return True - return False +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): ## 动态分配线路的地图虚拟线路启动成功 - if mapID not in PyGameData.g_crossDynamicLineOpeningInfo: + key = (mapID, copyMapID) + if key not in PyGameData.g_crossDynamicLineCopyMapInfo: return - openingCopyMapDict = PyGameData.g_crossDynamicLineOpeningInfo[mapID] - waitingPlayerDict = openingCopyMapDict.pop(copyMapID, {}) - if not waitingPlayerDict: - return + copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key] + copyMapObj.openState = 1 # 通知子服等待中的玩家可以进入副本 serverPlayerIDListDict = {} - for playerID, serverGroupID in waitingPlayerDict.items(): + for playerID, playerInfo in copyMapObj.waitPlayerDict.items(): + serverGroupID = playerInfo[0] if serverGroupID not in serverPlayerIDListDict: serverPlayerIDListDict[serverGroupID] = [] playerIDList = serverPlayerIDListDict[serverGroupID] @@ -220,26 +299,29 @@ 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_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo) + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo) + #GameWorld.DebugLog(" PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo) return -def OnCrossDynamicLineClose(mapID, copyMapID): +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, mapCopyInfo in zoneLineDict.items(): - if mapCopyInfo[0] == mapID and mapCopyInfo[1] == copyMapID: - zoneLineDict.pop(key) - break - - openingCopyMapDict = PyGameData.g_crossDynamicLineOpeningInfo.get(mapID, {}) - openingCopyMapDict.pop(copyMapID, {}) + 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 - GameWorld.DebugLog(" PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo) - GameWorld.DebugLog(" PyGameData.g_crossDynamicLineOpeningInfo=%s" % PyGameData.g_crossDynamicLineOpeningInfo) + 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): @@ -249,14 +331,61 @@ GameWorld.Log("动态分配虚拟线路地图重置 dataMapID=%s,mapID=%s,copyMapCount=%s" % (dataMapID, mapID, copyMapCount)) PyGameData.g_crossMapCopyMapCountDict[mapID] = copyMapCount - zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {}) - for key, mapCopyInfo in zoneLineDict.items(): - if mapCopyInfo[0] == mapID: - zoneLineDict.pop(key) + zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {}) + for key, funcLineObjList in zoneLineDict.items(): + for funcLineObj in funcLineObjList: + if funcLineObj.mapID == mapID: + funcLineObj.OnCopyMapClose(None) - PyGameData.g_crossDynamicLineOpeningInfo.pop(mapID, 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: 请求回调名 -- Gitblit v1.8.0