From 164b1a9e2eb3f9908e95e0050de828f0e35cb74b Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期一, 23 五月 2022 16:38:31 +0800
Subject: [PATCH] 9415 【BT5】【后端】古神战场(初版:包含战场副本外的所有功能;副本中暂仅支持击杀玩家玩法)

---
 ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py |  356 +++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 297 insertions(+), 59 deletions(-)

diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
index a8d5e9e..48853d9 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -31,19 +31,26 @@
 import GameWorld
 import ChConfig
 import IPY_PlayerDefine
+import CrossBattlefield
 import CrossRealmPlayer
 import CrossRealmMsg
 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
     
@@ -52,37 +59,48 @@
         self.copyMapID = 0
         return
 
-## 跨服地图动态分配的虚拟线路信息
+## 跨服地图动态分配的虚拟线路信息 {(mapID, copyMapID):CrossCopyMapInfo, ...}
 class CrossCopyMapInfo():
     
     def __init__(self, zoneID, funcLineID):
         self.zoneID = zoneID
         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:
-            return False
+        totalPlayerCount = fbPlayerCount + waitPlayerCount
+        if includeOffline:
+            totalPlayerCount += len(self.offlinePlayerDict)
+            
+        return totalPlayerCount
+    
+    def IsMustCopyMapPlayer(self, playerID):
+        ## 是否必定在此分线的玩家, 在请求队列里 或 曾经进入到该分线的,都强制认为属于该分线的玩家
+        return playerID in self.waitPlayerDict or playerID in self.enterPlayerIDList
         
-        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,37 +145,127 @@
     playerID = msgData["PlayerID"]
     mapID = msgData["MapID"]
     funcLineID = msgData["FuncLineID"]
+    playerLV = msgData["LV"]
     
     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(zoneID)
+        if not openTimeInfo:
+            PlayerControl.NotifyCodeCross(serverGroupID, playerID, "FBIsNotOpen")
+            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]
+        retInfo = [playerIDList, mapID, realMapID, copyMapID, funcLineID]
+        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])
+        
+    return tagCopyMapObj
 
 def CrossServerMsg_EnterFBRet(msgData, tick):
     ## 收到跨服服务器动态分配的跨服副本进入信息
@@ -172,26 +280,50 @@
         
     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)
@@ -203,59 +335,161 @@
         copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]
         openState = copyMapObj.openState
         if openState == IPY_PlayerDefine.fbosWaitForClose:
-            if not copyMapPlayerMax:
-                PlayerControl.CrossNotifyCode(serverGroupID, playerID, "HazyRegionClose")
+            # 没有限制分流人数的情况,代表都在同一场,这种情况下当副本已经在关闭的状态下,则代表已经结束了,不可再进入
+            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, 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 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
@@ -345,6 +579,8 @@
                 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)
@@ -401,6 +637,8 @@
     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)

--
Gitblit v1.8.0