| #!/usr/bin/python  | 
| # -*- coding: GBK -*-  | 
| #---------------------------------------------------------------------  | 
| #  | 
| #---------------------------------------------------------------------  | 
| ##@package PlayerFB  | 
| # @todo: Íæ¼Ò¸±±¾  | 
| #  | 
| # @author: hxp  | 
| # @date 2013-08-23  | 
| # @version 1.5  | 
| #  | 
| # @note  | 
| # @change: "2014-04-16 18:00" hxp Ôö¼Ó¼Ò×åboss¸±±¾½øÈëÅÐ¶Ï  | 
| # @change: "2015-01-09 10:30" hxp ¸ÄΪGetRouteServerIndex  | 
| # @change: "2015-03-20 15:00" hxp Ìõ¼þÅжÏÐÞ¸Ä  | 
| # @change: "2015-10-22 23:00" hxp Ôö¼Ó¿ç·þPK  | 
| # @change: "2017-01-04 12:00" hxp Ôö¼ÓÒì½çÈëÇÖ  | 
| #---------------------------------------------------------------------  | 
| #"""Version = 2017-01-04 12:00"""  | 
| #---------------------------------------------------------------------  | 
| import GameWorldBoss  | 
| import PlayerFamilyBoss  | 
| import PlayerHorsePetBoss  | 
| import GameWorldFamilyWar  | 
| import PlayerControl  | 
| import PyGameData  | 
| import IpyGameDataPY  | 
| import PlayerDBGSEvent  | 
| import PlayerTeam  | 
| import GameWorld  | 
| import ChConfig  | 
| import IPY_PlayerDefine  | 
| import CrossBattlefield  | 
| import CrossRealmPlayer  | 
| import CrossChampionship  | 
| import DataRecordPack  | 
| 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  | 
|       | 
|     def OnCopyMapClose(self):  | 
|         self.realMapID = 0  | 
|         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 GetCopyMapPlayerCount(self, includeOffline, tick):  | 
|         ## »ñÈ¡¸Ã·ÖÏßÍæ¼ÒÊý  | 
|         # @param includeOffline: ÊÇ·ñ°üº¬ÀëÏßÍæ¼Ò  | 
|           | 
|         # ÒƳýÇëÇó½øÈ볬ʱµÄÍæ¼Ò  | 
|         for waitPlayerID, playerInfo in self.waitPlayerDict.items():  | 
|             _, requestTick = playerInfo  | 
|             if tick - requestTick > 60000: # ÇëÇó½øÈëʱ¼ä±£Áô1·ÖÖÓ  | 
|                 self.waitPlayerDict.pop(waitPlayerID)  | 
|                   | 
|         # ÅжÏÊÇ·ñ³¬¹ýÈËÊýÉÏÏÞ  | 
|         fbPlayerCount, waitPlayerCount = len(self.fbPlayerDict), len(self.waitPlayerDict)  | 
|         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  | 
|           | 
|     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):  | 
|     mapID = GetRecordMapID(mapID)  | 
|     fbLineIpyData = IpyGameDataPY.GetIpyGameDataNotLog("FBLine", mapID, lineID)  | 
|     if not fbLineIpyData and isDefaultLine:  | 
|         #GameWorld.DebugLog("ûÓÐÖ¸¶¨¹¦ÄÜÏß·µÄÔòĬÈÏÈ¡0£¬ÔÙûÓеϰ¾ÍÊDz»ÐèÒªµÄmapID=%s, lineID=%s" % (mapID, lineID))  | 
|         fbLineIpyData = IpyGameDataPY.GetIpyGameDataNotLog("FBLine", mapID, 0)  | 
|     return fbLineIpyData  | 
|   | 
| ## »ñÈ¡¼Ç¼ֵµÄmapID  | 
| #  @param mapID ËùÒª²éµÄmapID  | 
| #  @return  | 
| #  @remarks Ò»°ãÓÃÓÚ¼¸ÕŵØÍ¼¹«ÓÃÒ»·Ý´æ´¢¼Ç¼£¬Èç×é¶Ó¸±±¾½øÈë´ÎÊý£¬CDʱ¼äµÈÊý¾ÝÐè¹²Ïí  | 
| def GetRecordMapID(mapID):  | 
|     DataMapIDDict = IpyGameDataPY.GetConfigEx("DataMapIDDict")  | 
|     if not DataMapIDDict:  | 
|         dMapIDDict = {}  | 
|         ipyDataMgr = IpyGameDataPY.IPY_Data()  | 
|         for i in xrange(ipyDataMgr.GetFBLineCount()):  | 
|             ipyData = ipyDataMgr.GetFBLineByIndex(i)  | 
|             dMapID = ipyData.GetDataMapID()  | 
|             mID = ipyData.GetMapID()  | 
|             dMapIDList= dMapIDDict.get(dMapID, [])  | 
|             if mID not in dMapIDList:  | 
|                 dMapIDList.append(mID)  | 
|                 dMapIDDict[dMapID] = dMapIDList  | 
|               | 
|         for dMapID in dMapIDDict.keys():  | 
|             if len(dMapIDDict[dMapID]) == 1:  | 
|                 dMapIDDict.pop(dMapID)  | 
|         DataMapIDDict = IpyGameDataPY.SetConfigEx("DataMapIDDict", dMapIDDict)  | 
|         #GameWorld.Log("¼ÓÔØDataMapIDDict=%s" % DataMapIDDict)  | 
|           | 
|     for dataMapID, mapIDList in DataMapIDDict.items():  | 
|         if mapID in mapIDList:  | 
|             return dataMapID  | 
|     return mapID  | 
|   | 
| def ClientServerMsg_EnterFB(serverGroupID, msgData, tick):  | 
|     ## ÊÕµ½×Ó·þÇëÇó½øÈ붯̬·ÖÅäµÄ¿ç·þ¸±±¾  | 
|     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()  | 
|       | 
|     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.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  | 
|       | 
|     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 = 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  | 
|       | 
|     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  | 
|       | 
|     for playerID in playerIDList:  | 
|         curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)  | 
|         if not curPlayer:  | 
|             continue  | 
|         CrossRealmPlayer.SendCrossRealmReg(curPlayer, dataMapID, realMapID, mapID, copyMapID, lineID=funcLineID)  | 
|           | 
|     return  | 
|   | 
| def __GetCrossDynamicLineInfo(playerID, serverGroupID, mapID, funcLineID, zoneID, shuntPlayerMax, copyMapPlayerMax, includeOffline, tick, dynamicShuntType):  | 
|     '''»ñÈ¡¿ç·þ·ÖÇø¶ÔÓ¦¶¯Ì¬·ÖÅäµÄ¸±±¾µØÍ¼ÐéÄâÏß·ÐÅÏ¢, ÓÉÓÚÐèÒªÖ§³Ö¶àµØÍ¼·ÖÁ÷£¬ËùÒÔÖ±½ÓÓÉGameServer¹ÜÀí·ÖÅä  | 
|             Ã¿¸ö¹¦ÄÜÏß·֧³Ö°´ÈËÊý·ÖÁ÷£¬³¬¹ý×î´óÈËÊýºó¿É¿ªÆôÒ»ÌõÏàͬ¹¦ÄÜÏß·µÄÐéÄâÏß·½øÐзÖÁ÷£¬ËùÒÔͬһ·ÖÇøÍ¬Ò»µØÍ¼µÄ¹¦ÄÜÏß·ID¿ÉÄܶÔÓ¦¶àÌõÐéÄâÏß·  | 
|         ·ÖÁ÷·½Ê½£º  | 
|             DynamicShuntType_Fill = 1    # ÌîÂúʽ·ÖÁ÷£¬°´´æÔÚµÄÏß·ÈËÊý¶àµÄÓÅÏÈÌî³ä£¬¶¼Âúºó¿ªÆôÐÂÏß·  | 
|             DynamicShuntType_Equally = 2 # ¾ù̯ʽ·ÖÁ÷£¬°´´æÔÚµÄÏß·ÈËÊýÉÙµÄÓÅÏÈÌî³ä£¬¶¼Âúºó¿ªÆôÐÂÏß·  | 
|         ·ÖÁ÷¹æÔò:  | 
|                     Ê±¼ä½ö¾ö¶¨·ÖÁ÷ÈËÊý£¬²»Ó°Ïì³£¹æ·ÖÅäÂß¼  | 
|         1. ÓÅÏÈ·ÖÅäµ½ÈËÊýСÓÚ·ÖÁ÷ÈËÊýµÄ³¡´Î  | 
|         2. ³¬¹ý·ÖÁ÷ÈËÊýµÄ³¡´ÎÒÀ´ÎÍùÈËÊýÉٵij¡´Î·ÖÅä  | 
|         3. µ±µ±Ç°ÒÑ¿ª·ÅµÄ³¡´Î¶¼´ïµ½ÈËÊý·ÖÁ÷ÈËÊý£¬Ôò¿ªÆôг¡´Î£¬Ã»ÓпÕÏеij¡£¬ÔòÍùδ´ïµ½ÈËÊýÉÏÏ޵ij¡´ÎÒÀ´Î·ÖÅ䣬ֱµ½´ïµ½ËùÓг¡´ÎÉÏÏÞ  | 
|       | 
|             ¹ØÓÚ 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)  | 
|     zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})  | 
|     funcLineObjList = zoneLineDict.get(zoneLineKey, [])  | 
|     isPlayerFullMax = (shuntPlayerMax >= copyMapPlayerMax)  | 
|       | 
|     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:  | 
|             continue  | 
|           | 
|         key = (realMapID, copyMapID)  | 
|         if key not in PyGameData.g_crossDynamicLineCopyMapInfo:  | 
|             GameWorld.ErrLog("ÒѾ·ÖÅäµÄÐéÄâÏß·²»´æÔÚ»º´æ¶ÔÓ¦¹ØÏµÀzoneID=%s,funcLineID=%s,realMapID=%s,copyMapID=%s"   | 
|                              % (zoneID, funcLineID, realMapID, copyMapID))  | 
|             continue  | 
|           | 
|         copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]  | 
|         openState = copyMapObj.openState  | 
|         if openState == IPY_PlayerDefine.fbosWaitForClose:  | 
|             # Ã»ÓÐÏÞÖÆ·ÖÁ÷ÈËÊýµÄÇé¿ö£¬´ú±í¶¼ÔÚͬһ³¡£¬ÕâÖÖÇé¿öϵ±¸±±¾ÒѾÔڹرյÄ״̬Ï£¬Ôò´ú±íÒѾ½áÊøÁË£¬²»¿ÉÔÙ½øÈë  | 
|             if not shuntPlayerMax:  | 
|                 PlayerControl.NotifyCodeCross(serverGroupID, playerID, "CrossFBClose")  | 
|                 return  | 
|             #GameWorld.DebugLog("    ÐéÄâÏß·µÈ´ý¹Ø±ÕÖÐ! realMapID=%s,copyMapID=%s" % (realMapID, copyMapID))  | 
|             continue  | 
|           | 
|         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  | 
|     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:  | 
|         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 = 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  | 
|     newFuncLineObj.newFuncLineNum = newFuncLineNum  | 
|       | 
|     copyMapObj = CrossCopyMapInfo(zoneID, funcLineID)  | 
|     copyMapObj.realMapID = realMapID  | 
|     copyMapObj.copyMapID = copyMapID  | 
|     copyMapObj.newFuncLineNum = newFuncLineNum  | 
|       | 
|     key = (realMapID, copyMapID)  | 
|     PyGameData.g_crossDynamicLineCopyMapInfo[key] = copyMapObj  | 
|       | 
|     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 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  | 
|     zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})  | 
|     for key, funcLineObjList in zoneLineDict.items():  | 
|         for funcLineObj in funcLineObjList:  | 
|             if funcLineObj.realMapID == realMapID and funcLineObj.copyMapID == copyMapID:  | 
|                 zoneID = key[0]  | 
|                 return zoneID  | 
|     return 0  | 
|   | 
| def OnCrossDynamicLineStateChange(msgList):  | 
|     mapID, realMapID, copyMapID, state = msgList[:4]  | 
|       | 
|     if state == IPY_PlayerDefine.fbosWaitForClose:  | 
|         funcLineDataCache = msgList[3]  | 
|         OnCrossDynamicLineWaitForClose(realMapID, copyMapID, funcLineDataCache)  | 
|     elif state == IPY_PlayerDefine.fbosClosed:  | 
|         OnCrossDynamicLineClose(realMapID, copyMapID)  | 
|     elif state == IPY_PlayerDefine.fbosOpen:  | 
|         OnCrossDynamicLineOpen(mapID, realMapID, copyMapID)  | 
|           | 
|     return  | 
|   | 
| def OnCrossDynamicLineOpen(mapID, realMapID, copyMapID):  | 
|     ## ¶¯Ì¬·ÖÅäÏß·µÄµØÍ¼ÐéÄâÏß·Æô¶¯³É¹¦  | 
|       | 
|     key = (realMapID, copyMapID)  | 
|     if key not in PyGameData.g_crossDynamicLineCopyMapInfo:  | 
|         return  | 
|     copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]  | 
|     copyMapObj.openState = IPY_PlayerDefine.fbosOpen  | 
|     funcLineID = copyMapObj.funcLineID  | 
|       | 
|     # Í¨Öª×Ó·þµÈ´ýÖеÄÍæ¼Ò¿ÉÒÔ½øÈ븱±¾  | 
|     serverPlayerIDListDict = {}  | 
|     for playerID, playerInfo in copyMapObj.waitPlayerDict.items():  | 
|         serverGroupID = playerInfo[0]  | 
|         if serverGroupID not in serverPlayerIDListDict:  | 
|             serverPlayerIDListDict[serverGroupID] = []  | 
|         playerIDList = serverPlayerIDListDict[serverGroupID]  | 
|         playerIDList.append(playerID)  | 
|           | 
|     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, recordMapID, mapID, realMapID, copyMapID, funcLineID]  | 
|         CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_EnterFBRet, retInfo, [serverGroupID])  | 
|           | 
|     #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)  | 
|     #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo)  | 
|     return  | 
|   | 
| def OnCrossDynamicLineWaitForClose(realMapID, copyMapID, funcLineDataCache):  | 
|     ## ¶¯Ì¬·ÖÅäÏß·µÄµØÍ¼ÐéÄâÏß·¹Ø±Õ  | 
|           | 
|     mapID = GetRecordMapID(realMapID)  | 
|     GameWorld.Log("¶¯Ì¬·ÖÅäÐéÄâÏß·µÈ´ý¹Ø±Õ mapID=%s,realMapID=%s,copyMapID=%s,funcLineDataCache=%s" % (mapID, realMapID, copyMapID, funcLineDataCache))  | 
|     zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})   | 
|     for key, funcLineObjList in zoneLineDict.items():  | 
|         for funcLineObj in funcLineObjList:  | 
|             if funcLineObj.realMapID == realMapID and funcLineObj.copyMapID == copyMapID:  | 
|                 funcLineObj.funcLineDataCache = funcLineDataCache  | 
|                 zoneID, funcLineID = key  | 
|                 GameWorld.Log("    ·ÖÇø¶ÔÓ¦¹¦ÄÜÏß·ÐéÄâ·ÖÏߵȴý¹Ø±Õ: zoneID=%s,mapID=%s,funcLineID=%s" % (zoneID, mapID, funcLineID))  | 
|                 break  | 
|       | 
|     key = (realMapID, copyMapID)  | 
|     if key in PyGameData.g_crossDynamicLineCopyMapInfo:  | 
|         copyMapObj = PyGameData.g_crossDynamicLineCopyMapInfo[key]  | 
|         copyMapObj.openState = IPY_PlayerDefine.fbosWaitForClose  | 
|           | 
|     #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineInfo=%s" % PyGameData.g_crossDynamicLineInfo)  | 
|     #GameWorld.DebugLog("    PyGameData.g_crossDynamicLineCopyMapInfo=%s" % PyGameData.g_crossDynamicLineCopyMapInfo)  | 
|     return  | 
|   | 
| def OnCrossDynamicLineClose(mapID, copyMapID):  | 
|     ## ¶¯Ì¬·ÖÅäÏß·µÄµØÍ¼ÐéÄâÏß·¹Ø±Õ  | 
|       | 
|     dataMapID = GetRecordMapID(mapID)  | 
|     GameWorld.Log("¶¯Ì¬·ÖÅäÐéÄâÏß·¹Ø±Õ dataMapID=%s,mapID=%s,copyMapID=%s" % (dataMapID, mapID, copyMapID))  | 
|     zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(dataMapID, {})   | 
|     for key, funcLineObjList in zoneLineDict.items():  | 
|         for funcLineObj in funcLineObjList:  | 
|             if funcLineObj.realMapID == mapID and funcLineObj.copyMapID == copyMapID:  | 
|                 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)  | 
|     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):  | 
|     ## ¶¯Ì¬·ÖÅäÏß·µÄµØÍ¼ÖØÖà  | 
|     realMapID, copyMapCount = msgList  | 
|     mapID = GetRecordMapID(realMapID)  | 
|     GameWorld.Log("¶¯Ì¬·ÖÅäÐéÄâÏß·µØÍ¼ÖØÖàmapID=%s,realMapID=%s,copyMapCount=%s" % (mapID, realMapID, copyMapCount))  | 
|     PyGameData.g_crossMapCopyMapCountDict[realMapID] = copyMapCount  | 
|       | 
|     zoneLineDict = PyGameData.g_crossDynamicLineInfo.get(mapID, {})  | 
|     for key, funcLineObjList in zoneLineDict.items():  | 
|         for funcLineObj in funcLineObjList:  | 
|             if funcLineObj.realMapID == realMapID:  | 
|                 funcLineObj.OnCopyMapClose()  | 
|               | 
|     for key in PyGameData.g_crossDynamicLineCopyMapInfo.keys():  | 
|         if key[0] == realMapID:  | 
|             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.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):  | 
|     ## Íæ¼ÒÀ뿪¿ç·þ·þÎñÆ÷  | 
|       | 
|     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)  | 
|       | 
|     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  | 
|   | 
| ##--------------------------------------------------------------------------------------------------  | 
|   | 
| ## ÇëÇó½øÈ븱±¾·ÖÏß  | 
| #  @param curPlayer: ÇëÇóÍæ¼Ò  | 
| #  @param queryCallName: ÇëÇ󻨵÷Ãû  | 
| #  @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:  | 
|         mapInfo = eval(sendCMD)  | 
|     except Exception, e:  | 
|         GameWorld.ErrLog("EnterFBLine() sendCMD=%s error" % sendCMD)  | 
|         return  | 
|       | 
|     if not mapInfo or len(mapInfo) < 2:  | 
|         GameWorld.ErrLog("EnterFBLine() sendCMD=%s error!" % sendCMD)  | 
|         return  | 
|       | 
|     #if mapInfo and len(mapInfo) == 2:  | 
|     tagMapID = mapInfo[0]  | 
|     tagLineID = mapInfo[1]  | 
|       | 
|     fbLineIpyData = GetFBLineIpyData(tagMapID, tagLineID)  | 
|     sceneMapID = tagMapID if not fbLineIpyData else fbLineIpyData.GetMapID()  | 
|     gameMap = GameWorld.GetMap(sceneMapID)  | 
|     if not gameMap:  | 
|         GameWorld.ErrLog("Ä¿±ê¸±±¾µØÍ¼²»´æÔÚ!tagMapID=%s,sceneMapID=%s" % (tagMapID, sceneMapID), curPlayer.GetPlayerID())  | 
|         return  | 
|       | 
|     # ×é¶Ó¸±±¾, ÓжÓÎéµÄÇé¿ö²ÅÑéÖ¤ÆäËû¶ÓÔ±¿É·ñ½øÈ룬·ñÔò´ú±íµ¥È˽øÈë  | 
|     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  | 
|       | 
|     #·âħ̳¸±±¾ÅжÏÀïÃæµÄBOSSÊÇ·ñµ½ÁËË¢ÐÂʱ¼ä  | 
|     if tagMapID in ChConfig.WorldBossFBMapIDList:  | 
|         bossID = mapInfo[2]  | 
|         if not GameWorldBoss.GetBossIsAliveOrCanReborn(bossID):  | 
|             return  | 
|           | 
|     elif tagMapID == ChConfig.Def_FBMapID_FamilyWar:  | 
|         if not GameWorldFamilyWar.CheckPlayerCanEnterFamilyWarFBMap(curPlayer):  | 
|             return  | 
|           | 
|     #ÊØÎÀÈ˻ʠÊÇ·ñÒÑ²Î¼Ó  | 
|     elif tagMapID == ChConfig.Def_FBMapID_FamilyInvade:  | 
|         if curPlayer.GetFamilyID() in PyGameData.g_swrhJoinRecord:  | 
|             PlayerControl.NotifyCode(curPlayer, "TheEmperor1")  | 
|             return  | 
|     #¶àÏÉÃËBOSS ÊÇ·ñÒѽáÊø  | 
|     elif tagMapID == ChConfig.Def_FBMapID_AllFamilyBoss:  | 
|         if not PlayerFamilyBoss.IsInAllFamilyBoss(tagLineID):  | 
|             #»î¶¯Î´¿ªÆô  | 
|             return  | 
|         if PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_AllFamilyBossTime):  | 
|             #BOSSÒѱ»»÷ɱ  | 
|             return  | 
|     #Æï³èBOSS ÊÇ·ñÒѽáÊø  | 
|     elif tagMapID == ChConfig.Def_FBMapID_HorsePetBoss:  | 
|         if not PlayerHorsePetBoss.IsInHorsePetBoss():  | 
|             #»î¶¯Î´¿ªÆô  | 
|             return  | 
|         if PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_HorsePetBossTime % tagLineID):  | 
|             #BOSSÒѱ»»÷ɱ  | 
|             return  | 
|     # MapServer_QueryPlayer(int srcPlayerID, int queryType, int queryID, int mapID, char *callName, char *cmd,WORD cmdLen, int RouteServerIndex)  | 
|     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("        ²»ÊDZ¾·þÍæ¼Ò£¬²»´¦Àí!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  | 
|   |