hxp
2025-12-03 5ac407ea08e218f3638e67b37c9261b437393d34
129 【战斗】战斗系统-服务端(支持多地图战斗)
11个文件已修改
330 ■■■■ 已修改文件
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyCrossServerPack.ini 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChServerToServerPyPack.py 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CrossServerPackLogic.py 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyMongoDB/LogicProcess/UserCtrlDB.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyCrossServerPack.ini
@@ -7,10 +7,13 @@
Writer = alee
Releaser = alee
RegType = 0
RegisterPackCount = 1
RegisterPackCount = 2
PacketCMD_1=0xC2
PacketSubCMD_1=0x01
PacketCallFunc_1=OnTest
PacketCMD_2=0xC2
PacketSubCMD_2=0x02
PacketCallFunc_2=OnSSCommMsg
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/BattleObj.py
@@ -1224,7 +1224,7 @@
    return batObjMgr
def OnMinute():
    GameWorld.Log("战斗单位数量: %s" % len(GetBatObjMgr().batObjDict))
    #GameWorld.Log("战斗单位数量: %s" % len(GetBatObjMgr().batObjDict))
    return
def NotifyObjInfoRefresh(batObj, attrID, value):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Attack/TurnAttack.py
@@ -29,6 +29,7 @@
import GameWorld
import PlayerLLMJ
import PlayerPrestigeSys
import CrossServerPackLogic
import IpyGameDataPY
import PlayerOnline
import NPCCommon
@@ -47,7 +48,6 @@
import random
import time
import json
import shutil
import os
g_gmTestFightReq = []
@@ -1138,21 +1138,31 @@
        # pvp 或 pve 必须要满足其中一种
        return
    
    # 先默认本地图处理,后续优化多战斗地图支持
    reqServerID = GameWorld.GetGameWorld().GetServerID()
    reqInfo = [reqServerID, guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, npcLineupIDList, strongerLV, difficulty, reqData]
    OnMsg_BattleRequest(reqInfo)
    reqInfo = [guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, npcLineupIDList, strongerLV, difficulty, reqData]
    multiMapSet = IpyGameDataPY.GetFuncCfg("TurnFightProcess", 1)
    # 多地图战斗 0-本地图处理;1-多地图处理;2-debug模式默认本地图处理,非debug默认多地图处理
    isMultiMap = False
    if multiMapSet == 1:
        isMultiMap = True
    elif multiMapSet == 2:
        if not GameWorld.GetGameWorld().GetDebugLevel():
            isMultiMap = True
    if isMultiMap:
        CrossServerPackLogic.SendToBattleServer(ShareDefine.SSMsg_BattleRequest, reqInfo, reqPlayerID)
    else:
        SSMsg_BattleRequest(reqInfo, reqServerID)
    return
def OnMsg_BattleRequest(reqInfo):
def SSMsg_BattleRequest(reqInfo, fromServerID):
    ## 请求执行战斗,由本地图或其他服务器地图分配过来的战斗请求
    reqServerID, guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, npcLineupIDList, strongerLV, difficulty, reqData = reqInfo
    guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, npcLineupIDList, strongerLV, difficulty, reqData = reqInfo
    
    if npcLineupIDList:
        turnFight = DoTurnFightPVE(guid, mapID, funcLineID, reqPlayerID, reqServerID, lineupDictA, npcLineupIDList, strongerLV, difficulty)
        turnFight = DoTurnFightPVE(guid, mapID, funcLineID, reqPlayerID, fromServerID, lineupDictA, npcLineupIDList, strongerLV, difficulty)
    else:
        turnFight = DoTurnFightPVP(guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, reqServerID)
        turnFight = DoTurnFightPVP(guid, mapID, funcLineID, lineupDictA, lineupDictB, reqPlayerID, fromServerID)
        
    winFaction = None
    statMsg = {}
@@ -1165,16 +1175,15 @@
    retInfo = [guid, mapID, funcLineID, reqPlayerID, winFaction, statMsg, dateStr, reqData]
    
    # 本地图自己处理的
    if reqServerID == GameWorld.GetGameWorld().GetServerID():
        OnMsg_BattleResult(retInfo)
    if fromServerID == GameWorld.GetGameWorld().GetServerID():
        SSMsg_BattleResult(retInfo, fromServerID)
        
    # 其他服务器地图请求的,发送战斗结果回去
    else:
        pass
        CrossServerPackLogic.SendToServer(ShareDefine.SSMsg_BattleResult, retInfo, [fromServerID], playerID=reqPlayerID)
    return
def OnMsg_BattleResult(retInfo):
def SSMsg_BattleResult(retInfo, fromServerID):
    ## 收到战斗结果信息
    
    guid, mapID, funcLineID, reqPlayerID, winFaction, statMsg, dateStr, reqData = retInfo
@@ -1993,7 +2002,7 @@
    funcLineID = turnFight.funcLineID
    GameWorld.DebugLog("--- 战斗结束处理 ---, winFaction=%s, costTime=%ss, %s" % (winFaction, turnFight.costTime, guid))
    if mapID != ChConfig.Def_FBMapID_Main:
        GameWorld.Log("战斗耗时: %ss, mapID=%s,funcLineID=%s" % (turnFight.costTime, mapID, funcLineID))
        GameWorld.Log("战斗耗时: %ss, mapID=%s,funcLineID=%s,turnNum=%s/%s" % (turnFight.costTime, mapID, funcLineID, turnFight.turnNum, turnFight.turnMax))
        
    # 统计明细
    batObjMgr = BattleObj.GetBatObjMgr()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChServerToServerPyPack.py
@@ -101,6 +101,98 @@
        return  DumpString
#------------------------------------------------------
# C2 02 跨服通用信息包 #tagSSCommMsg
class  tagSSCommMsg(Structure):
    Head = tagHead()
    FromServerID = 0    #(DWORD FromServerID)//哪个服发的
    ServerTime = 0    #(DWORD ServerTime)//来源服务器时间戳
    TypeLen = 0    #(BYTE TypeLen)
    MsgType = ""    #(String MsgType)
    Len = 0    #(DWORD Len)
    Data = ""    #(String Data)
    data = None
    def __init__(self):
        self.Clear()
        self.Head.Cmd = 0xC2
        self.Head.SubCmd = 0x02
        return
    def ReadData(self, _lpData, _pos=0, _Len=0):
        self.Clear()
        _pos = self.Head.ReadData(_lpData, _pos)
        self.FromServerID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.ServerTime,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.TypeLen,_pos = CommFunc.ReadBYTE(_lpData, _pos)
        self.MsgType,_pos = CommFunc.ReadString(_lpData, _pos,self.TypeLen)
        self.Len,_pos = CommFunc.ReadDWORD(_lpData, _pos)
        self.Data,_pos = CommFunc.ReadString(_lpData, _pos,self.Len)
        return _pos
    def Clear(self):
        self.Head = tagHead()
        self.Head.Clear()
        self.Head.Cmd = 0xC2
        self.Head.SubCmd = 0x02
        self.FromServerID = 0
        self.ServerTime = 0
        self.TypeLen = 0
        self.MsgType = ""
        self.Len = 0
        self.Data = ""
        return
    def GetLength(self):
        length = 0
        length += self.Head.GetLength()
        length += 4
        length += 4
        length += 1
        length += len(self.MsgType)
        length += 4
        length += len(self.Data)
        return length
    def GetBuffer(self):
        data = ''
        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
        data = CommFunc.WriteDWORD(data, self.FromServerID)
        data = CommFunc.WriteDWORD(data, self.ServerTime)
        data = CommFunc.WriteBYTE(data, self.TypeLen)
        data = CommFunc.WriteString(data, self.TypeLen, self.MsgType)
        data = CommFunc.WriteDWORD(data, self.Len)
        data = CommFunc.WriteString(data, self.Len, self.Data)
        return data
    def OutputString(self):
        DumpString = '''
                                Head:%s,
                                FromServerID:%d,
                                ServerTime:%d,
                                TypeLen:%d,
                                MsgType:%s,
                                Len:%d,
                                Data:%s
                                '''\
                                %(
                                self.Head.OutputString(),
                                self.FromServerID,
                                self.ServerTime,
                                self.TypeLen,
                                self.MsgType,
                                self.Len,
                                self.Data
                                )
        return DumpString
m_NAtagSSCommMsg=tagSSCommMsg()
ChNetPackDict[eval("0x%02x%02x"%(m_NAtagSSCommMsg.Head.Cmd,m_NAtagSSCommMsg.Head.SubCmd))] = m_NAtagSSCommMsg
#------------------------------------------------------
# C2 01 跨服服务器间的测试包 #tagSSTest
class  tagSSTest(Structure):
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/CrossServerPackLogic.py
@@ -7,16 +7,163 @@
# 跨服服务器间的封包 既是收包也是发包
import GameWorld
import ChServerToServerPyPack
import ShareDefine
import NetPackCommon
import ChServerToServerPyPack
import TurnAttack
import PyGameData
import ChPlayer
import traceback
import cPickle
import time
def OnTest(netPack):
    GameWorld.Log("收到跨服包 " + str(netPack.Data))
def SendTest():
def SendTest(dirType, serverList):
    pack = ChServerToServerPyPack.tagSSTest()
    pack.Data = 12
    #0全广播,1通知主服务器排除合服子服,2通知服务器包含合服子服, 3通知跨服服务器 
    NetPackCommon.SendCrossServerToServerPack(1, "[501]", pack.GetBuffer())
    NetPackCommon.SendCrossServerToServerPack(dirType, serverList, pack.GetBuffer())
    return
def GetCrossServerID():
    ## 获取本服务器所属的跨服中心服务器
    return 0
def SendToCrossServer(msgType, dataMsg):
    ## 发送信息到跨服服务器
    if GameWorld.IsCrossServer():
        return
    if not dataMsg:
        return
    if msgType not in [ShareDefine.ClientServerMsg_ServerInitOK]:
        isOpen = GameWorld.GetGameWorld().GetDictByKey(ShareDefine.Def_Notify_WorldKey_CrossServerOpen)
        if not isOpen:
            GameWorld.Log("跨服服务器未开启或维护中不发送消息! SendMsgToCrossServer => %s" % msgType)
            return
    crossServerID = GetCrossServerID()
    if not crossServerID:
        return
    playerID = 0
    if isinstance(dataMsg, dict):
        playerID = dataMsg.get("playerID", 0)
        if not playerID:
            playerID = dataMsg.get("PlayerID", 0)
    GameWorld.Log("SendMsgToCrossServer => %s, %s, %s" % (msgType, crossServerID, dataMsg), playerID)
    SendToServer(msgType, dataMsg, [crossServerID], ShareDefine.dirType_Cross, playerID, isLog=False)
    return
def SendToClientServer(msgType, dataMsg, serverIDList=None):
    ''' 发送信息到子服务器
        @param serverGroupIDList: 发送指定的服务器组ID列表,内部已经针对列表中组ID去重,
        所以外部逻辑可直接添加,不用考虑组ID重复问题,没有指定服务器组ID时,默认广播所有子服
    '''
    if not GameWorld.IsCrossServer():
        return
    if not PyGameData.g_serverInitOK:
        GameWorld.ErrLog("跨服服务器未启动好,不允许向子服发送数据! %s, %s, %s" % (msgType, serverIDList, dataMsg))
        return
    playerID = 0
    if isinstance(dataMsg, dict):
        playerID = dataMsg.get("playerID", 0)
        if not playerID:
            playerID = dataMsg.get("PlayerID", 0)
    GameWorld.Log("SendToClientServer => %s, %s, %s" % (msgType, serverIDList, dataMsg), playerID)
    SendToServer(msgType, dataMsg, serverIDList, ShareDefine.dirType_Main, playerID, isLog=False) # 默认发给主服即可
    return
def SendToBattleServer(msgType, dataMsg, playerID=0):
    SendToServer(msgType, dataMsg, dirType=ShareDefine.dirType_Battle, playerID=playerID)
    return
def SendToServer(msgType, dataMsg, serverIDList=None, dirType=ShareDefine.dirType_Main, playerID=0, isLog=True):
    '''发送给其他服务器
    @param msgType: 功能信息类型字符定义
    @param dataMsg: 发送的数据,任意格式,由功能自行决定
    @param serverIDList: 指定目标服务器ID 或   服务器ID列表
    '''
    if isinstance(serverIDList, int):
        serverIDList = [serverIDList]
    elif not isinstance(serverIDList, list):
        serverIDList = []
    else:
        serverIDList = list(set(serverIDList)) # 去重
    if isLog:
        GameWorld.Log("SendToServer => %s, %s, %s" % (msgType, serverIDList, dataMsg), playerID)
    # 协议要用最高级2,可减少长度
    sendMsg = cPickle.dumps(dataMsg, 2)
    pack = ChServerToServerPyPack.tagSSCommMsg()
    pack.FromServerID = GameWorld.GetGameWorld().GetServerID()
    pack.ServerTime = int(time.time())
    pack.MsgType = msgType
    pack.TypeLen = len(pack.MsgType)
    pack.Data = sendMsg
    pack.Len = len(pack.Data)
    NetPackCommon.SendCrossServerToServerPack(dirType, serverIDList, pack.GetBuffer())
    return
def OnSSCommMsg(netPack):
    ## 收到其他服务器发来的消息
    fromServerID = netPack.FromServerID
    fromServerTime = netPack.ServerTime
    msgType = netPack.MsgType
    recvMsg = netPack.Data
    if not PyGameData.g_serverInitOK:
        GameWorld.Log("服务器未启动好,不处理其他服务器信息! %s, fromServerID=%s" % (msgType, fromServerID))
        return
    try:
        dataMsg = cPickle.loads(recvMsg)
        if GameWorld.IsCrossServer():
            GameWorld.Log("OnCrossServerReceiveMsg: %s, fromServerID=%s, %s" % (msgType, fromServerID, dataMsg))
        else:
            GameWorld.Log("OnClientServerReceiveMsg: %s, fromServerID=%s, %s" % (msgType, fromServerID, dataMsg))
            crossServerID = GetCrossServerID()
            if crossServerID == fromServerID:
                __fixCrossServerTime(msgType, fromServerTime)
        if msgType == ShareDefine.SSMsg_BattleRequest:
            TurnAttack.SSMsg_BattleRequest(dataMsg, fromServerID)
        elif msgType == ShareDefine.SSMsg_BattleResult:
            TurnAttack.SSMsg_BattleResult(dataMsg, fromServerID)
    except:
        GameWorld.RaiseException("服务器接收信息处理报错 \r\n%s" % str(traceback.format_exc()))
    return
def __fixCrossServerTime(msgType, crossServerTime):
    # 子服矫正跨服服务器时间
    curServerTime = int(time.time())
    curServerCrossServerTime = GameWorld.ChangeTimeStrToNum(GameWorld.GetCrossServerTimeStr())
    diffSeconds = curServerCrossServerTime - crossServerTime # 本服计算误差
    PyGameData.g_crossServerTimeInfo = [crossServerTime, curServerTime] # 覆盖更新
    # 误差超过30秒 或强制同步时间的
    if abs(diffSeconds) >= 30 or msgType == ShareDefine.CrossServerMsg_CrossServerTime:
        GameWorld.DebugLog("同步跨服服务器时间,本服与跨服服务器时间计算误差! diffSeconds=%s" % (diffSeconds))
        playerManager = GameWorld.GetPlayerManager()
        for i in xrange(playerManager.GetPlayerCount()):
            curPlayer = playerManager.GetPlayerByIndex(i)
            if not GameWorld.IsNormalPlayer(curPlayer):
                continue
            ChPlayer.Sync_PyServerDataTimeToClient(curPlayer)
    return
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py
@@ -124,7 +124,7 @@
    curTime = int(time.time())
    if not saveToDB:
        if curTime > PyGameData.g_lastRTBTime and curTime - PyGameData.g_lastRTBTime < BackupInterval * 60:
            GameWorld.DebugLog("备档冷却中! 上次备档时间=%s" % (GameWorld.ChangeTimeNumToStr(PyGameData.g_lastRTBTime)))
            #GameWorld.DebugLog("备档冷却中! 上次备档时间=%s" % (GameWorld.ChangeTimeNumToStr(PyGameData.g_lastRTBTime)))
            return
    
    serverID = GameWorld.GetGameWorld().GetServerID()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -1948,7 +1948,7 @@
    ## 跨服服务器时间
    if IsCrossServer():
        return GetCurrentDataTimeStr()
    lastCrossServerTime, lastServerTime, _ = PyGameData.g_crossServerTimeInfo
    lastCrossServerTime, lastServerTime = PyGameData.g_crossServerTimeInfo
    if not lastCrossServerTime:
        return GetCurrentDataTimeStr()
    curTime = int(time.time())
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -995,16 +995,6 @@
            PyGameData.g_crossZoneName = msgValue
            return
        
        if key == ShareDefine.Def_Notify_WorldKey_CrossServerTime:
            PyGameData.g_crossServerTimeInfo = eval(msgValue)
            playerManager = GameWorld.GetPlayerManager()
            for index in xrange(playerManager.GetPlayerCount()):
                curPlayer = playerManager.GetPlayerByIndex(index)
                if not GameWorld.IsNormalPlayer(curPlayer):
                    continue
                ChPlayer.Sync_PyServerDataTimeToClient(curPlayer)
            return
        # 幸运云购
        if key == ShareDefine.Def_Notify_WorldKey_LuckyCloudBuyInfo:
            PyGameData.g_luckyCloudBuyInfo = eval(msgValue)
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyGameData.py
@@ -106,7 +106,7 @@
g_luckyCloudBuyInfo = {} # 幸运云购最新一期信息
g_crossServerTimeInfo = [0, 0, 0] # 跨服时间信息 [crossServerTime, curServerTime, syncMapTime]
g_crossServerTimeInfo = [0, 0] # 跨服时间信息 [crossServerTime, curServerTime]
g_crossZoneName = "" # 跨服分区名
g_crossRegPlayerAttrDict = {} #跨服注册时登记的会影响战力的属性值 {playerID:[value, ...], ...}
g_crossSyncTickDict = {} #需要同步跨服数据的玩家同步tick字典 {playerID:tick, ...}
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/PyMongoDB/LogicProcess/UserCtrlDB.py
@@ -938,9 +938,9 @@
    
    def OnCrossServerToServerPack(self, db, pack):
        if CommonDefine.IsDebug():
            import binascii
            mylog.debug('buf = %s'%binascii.b2a_hex(pack.getBuffer()))
        #if CommonDefine.IsDebug():
        #    import binascii
        #    mylog.debug('buf = %s'%binascii.b2a_hex(pack.getBuffer()))
        
        try:
            recvPack = MergeServerRecvProtocol.tagLPStringData()
ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -1037,6 +1037,23 @@
CampType_Evil,       #邪恶(与 Def_ID2Win 一致)
] = range(3)
# json里的配表 服务器类型(0主服,1子服,2跨服,3战斗服)]
serverType_Main = 0 # 0主服
serverType_Child = 1 # 1子服
serverType_Cross = 2 # 2跨服
serverType_Battle = 3 # 3战斗服
# dirType //0全广播,1通知主服务器排除合服子服,2通知服务器包含合服子服, 3通知跨服服务器 ,4通知战斗服务器
dirType_All = 0 # 0全广播
dirType_Main = 1 # 1通知主服务器排除合服子服
dirType_Child = 2 # 2通知服务器包含合服子服
dirType_Cross = 3 # 3通知跨服服务器
dirType_Battle = 4 # 4通知战斗服务器
# 服务器间的信息定义
SSMsg_BattleRequest = "SS_BattleRequest"
SSMsg_BattleResult = "SS_BattleResult"
# 跨服服务器发送子服信息定义
CrossServerMsg_CrossServerState = "CrossServerState"    # 跨服服务器状态变更
CrossServerMsg_PlayerLoginout = "PlayerLoginout"        # 玩家上下线状态同步