local BattleSimulator = { FastPlayFrame = 0, } local world_boss_pb = require('Proto/world_boss_pb') local LevelRecordManager = require("Battle/Logic/Level/LevelRecordManager") local Infrast = require("Infrast/Infrast") local Command = require "Battle/Logic/MainLoop/Command" local ClientProfilerSaveAndRideCmdManager = require("Battle/Logic/NetWork/ClientProfilerSaveAndRideCmdManager") local private = { SaveFrameData = false,-- 是否保存数据 WriteFunc = {} , PlayerID = -1, -- 视角玩家 isServerData = false, -- 播放从服务器拿过来的数据 playerCompareInfo = {} ,-- 存储获取服务器数据后保存的玩家 对比数据 checkStr acid playerID onlinePlayers = nil , -- 记录在线的玩家 clientReplayType = nil, -- 客户端当前回放的类型(ReplayTypeEnum) } --回放的枚举 local ReplayTypeEnum = { Client_AllServerData = 1, -- 客户端完全使用服务器的数据回放(同步玩法) Client_FileFrameData = 2, -- 客户端 帧数据使用Lua本地文件 数据回放(同步玩法) Client_Async_FileData = 3, --客户端 使用本地文件数据 播放 非同步玩法(boss挑战)。 Client_SplitServerData = 4, -- 客户端完全使用服务器的数据回放(同步玩法),但是要分帧读取 } local ServerInfo = "ReplayData/ServerDatas" -- 回放服务器数据的文件 ; 同步玩法 和 非同步玩法都用这个文件 ,只是解析出来的数据结构不同 --------------- ↓↓ 在客户端自己保存战斗数据 ,用此数据回放一次战斗 ↓↓-------------- local role_enterScene_pb = require "Proto/role_enter_scene_pb" local battle_common_pb = require 'Proto/battle_common_pb' local reconnect_pb = require "Proto/battle_reconnect_pb" local FrameBytesFilePath = "BattleCheck/Frames.bytes" local BattleBeginPbFilePath = "BattleCheck/G2CBattleFrameDatas.pb" local InitFubenPbFilePath = "BattleCheck/G2CInitFubenInfo.pb" function BattleSimulator.Client_SaveReplayData(val) private.SaveFrameData = val KCPTransport.SetSaveFrameData(private.SaveFrameData) end function BattleSimulator.StoreMessage(id ,bytes) local func = private.WriteFunc[id] if private.SaveFrameData and func then func(bytes) end end private.WriteFunc[MessageEnums.MessageG2CInitFubenInfo] = function (bytes) LuaExt.FileWriteBytes(InitFubenPbFilePath, false, bytes ) end private.WriteFunc[MessageEnums.MessageG2CBattleFrameDatas] = function (bytes) LuaExt.FileWriteBytes(BattleBeginPbFilePath,false, bytes) end function BattleSimulator.GetG2CInitFubenInfoMsg() local buf = LuaExt.FileReadBytes(InitFubenPbFilePath) local msg = role_enterScene_pb.G2CInitFubenInfo() msg:ParseFromString(buf) print("[gws Test] playID = " , msg.playerID) MultiplayManager.SetKCPConnectInfo(nil,nil,msg.battleRoomId,msg.playerID,msg.battleRoomId) -- gRoomId没存,故用battleRoomId代替 private.OnG2CInitFubenInfo(msg) end function BattleSimulator.GetBattleBeginMsg() local buf = LuaExt.FileReadBytes(BattleBeginPbFilePath) local msg = reconnect_pb.G2CBattleFrameDatas() msg:ParseFromString(buf) private.OnG2CBattleBegin(msg) end function BattleSimulator.GetAllFrameDataMsg() KCPTransport.BindFrameCallBack() BattleExt.GetFrameDataFromFile(FrameBytesFilePath) LogicUpdateManager.GetFrameDataFromKCPCache() end -----处理关卡 3v3------- function private.OnG2CInitFubenInfo(msg) if private.PlayerID == -1 then LevelPlayerManager.MyPlayerIDInRoom = msg.playerID else LevelPlayerManager.MyPlayerIDInRoom = private.PlayerID end local mapid = msg.mapCfgId LevelManager.SetServerLevel(msg.serverLevel) LevelManager.SetHasNextLevel(msg.hasNext) LevelManager.SetMatchStage(msg.newBattleInfo.matchStage) LevelManager.SetServerGs(msg.newBattleInfo.serverFightScore) LevelManager.SetServerAttrInfos(msg.newBattleInfo.attrInfos) LevelManager.SetServerClientIntermediateParamsBytes(msg.clientIntermediateParams) --clientIntermediateParams是上一个副本带给下一个副本的参数,由客户端发给服务器再转发给客户端 LevelManager.SetSwitchInGMS(msg.battleInfo.switchInGMS) LevelManager.SetOpenServerDays(msg.battleInfo.serverOpenDays) MultiplayManager.SetVerifyRoomID(msg.BRoomID) local isFair = false local levelInfoData = DataSheetManager.LevelInfoData:GetDataByLevelID(mapid) if levelInfoData.LevelType == LuaConst.LevelType.SyncPlanesEscort or levelInfoData.LevelType == LuaConst.LevelType.SyncCountryPVP then LevelManager.SetBattleAnchorPos(msg.cartPos) end if levelInfoData.LevelType == LuaConst.LevelType.SyncTeamPVP or levelInfoData.LevelType == LuaConst.LevelType.SyncSolo or levelInfoData.LevelType == LuaConst.LevelType.SyncSoloPractice or levelInfoData.LevelType == LuaConst.LevelType.SyncTeamPVPPractice or levelInfoData.LevelType == LuaConst.LevelType.SyncFairSoloTournament or levelInfoData.LevelType == LuaConst.LevelType.SyncTeamFairChampionship or levelInfoData.LevelType == LuaConst.LevelType.SyncFourCountryChaos then isFair = true end LevelPlayerManager.SetSyncAllPlayersArmyTable(msg.roleList,isFair) MultiplayManager.IsFromStartEnter = true LogicUpdateManager.SetServerReplenishFrame(-1) BattleExt.KCPFilterTankInit() LevelManager.SwitchLevel(mapid,msg.randomSeed,false, false) end function private.OnG2CBattleBegin(msg) local length, startFrame, endFrame = BattleExt.ParseBeginBattleData(msg.BattleData) LogicUpdateManager.ResetData() LevelManager.EnterSyncLevel() MultiReconnectManager.SetInBattleReconnect(true) --因为在MultiplayManager里,隐含了LevelManager.IsSyncLevel(),所以下面不用判断LevelManager.IsSyncLevel() if not LevelManager.IsFreeToEnterAndLeaveLevel() then MultiplayManager.SetAllRolesActive() -- 关闭loading界面等 ExpLevelManager.EnterLevel(LevelManager.GetCurInfoData()) end end -- 客户端自己录制的3v3,点击了3v3回放按钮 function BattleSimulator.StartClientData() LogicUpdateManager.IsReplayOrServerCheck = true private.isServerData = false BattleSimulator.GetG2CInitFubenInfoMsg() end ---------------↑↑ 在客户端自己保存战斗数据 ,用此数据回放一次战斗 ↑↑-------------- ------ 通用运行战斗的方法 ↓ --------- function BattleSimulator.ReadyOver() if private.isServerData then if private.clientReplayType == ReplayTypeEnum.Client_FileFrameData then private.ReadyDone_ClientFrameFile() else private.ReadyDone_Server() end else BattleSimulator.GetBattleBeginMsg() BattleSimulator.GetAllFrameDataMsg() end local serverFrame = KCPTransport.GetCurserverFrame() print("GWS : pvp - serverFrame = " , serverFrame) LogicUpdateManager.SetServerReplenishFrame(BattleSimulator.FastPlayFrame) --设置快速补帧的值 end function BattleSimulator.EndPlay(noReplayMessageBox) LogicUpdateManager.IsReplayOrServerCheck = false private.isServerData = false KCPTransport.UnBindFrameCallBack() if not USE_LUA_STANDALONE then if LevelManager.IsReplayBattle() then CTL.Replay.EndPlay(noReplayMessageBox) else LevelManager.BeforeRoleEnterSceneResp() LevelPlayerManager.SetLaboratoryHeroTable() LevelManager.SwitchLevel(201) end end end function BattleSimulator.SetPlayerID(playerID) private.PlayerID = playerID end function BattleSimulator.SetCheckResult(checkStr ,battleResultMsgStr) logging.log(LTAG.LOG , "replay server check str = " ,checkStr) BattleSimulator.CheckBattleResultStr = checkStr BattleSimulator.BattleResultMsgBytes = battleResultMsgStr end ------ 通用运行战斗的方法 ↑ --------- -------------------↓ 服务器校验 | unity回放服务器数据 ↓------------------ -- 服务器记录的数据,进行播放 。(客户端点击了回放按钮 、服务器校验入口) function BattleSimulator.StartServerData(dataBytes, needSplitFrame) --needSplitFrame 是否需要分帧读取cmd LogicUpdateManager.IsReplayOrServerCheck = true private.isServerData = true private.clientReplayType = needSplitFrame and ReplayTypeEnum.Client_SplitServerData or ReplayTypeEnum.Client_AllServerData -- CompareSyncLogManager.SetNeedSendToServerLog(true) --打开记录log功能 if dataBytes == nil then -- 外面没有传数据 ,那么就从本地找数据 dataBytes = LuaExt.FileReadBytes(ServerInfo) end local msg = battle_common_pb.ForCheckBattleAllInfo() msg:ParseFromString(dataBytes) logging.log(LTAG.LOG, "服务器校验产出的字符串serverCheckStr = " ,msg.serverCheckStr) private.GetServerInfo(msg) end --这个方法跟逻辑数据相关的修改 要跟 MultiplayManager 里的 MultiplayManager.HandleInitFubenInfo 保持一致! function private.GetServerInfo(msg) LevelManager.BeforeHandleInitFubenInfo() --[[如需自取:msg.ServerId msg.BRoomID msg.mapInfo.SceneType msg.mapInfo.GotoType --]] local mapid = msg.mapInfo.MapCfgId local playersInfo = msg.playerInfo private.ServerFrameDatas = msg.battleInfo if private.PlayerID == -1 then LevelPlayerManager.MyPlayerIDInRoom = 1 else LevelPlayerManager.MyPlayerIDInRoom = private.PlayerID end MultiplayManager.SetKCPConnectInfo(nil,nil,msg.BRoomID,LevelPlayerManager.MyPlayerIDInRoom,msg.GRoomID) LevelManager.SetServerLevel(msg.serverLevel) LevelManager.SetHasNextLevel(msg.hasNext) LevelManager.SetMatchStage(msg.newBattleInfo.matchStage) LevelManager.SetServerGs(msg.newBattleInfo.serverFightScore) LevelManager.SetServerAttrInfos(msg.newBattleInfo.attrInfos) LevelManager.SetServerClientIntermediateParamsBytes(msg.clientIntermediateParams) --clientIntermediateParams是上一个副本带给下一个副本的参数,由客户端发给服务器再转发给客户端 LevelManager.SetBattleStatueInfoList(msg.newBattleInfo.roleList) LevelManager.SetPreBattleResult(msg.newBattleInfo.preBattleResults) LevelManager.SetMonsterHpInfos(msg.newBattleInfo.monsterHpInfos) LevelManager.SetBattleTime(msg.newBattleInfo.battleTime) LevelManager.SetBattleEndTime(msg.newBattleInfo.battleEndTime) LevelManager.SetNewBattleInfoIntParam(msg.newBattleInfo.paramInt) LevelManager.SetSwitchInGMS(msg.newBattleInfo.switchInGMS) LevelManager.SetOpenServerDays(msg.newBattleInfo.serverOpenDays) MultiplayManager.SetVerifyRoomID(msg.BRoomID) table.clearArray(private.playerCompareInfo) local roleList = {} for _,info in ipairs(playersInfo) do table.insert(roleList,info.roleInfo) logging.log(LTAG.LOG, "[GWS]server : BattleCheckInfo - PlayerID :",info.roleInfo.playerID ,",CheckStr = ",info.result ,"fighterType =" , info.roleInfo.fighterType,",guild =",info.roleInfo.GuildName) if info.roleInfo.fighterType ~= LuaConst.BattleMemberType.Robot_Hero and (info.result ~= nil and info.result ~= "") then table.insert(private.playerCompareInfo, { playerID = info.roleInfo.playerID, acid = info.roleInfo.acid , checkStr = info.result}) end end local isFair = false local levelInfoData = DataSheetManager.LevelInfoData:GetDataByLevelID(mapid) if levelInfoData == nil then-- 协议解析失败 或者 mapid错误 logging.error(LTAG.ERROR,"map cant find : mapid = " ,mapid , " ,random =" ,msg.randomSeed) end if levelInfoData.LevelType == LuaConst.LevelType.SyncPlanesEscort or levelInfoData.LevelType == LuaConst.LevelType.SyncCountryPVP then LevelManager.SetBattleAnchorPos(msg.cartPos) end if levelInfoData.LevelType == LuaConst.LevelType.SyncTeamPVP or levelInfoData.LevelType == LuaConst.LevelType.SyncSolo or levelInfoData.LevelType == LuaConst.LevelType.SyncSoloPractice or levelInfoData.LevelType == LuaConst.LevelType.SyncTeamPVPPractice or levelInfoData.LevelType == LuaConst.LevelType.SyncFairSoloTournament or levelInfoData.LevelType == LuaConst.LevelType.SyncTeamFairChampionship or levelInfoData.LevelType == LuaConst.LevelType.SyncFourCountryChaos then isFair = true end LevelPlayerManager.SetSyncAllPlayersArmyTable(roleList,isFair) MultiplayManager.IsFromStartEnter = true LogicUpdateManager.SetServerReplenishFrame(-1) BattleExt.KCPFilterTankInit() LevelManager.SwitchLevel(mapid,msg.randomSeed,false, false) end function private.ReadyDone_Server() if private.clientReplayType == ReplayTypeEnum.Client_SplitServerData then-- 分帧回放时,进入战斗不要读取所有的帧数据 private.ServerFrameDatas = nil else BattleExt.ParseBattleData(private.ServerFrameDatas) end LogicUpdateManager.ResetData() LevelManager.EnterSyncLevel() MultiReconnectManager.SetInBattleReconnect(true) --因为在MultiplayManager里,隐含了LevelManager.IsSyncLevel(),所以下面不用判断LevelManager.IsSyncLevel() if LevelManager.IsSyncTeamPVP() or LevelManager.IsSyncSolo() then MultiplayManager.SetAllRolesActive() -- 关闭loading界面等 ExpLevelManager.EnterLevel(LevelManager.GetCurInfoData()) end KCPTransport.BindFrameCallBack() if private.clientReplayType ~= ReplayTypeEnum.Client_SplitServerData then-- 分帧回放时,进入战斗不要读取所有的帧数据 LogicUpdateManager.GetFrameDataFromKCPCache() end end -- 如果需要分支读取数据,则将数据放入C#缓存中 function BattleSimulator.TryReadSplitFrame() if private.clientReplayType ~= ReplayTypeEnum.Client_SplitServerData then return end ClientProfilerSaveAndRideCmdManager.ReadOneFrameData() end -------------------↑ 服务器校验 | unity回放服务器数据 ↑------------------ --------------------↓ 服务器同步校验使用 ↓ ------------------ function BattleSimulator.InsertServerFrameData(dataBytes) local curLevelData = LevelManager.GetCurInfoData() if curLevelData == nil then logging.error(LTAG.ERROR, "[gws] : InsertServerFrameData fail ,level not create!") end if not LogicUpdateManager.FightBegin then -- 如果不在战斗中了 ,那么过来的帧要舍弃 return end BattleExt.ParseCmdB2CFrames(dataBytes) --KCPTransport.BindFrameCallBack() -- 在 ReadyDone_Server 已经绑定了 ,这就不用再绑定了 LogicUpdateManager.GetFrameDataFromKCPCache(true) local serverFrame = KCPTransport.GetCurserverFrame() while(LogicUpdateManager._clientHandleFrame < serverFrame) do -- 这个 < 逻辑要和logicupdateManager中一致 if not LogicUpdateManager.FightBegin then -- 服务器发送的帧数据是多余结算时的帧的 ,所以结算后就要停止。 return true end LogicUpdateManager.OnLogicUpdate(0.05) if MultiplayManager.ForceWaitting then logging.error(LTAG.ERROR, "HYW : InsertServerFrameData when ForceWaitting ERROR! ") return true end end return not BattleSimulator.LoseFrame end -- 添加一个新的玩家 function BattleSimulator.NewFighterEnter(bytes) local msg = world_boss_pb.G2CNewEnterFighter() msg:ParseFromString(bytes) MultiplayManager.OnNewEnterFighter(msg) return true end --------------------↑ 服务器同步校验使用 ↑ ------------------ ----------- ↓ 服务器校验 使用的 接口 ↓ -------- function BattleSimulator.GetKillBossPlayerID() return LevelManager.CurLevelFunc.killBossPlayerID end function BattleSimulator.GetPlayerToBossDmgRecord() --数据结构必须 为 {pointer,playerID,damage,frame,playerID,damage,frame,...} ,第1个元素是最后的指针位置,从第2个元素开始是playerID、伤害、发生的帧数间隔重复 return LevelManager.CurLevelFunc:GetPlayerToBossDamageRecord() end --实际玩家的数量 function BattleSimulator.GetRealPlayerCount() local count = 0 for k,v in pairs(LevelPlayerManager.PlayersArmyTable) do if v.playerInfo.fighterType == LuaConst.BattleMemberType.Account_Hero or v.playerInfo.fighterType == LuaConst.BattleMemberType.Called_Player then count = count + 1 end end return count end function BattleSimulator.RecordOnlinePlayer(playerIDs) private.onlinePlayers = playerIDs end --找到不一致的玩家信息 function BattleSimulator.GetComparedResult() local result = {} for i,info in ipairs(private.playerCompareInfo) do local needCompare = false for _,v in ipairs(private.onlinePlayers) do if info.playerID == v then needCompare = true break; end end if needCompare and info.checkStr ~= BattleSimulator.CheckBattleResultStr then table.insert(result ,{info.playerID , info.acid} ) end end return result end -- 军团boss :所有玩家总伤害 ; 大乱斗:所有玩家对boss的伤害统计 function BattleSimulator.GetCurLevelFuncBossDamage() local result = LevelManager.CurLevelFunc:GetBossDamageRecord() return result end -- 军团战 :获取城墙的受伤量 function BattleSimulator.GetGuildWarWallDamage() local result = {0,0} local recordInfo = LevelManager.CurLevelFunc:GetWallDamageRecord() if recordInfo then if recordInfo[1] then result[1] = math.floor(FP.tonumberV1(recordInfo[1])) end if recordInfo[2] then result[2] = math.floor(FP.tonumberV1(recordInfo[2])) end end return result end --军团战 : 获取资源战场的资源数 function BattleSimulator.GetGuildWarResourceAmount() local result = {0,0} local recordInfo = LevelManager.CurLevelFunc:GetResourceAmount() if recordInfo then if recordInfo[1] then result[1] = recordInfo[1] end if recordInfo[2] then result[2] = recordInfo[2] end end return result end --军团战 : 获取战场的地图信息 function BattleSimulator.GetGuildWarMapInfo() local info = LevelManager.CurLevelFunc:GetMapInfo() return info end --军团战 : 获取后来附加的战场地图信息 function BattleSimulator.GetGuildWarMapAttackInfo() return LevelManager.CurLevelFunc:GetMapAttackInfo() end --军团战 : 获取战斗中的广播信息 function BattleSimulator.GetGuildWarBroadcastTips(hasFirstBlood) local hasFirstDead,info = LevelManager.CurLevelFunc:GetBroadcastTips(hasFirstBlood) return hasFirstDead,info end --军团战 : 获取战斗结果数据 积分等 function BattleSimulator.GetGuildWarPlayerInfos() local info = LevelManager.CurLevelFunc:GetBattlePlayerInfos() return info end --获取没有处理过的插帧请求(插帧请求:校验程序跑出来的插帧请求,需要服务器执行请求进行指定的插帧。比如玩家杀死boss时要给4个战场的同阵营玩家添加鼓舞buff,这个就必须得由服务器插帧实现。因为服务器没有做触发器,所以由校验程序借助Buff + BattleEffect来实现事件触发) --返回一个table,包含从start开始到最后的所有Request的数组,以及最后一个元素的index --index从1开始 function BattleSimulator.GetServerBattleActionRequestListByStart(start) start = start or 1 local originList = LevelManager.CurLevelFunc:GetServerBattleActionRequestList() local endIndex = #originList if start <= endIndex then local result = {0, 0} local requestList = {} for i = start, endIndex do table.insert(requestList, originList[i]) end result[1] = requestList result[2] = endIndex return result else return nil end end -- 返回接收的cmd和执行cmd的序列化统计 function BattleSimulator.GetFrameCmdStatistics() local getFrame2CmdDic, handleFrame2CmdDic = BattleDataGetter.GetFrame2CmdCountDicAndClear() local maxFrame = BattleDataGetter.GetCurFrameIndex() local statistic = battle_common_pb.Frame2CmdStatistic() for i = 1,maxFrame do table.insert(statistic.getCmds, getFrame2CmdDic[i] or 0) table.insert(statistic.handleCmds, handleFrame2CmdDic[i] or 0) end local datas = statistic:SerializeToString() return datas end function BattleSimulator.GetCountryEveningBossPlayerInfos() return LevelManager.CurLevelFunc:GetBattlePlayerInfos() end function BattleSimulator.GetCityWarPlayerInfos() return LevelManager.CurLevelFunc:GetBattlePlayerInfos() end --军团战 : 获取资源战场的资源数 function BattleSimulator.GetCityWarResourceAmount() local result = {0,0} local recordInfo = LevelManager.CurLevelFunc:GetResourceAmount() if recordInfo then if recordInfo[1] then result[1] = recordInfo[1] end if recordInfo[2] then result[2] = recordInfo[2] end end return result end function BattleSimulator.GetCityWarDefendBossIsDead() return LevelManager.CurLevelFunc.defineBossDead or false end function BattleSimulator.GetVehicleEntityCount() if LevelManager.CurLevelFunc.GetVehicleEntityCount then return LevelManager.CurLevelFunc:GetVehicleEntityCount() else return 0 end end function BattleSimulator.GetTowerMonsterIdByIndex(index) if LevelManager.CurLevelFunc.GetTowerMonsterIdByIndex then return LevelManager.CurLevelFunc:GetTowerMonsterIdByIndex(index) else return -1 end end function BattleSimulator.GetTowerEventList() if LevelManager.CurLevelFunc.GetTowerEventListPbBytes then return LevelManager.CurLevelFunc:GetTowerEventListPbBytes() else return "" end end --frameIndex : 如果传nil,则会清空所有的事件,如果传一个number,则会清理掉列表里所有frameId <= frameIndex的事件 function BattleSimulator.ClearTowerEventList(toFrameIndex) if LevelManager.CurLevelFunc.ClearTowerEventList then LevelManager.CurLevelFunc:ClearTowerEventList(toFrameIndex) end end function BattleSimulator.GetCityWarWallDamage() if LevelManager.CurLevelFunc.GetWallDamage then return LevelManager.CurLevelFunc:GetWallDamage() else return 0 end end function BattleSimulator.GetCityWarResourcesList() if LevelManager.CurLevelFunc.GetResourcesListPbBytes then return LevelManager.CurLevelFunc:GetResourcesListPbBytes() else return "" end end function BattleSimulator.GetCityWarStatueHpLossList() if LevelManager.CurLevelFunc.GetStatueHpLossListPbBytes then return LevelManager.CurLevelFunc:GetStatueHpLossListPbBytes() else return "" end end ----------- ↑ 服务器校验 使用的 接口 ↑ -------- ------- ↓ 服务器 校验单机的战斗 ↓-------------- local RoleEnterScenePath = "ReplayData/RoleEnterScene.pb" local CheckFrameDataPath = "ReplayData/FrameDatas" local S2CFubenEnemyInfoPath = "ReplayData/S2CFubenEnemyInfo.pb" private.WriteFunc[MessageEnums.MessageS2CRoleEnterScene] = function (bytes) LuaExt.FileWriteBytes(RoleEnterScenePath,false, bytes) end private.WriteFunc[MessageEnums.MessageS2CFubenEnemyInfo] = function (bytes) LuaExt.FileWriteBytes(S2CFubenEnemyInfoPath,false, bytes) end function BattleSimulator.SaveAsyncFrames(framebytes) if private.SaveFrameData then LuaExt.FileWriteBytes(CheckFrameDataPath,false, framebytes) private.SaveFrameData = false -- 为了防止回主城时错误记录回主城的数据 end end function BattleSimulator.ReplayAsyncLevel() --优先使用服务器的数据回放 ,如果没有用本地保存的数据回放 local roleEnterSceneMsg = nil local frameCmdsBytes = nil local s2CFubenEnemyInfo = nil local dataBytes = LuaExt.FileReadBytes(ServerInfo) if dataBytes then local msg = battle_common_pb.OfflineBattleCheckInfo() msg:ParseFromString(dataBytes) roleEnterSceneMsg = msg.roleEnterScene frameCmdsBytes = msg.frames s2CFubenEnemyInfo = msg.s2CFubenEnemyInfo logging.log(LTAG.LOG, "离线校验-客户端的校验结果 :", msg.clientCheckStr) logging.log(LTAG.LOG, "离线校验-服务器的校验结果 :", msg.serverCheckStr) else roleEnterSceneMsg = LuaExt.FileReadBytes(RoleEnterScenePath) frameCmdsBytes = LuaExt.FileReadBytes(CheckFrameDataPath) s2CFubenEnemyInfo = LuaExt.FileReadBytes(S2CFubenEnemyInfoPath) end private.HandleRoleEnterSceneAndFrame(roleEnterSceneMsg,frameCmdsBytes,s2CFubenEnemyInfo) end --客户端重放和服务器校验公用 function private.HandleRoleEnterSceneAndFrame(roleEnterSceneMsg, frameCmdsBytes, s2CFubenEnemyInfo) -- 类似 S2CRoleEnterSceneResp(msgResp) local msgResp = role_enterScene_pb.S2CRoleEnterScene() msgResp:ParseFromString(roleEnterSceneMsg) LevelPlayerManager.BeforeStorePlayerInfo() LevelManager.BeforeRoleEnterSceneResp() local levelInfo = DataSheetManager.LevelInfoData:GetDataByLevelID(msgResp.mapCfgId) --当前非同步的公平竞技玩法 local isFairLevel = levelInfo.LevelType == LuaConst.LevelType.BossChallenge or levelInfo.LevelType == LuaConst.LevelType.FairNormalPVE or levelInfo.LevelType == LuaConst.LevelType.SubjectMonthBossChallenge ----敌人信息 ↓ if s2CFubenEnemyInfo and not string.isNilOrEmpty(s2CFubenEnemyInfo) then local msgEnemy = role_enterScene_pb.S2CFubenEnemyInfo() msgEnemy:ParseFromString(s2CFubenEnemyInfo) local enemyPlayerInfo = { name = msgEnemy.name, setting = { [1] = false }, officialPosition = msgEnemy.officialTitle, guildEmblemContent = msgEnemy.guildShow.emblemContent, guildEmblemFrame = msgEnemy.guildShow.emblemFrame, guildID = msgEnemy.guildShow.guildId, guildName = msgEnemy.guildShow.guildName, guildJob = msgEnemy.guildShow.jobTitle, guildIdentity = msgEnemy.guildShow.identity, } LevelPlayerManager.SetNotSyncArmyTable(LuaConst.STANDALONE_ENEMYPLAYER1_PLAYID, msgEnemy.enemyHeroLineUpInfos, enemyPlayerInfo, isFairLevel, msgEnemy.stratagemDevelopEffects) end if IsServerDataNotNil(msgResp.enemyInfo) then local playerInfo = { name = msgResp.enemyInfo.name, setting = { [1] = false }, officialPosition = msgResp.enemyInfo.officialTitle, fightScores = msgResp.enemyInfo.fightScores} private.SyncRoleGuildInfo(playerInfo, msgResp.enemyInfo.guildShow.emblemContent, msgResp.enemyInfo.guildShow.emblemFrame, msgResp.enemyInfo.guildShow.guildId, msgResp.enemyInfo.guildShow.guildName, msgResp.enemyInfo.guildShow.jobTitle, msgResp.enemyInfo.guildShow.identity, msgResp.enemyInfo.serId, msgResp.enemyInfo.portraitId, msgResp.enemyInfo.frameId) local robotEnemyRecommendGS = msgResp.enemyInfo.isRobot and msgResp.enemyInfo.recommendGS1 or nil NotSyncBattleSetter.SetPlayerArmyTable(LuaConst.STANDALONE_ENEMYPLAYER1_PLAYID, msgResp.enemyInfo.enemyHeroLineUpInfos, playerInfo, false, msgResp.enemyInfo.stratagemDevelopEffects, robotEnemyRecommendGS) local extraInfo = msgResp.enemyInfo.isRobot and {recommendGS1 = robotEnemyRecommendGS, recommendGS2 = msgResp.enemyInfo.recommendGS2, recommendGS3 = msgResp.enemyInfo.recommendGS3 } or nil NotSyncBattleSetter.SetPlayerReserveArmyInfo(LuaConst.STANDALONE_ENEMYPLAYER1_PLAYID, msgResp.enemyInfo.enemyHeroLineUpInfos, msgResp.enemyInfo.enemyHeroLineUpInfos2, msgResp.enemyInfo.enemyHeroLineUpInfos3, extraInfo) end ----敌人信息 ↑ --玩家在场景中的状态 LevelManager.ScenePlayerState = msgResp.playerState local basicInfo = msgResp.BasicInfo local roleAccid = basicInfo.accid local roleName = basicInfo.Name if msgResp.curTowerId > 0 then NotSyncBattleSetter.SetClimbTowerId(msgResp.curTowerId) end LogicUpdateManager.SetServerReplenishFrame(-1) -- 防止同步玩法断线重连后,异常退出关卡,再次进入pve出现表现不能动问题(暂时没有出现,理论上也不会有,多一个层保护而已) -- 普通离线玩法,主城没有 local playerSet = {} for i,set in ipairs(basicInfo.settingInfo) do playerSet[set.settingId] = set.state end if IsServerDataNotNil(msgResp.heroLineUpInfos) then local pos,dir = LevelPlayerManager.GetMoveFromSceneInfo(basicInfo.SceneInfo) local playerInfo = { acid = roleAccid, name = roleName, setting = playerSet, Pos = pos, Dir = dir , officialPosition = basicInfo.officialTitle, countryId = basicInfo.countryInfo.countryId, countryJobId = basicInfo.countryInfo.countryJobId, fightScores = msgResp.fightScores } NotSyncBattleSetter.SetPlayerArmyTable(LuaConst.STANDALONE_PLAYER_PlayID, msgResp.heroLineUpInfos, playerInfo, isFairLevel, msgResp.stratagemDevelopEffects, nil, levelInfo.LevelType)--new 从LevelSelect_Model的OnEnterFubenResponse移过来的 NotSyncBattleSetter.SetPlayerReserveArmyInfo(LuaConst.STANDALONE_PLAYER_PlayID, msgResp.heroLineUpInfos, msgResp.heroLineUpInfos2, msgResp.heroLineUpInfos3) end -- boss 挑战玩法,与上互斥 ;simpleAttrs 起名非常不好,其实意义是 HeroSampleInfo ,主城没有 if IsServerDataNotNil(msgResp.simpleAttrs) then local pos,dir = LevelPlayerManager.GetMoveFromSceneInfo(basicInfo.SceneInfo) local playerInfo = { acid = roleAccid, name = roleName, setting = playerSet, Pos = pos, Dir = dir, fightScores = msgResp.fightScores } NotSyncBattleSetter.SetPlayerArmyTable(LuaConst.STANDALONE_PLAYER_PlayID, msgResp.simpleAttrs, playerInfo, isFairLevel, msgResp.stratagemDevelopEffects) end if IsServerDataNotNil(msgResp.buffId) then NotSyncBattleSetter.SetLevelBuffIdList(msgResp.buffId) end if IsServerDataNotNil(msgResp.playerBuffs) then NotSyncBattleSetter.SetPlayerBuffInfoList(msgResp.playerBuffs) end NotSyncBattleSetter.SetClientIntermediateParamsBytes(msgResp.clientIntermediateParams) if IsServerDataNotNil(msgResp.goldHeroBoss) then LevelManager.SetServerGs(msgResp.goldHeroBoss.recommendGs) LevelManager.SetServerBossHp(msgResp.goldHeroBoss.curBossHp, msgResp.goldHeroBoss.maxBossHp) LevelManager.SetGsGroupId(msgResp.goldHeroBoss.gsGroupId) LevelManager.SetBossLevel(msgResp.goldHeroBoss.bossLevel) elseif msgResp.serverFightScore > 0 then LevelManager.SetServerGs(msgResp.serverFightScore) end if IsProtoDataNotNil(msgResp.gveInfo) then --slg关卡的服务器数据 LevelManager.SetServerGs(msgResp.gveInfo.recommendGs) LevelManager.SetServerBossHp(msgResp.gveInfo.curBossHp, msgResp.gveInfo.maxBossHp) LevelManager.SetSlgGveData(msgResp.gveInfo.landId, msgResp.gveInfo.thiefId, msgResp.gveInfo.enemyBuffId, msgResp.gveInfo.openDayId) end if msgResp.subjugateDataPVEID > 0 then LevelManager.SetSubjugateDataPVEID(msgResp.subjugateDataPVEID) else LevelManager.SetSubjugateDataPVEID(nil) end local randomSeed = msgResp.randomSeed --msgResp.sceneType : 1 主城 ;2 离线战斗 ;3 在线战斗 ;4 动态场景 boss准备 军团领地 logging.log(LTAG.LOG, "校验服[进入关卡]-------RoleEnterScene----- sceneType = " , msgResp.sceneType , " ,mapid = " ,msgResp.mapCfgId ,"gotoType =" , msgResp.gotoType) -- msgResp.gotoType==2 ,msgResp.gotoType==5(换城) --爬塔关走单独的关卡id if levelInfo and levelInfo.LevelType == LuaConst.LevelType.ClimbTower then local data= DataSheetManager.ClimbTowerLevel:GetDataByID(msgResp.curTowerId) LevelManager.SwitchCombinationLevel(data.CombinationLevelID, randomSeed, msgResp.needCheck, true) elseif levelInfo and levelInfo.LevelType == LuaConst.LevelType.NationClimbTower then local data= DataSheetManager.NationClimbTowerLevel:GetDataByID(msgResp.curTowerId) LevelManager.SwitchCombinationLevel(data.CombinationLevelID, randomSeed, msgResp.needCheck, true) else LevelManager.SwitchLevel(msgResp.mapCfgId, randomSeed, msgResp.needCheck, false) end --设置要使用的cmd local frameCommandList = {} local cmdDic = { [LuaConst.STANDALONE_PLAYER_PlayID] = frameCommandList} local frameCmds = Infrast.DecodeFromBytes(frameCmdsBytes) local count = #frameCmds / 9 --9是 LevelManager.RecordFrameCmd的参数个数 local maxFrame = 1 for i=1,count do local startIndex = (i-1) * 9 + 1 local command = Command.TryGet() command.Frame = frameCmds[startIndex] command.MoveType = frameCmds[startIndex + 1] command.MoveDir:Rawset(frameCmds[startIndex + 2], frameCmds[startIndex + 3])-- 乘过1000的了 command.RotateDir:Reset(command.MoveDir) command.SkillIndex = frameCmds[startIndex + 4] command.SpecialCommand = frameCmds[startIndex + 5] command.UseJoyStick = frameCmds[startIndex + 6] command.SkillJoystickPosX:Rawset( frameCmds[startIndex + 7] * 10)-- Rawset乘10代表保留两位小数【传输的是short最大只能是32768,只能表示±327.68以内的值】 command.SkillJoystickPosZ:Rawset( frameCmds[startIndex + 8] * 10)-- Rawset乘10代表保留两位小数【传输的是short最大只能是32768,只能表示±327.68以内的值】 frameCommandList[command.Frame] = {command} maxFrame = command.Frame end LogicUpdateManager.SetSyncCommandDic(cmdDic,maxFrame) LogicUpdateManager.IsReplayOrServerCheck = true return maxFrame end --服务器使用的接口 function BattleSimulator.CheckNotSyncLevelBattle(...) local roleEnterSceneMsg , frameCmdsBytes, s2CFubenEnemyInfo = ... local maxFrame = private.HandleRoleEnterSceneAndFrame(roleEnterSceneMsg, frameCmdsBytes, s2CFubenEnemyInfo) while(LogicUpdateManager._clientHandleFrame < maxFrame) do if not LogicUpdateManager.FightBegin then -- 服务器发送的帧数据是多余结算时的帧的 ,所以结算后就要停止。 return true end LogicUpdateManager.OnLogicUpdate(0.05) end end function BattleSimulator.CheckNotSyncLevelBattleByFile(dataBytes) local msg = battle_common_pb.OfflineBattleCheckInfo() msg:ParseFromString(dataBytes) BattleSimulator.CheckNotSyncLevelBattle(msg.roleEnterScene, msg.frames, msg.s2CFubenEnemyInfo) end function private.SyncRoleGuildInfo(roleInfo, emblemContent, emblemFrame, guildId, guildName, jobTitle, identity, serId, portraitId, frameId) roleInfo.guildEmblemContent = emblemContent roleInfo.guildEmblemFrame = emblemFrame roleInfo.guildID = guildId roleInfo.guildName = guildName roleInfo.guildJob = jobTitle roleInfo.guildIdentity = identity roleInfo.serId = serId roleInfo.portraitId = portraitId roleInfo.frameId = frameId end ------- ↑ 服务器 校验单机的战斗 ↑-------------- --region 客户端(玩家)回放使用的接口 -- 回放单机离线关卡 function BattleSimulator.ClientReplayAsyncLevel(offlineBattleCheckInfoBytes) local msg = battle_common_pb.OfflineBattleCheckInfo() msg:ParseFromString(offlineBattleCheckInfoBytes) local roleEnterSceneMsg = msg.roleEnterScene local frameCmdsBytes = msg.frames local s2CFubenEnemyInfo = msg.s2CFubenEnemyInfo private.HandleRoleEnterSceneAndFrame(roleEnterSceneMsg,frameCmdsBytes,s2CFubenEnemyInfo) end --endregion --region 客户端(玩家)使用本地文件 回放同步玩法(3v3,1v1) -- 服务器记录的数据,进行播放 。(客户端点击了回放按钮 、服务器校验入口) function BattleSimulator.ClientReplaySyncLevel_File(dataBytes, gRoomID) LogicUpdateManager.IsReplayOrServerCheck = true private.isServerData = true private.clientReplayType = ReplayTypeEnum.Client_FileFrameData private.clientReplayGroomID = gRoomID local msg = battle_common_pb.ForCheckBattleAllInfo() msg:ParseFromString(dataBytes) private.GetServerInfo(msg) end function private.ReadyDone_ClientFrameFile() LogicUpdateManager.ResetData() KCPTransport.BindFrameCallBack() SaveSyncBattleInfoManager.GetFrameDatasFromBytes(private.ServerFrameDatas) -- 存储的结构是CmdB2CFramePBInfos --因为在MultiplayManager里,隐含了LevelManager.IsSyncLevel(),所以下面不用判断LevelManager.IsSyncLevel() if LevelManager.IsSyncTeamPVP() or LevelManager.IsSyncSolo() then MultiplayManager.SetAllRolesActive() -- 关闭loading界面等 ExpLevelManager.EnterLevel(LevelManager.GetCurInfoData()) end LevelManager.EnterSyncLevel() MultiReconnectManager.SetInBattleReconnect(true) private.clientReplayGroomID = nil end --endregion return BattleSimulator