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