From e25e81a0a62baf5b6df6f9599cf791ef7085baa8 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期三, 08 十一月 2023 14:53:39 +0800
Subject: [PATCH] 0312 【主干】【BT0.1】增加持续回血 按目标最大血量(效果1307)
---
ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py | 524 ++++++++++++++++++++++++++++++++++++++++++++++++----------
1 files changed, 434 insertions(+), 90 deletions(-)
diff --git a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
index 96f3d31..10bcd77 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerFB.py
@@ -31,20 +31,28 @@
import GameWorld
import ChConfig
import IPY_PlayerDefine
+import CrossBattlefield
import CrossRealmPlayer
+import CrossChampionship
+import DataRecordPack
import CrossRealmMsg
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 +61,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,80 +147,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, realMapID, copyMapID, funcLineID = msgData
- playerIDList, dataMapID, mapID, copyMapID, funcLineID = msgData
-
- dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1)
- if dataMapID not in dynamicLineMapDict:
- return
- mapPosInfo = dynamicLineMapDict[dataMapID][0]
- posX, posY = mapPosInfo[:2]
- dist = mapPosInfo[2] if len(mapPosInfo) > 2 else 0
- if dist > 0:
- posX, posY = random.randint(posX - dist, posX + dist), random.randint(posY - dist, posY + dist)
-
for playerID in playerIDList:
curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
if not curPlayer:
continue
- CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, mapID, dataMapID, copyMapID, posX, posY, 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)
@@ -212,61 +344,170 @@
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
- dynamicLineMapDict = IpyGameDataPY.GetFuncEvalCfg("CrossDynamicLineMap", 1)
- if mapID not in dynamicLineMapDict:
+ 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
- dynamicMapIDList = dynamicLineMapDict[mapID][1]
+
+ 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 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
@@ -279,7 +520,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]
@@ -287,11 +528,11 @@
elif state == IPY_PlayerDefine.fbosClosed:
OnCrossDynamicLineClose(realMapID, copyMapID)
elif state == IPY_PlayerDefine.fbosOpen:
- OnCrossDynamicLineOpen(realMapID, copyMapID)
+ OnCrossDynamicLineOpen(mapID, realMapID, copyMapID)
return
-def OnCrossDynamicLineOpen(realMapID, copyMapID):
+def OnCrossDynamicLineOpen(mapID, realMapID, copyMapID):
## 动态分配线路的地图虚拟线路启动成功
key = (realMapID, copyMapID)
@@ -310,11 +551,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)
@@ -356,13 +597,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):
@@ -398,12 +653,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):
@@ -419,10 +698,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
##--------------------------------------------------------------------------------------------------
@@ -433,6 +728,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:
@@ -458,6 +754,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
@@ -471,10 +773,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:
@@ -500,3 +798,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