| | |
| | | using System.Collections.Generic; |
| | | using UnityEngine; |
| | | |
| | | |
| | | using LitJson; |
| | | using System; |
| | | |
| | | public class BattleManager : GameSystemManager<BattleManager> |
| | | { |
| | | public StoryBattleField storyBattleField = new StoryBattleField();//主线战场 |
| | | public StoryBattleField storyBattleField = null; |
| | | |
| | | protected Dictionary<int, BattleField> battleFields = new Dictionary<int, BattleField>(); |
| | | // 同时只能有一场战斗在进行 guid, battlefield |
| | | protected Dictionary<string, BattleField> battleFields = new Dictionary<string, BattleField>(); |
| | | |
| | | |
| | | public Action<string, BattleField> onBattleFieldCreate; |
| | | |
| | | public Action<string, BattleField> onBattleFieldDestroy; |
| | | |
| | | public bool isWaitServerStory = false; //主线等服务端回报 0425 |
| | | |
| | | public override void Init() |
| | | { |
| | | base.Init(); |
| | | |
| | | LogicEngine.Instance.OnUpdate += Run; |
| | | DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk; |
| | | DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += BeforePlayerInit; |
| | | |
| | | } |
| | | |
| | | public override void Release() |
| | | { |
| | | base.Release(); |
| | | LogicEngine.Instance.OnUpdate -= Run; |
| | | DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk; |
| | | DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= BeforePlayerInit; |
| | | } |
| | | |
| | | public void StartStoryBattle() |
| | | protected void OnPlayerLoginOk() |
| | | { |
| | | long exAttr1 = PlayerDatas.Instance.GetPlayerDataByType(PlayerDataType.ExAttr1); |
| | | long exAttr2 = PlayerDatas.Instance.GetPlayerDataByType(PlayerDataType.ExAttr2); |
| | | |
| | | int MapID = 1; |
| | | int FuncLineID = (int)exAttr2; |
| | | |
| | | CreateStoryBattle(MapID, FuncLineID, null, null); |
| | | } |
| | | |
| | | void BeforePlayerInit() |
| | | { |
| | | isWaitServerStory = false; //后续考虑断线重连 |
| | | } |
| | | |
| | | // 上游戏的时候 等战斗阵容更新完毕 创建主线副本 敌方的数据可以暂时不显示 己方表现为睡觉 |
| | | // 如果主线副本存在 那么维持当前的副本不变 |
| | | protected void CreateStoryBattle(int MapID, int FuncLineID, JsonData extendData, List<TeamBase> blueTeamList = null) |
| | | { |
| | | if (null == storyBattleField) |
| | | { |
| | | storyBattleField = new StoryBattleField(); |
| | | var redTeamList = new List<TeamBase>(); |
| | | |
| | | TeamBase storyTeam = TeamManager.Instance.GetTeam(TeamType.Story); |
| | | |
| | | redTeamList.Add(storyTeam); |
| | | |
| | | CreateBattleField(string.Empty, MapID, FuncLineID, extendData, redTeamList, blueTeamList); |
| | | } |
| | | else |
| | | { |
| | | // storyBattleField |
| | | } |
| | | } |
| | | |
| | | public void Run() |
| | | public void OnBattleClose(BattleField _battleField) |
| | | { |
| | | if (null != storyBattleField) |
| | | |
| | | } |
| | | |
| | | #region 截断网络派发包 只收入当前包的后续 b425是主线的 非主线的包并不会走b425 |
| | | private bool allow = true; |
| | | |
| | | private Queue<GameNetPackBasic> packQueue = new Queue<GameNetPackBasic>(); |
| | | |
| | | public bool IsCanDistributePackage(GameNetPackBasic _package) |
| | | { |
| | | if (_package is HB425_tagSCTurnFightReportSign) |
| | | { |
| | | storyBattleField.Run(); |
| | | HB425_tagSCTurnFightReportSign pkg = _package as HB425_tagSCTurnFightReportSign; |
| | | |
| | | // 0-战报片段开始;1-战报片段结束; |
| | | if (pkg.Sign == 0) |
| | | { |
| | | allow = false; |
| | | } |
| | | else |
| | | { |
| | | allow = true; |
| | | |
| | | // 发送战报片段结束包 |
| | | AnalysisPackQueueAndDistribute(); |
| | | |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (!allow) |
| | | { |
| | | packQueue.Enqueue(_package); |
| | | } |
| | | } |
| | | |
| | | foreach (var battleField in battleFields) |
| | | return allow; |
| | | } |
| | | |
| | | protected int continousEmptyCount = 0; // 连续空包计数 |
| | | |
| | | protected void AnalysisPackQueueAndDistribute() |
| | | { |
| | | // 建议前端做一个防范机制:当连续多次请求得到空的战斗片段封包时(不包含B425标记的开始跟结束封包,即开始跟中间没有任何封包),强制自动帮玩家回城休息, |
| | | // 原因可能前后端数据不一致bug(比如战锤可能后端没有了,前端认为还有)或者 后端有bug导致没有处理战斗 |
| | | // 为防止死循环,可强制回城休息,让玩家重新点击关卡战斗或挑战boss, |
| | | // 正常情况下在战锤足够时理论上都可以一直循环刷怪,如果连续多次没有战斗片段封包,比如限制个连续10次以内,就可以理解为异常了 |
| | | |
| | | const int MaxContinousEmptyCount = 10; // 连续空包最大次数 |
| | | |
| | | List<GameNetPackBasic> packQueueSnapshot = new List<GameNetPackBasic>(packQueue); |
| | | |
| | | List<GameNetPackBasic> newPackList = new List<GameNetPackBasic>(); |
| | | HashSet<int> skipIndexes = new HashSet<int>(); |
| | | |
| | | // 这里已经是按照Dequeue的顺序了 |
| | | for (int i = 0; i < packQueueSnapshot.Count; i++) |
| | | { |
| | | battleField.Value?.Run(); |
| | | if (skipIndexes.Contains(i)) continue; |
| | | |
| | | GameNetPackBasic pack = packQueueSnapshot[i]; |
| | | |
| | | // 碰到B421 截断 往下收集b421里的全部内容 |
| | | if (pack is HB421_tagMCTurnFightObjAction) |
| | | { |
| | | HB421_tagMCTurnFightObjAction b421Pack = pack as HB421_tagMCTurnFightObjAction; |
| | | |
| | | List<GameNetPackBasic> b421PackList = new List<GameNetPackBasic>(); |
| | | i++; // 跳过当前的B421包 |
| | | |
| | | // 收集所有非B421包,直到遇到下一个B421或队列结束 |
| | | for (; i < packQueueSnapshot.Count; i++) |
| | | { |
| | | GameNetPackBasic nextPack = packQueueSnapshot[i]; |
| | | if (nextPack is HB421_tagMCTurnFightObjAction) |
| | | { |
| | | i--; // 回退一个位置,留给外层循环处理 |
| | | break; |
| | | } |
| | | else |
| | | { |
| | | b421PackList.Add(nextPack); |
| | | skipIndexes.Add(i); // 标记已被合包 |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | // 合并所有相关包 |
| | | CustomB421ActionPack actionPack = CustomB421ActionPack.CreateB421ActionPack(GetGUID(b421Pack.packUID), b421PackList); |
| | | |
| | | newPackList.Add(actionPack); |
| | | } |
| | | else |
| | | { |
| | | newPackList.Add(pack); |
| | | } |
| | | } |
| | | |
| | | // 防范机制:连续多次没有战斗片段封包时自动回城 |
| | | if (newPackList.Count == 0) |
| | | { |
| | | continousEmptyCount++; |
| | | Debug.LogWarning($"连续空战斗片段封包次数:{continousEmptyCount}"); |
| | | if (continousEmptyCount >= MaxContinousEmptyCount) |
| | | { |
| | | Debug.LogError("连续多次没有战斗片段封包,自动回城休息!"); |
| | | MainFightRequest(0); // 0-停止战斗回城 |
| | | continousEmptyCount = 0; |
| | | packQueue.Clear(); |
| | | return; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | continousEmptyCount = 0; // 有包就重置 |
| | | } |
| | | |
| | | |
| | | // b421跟b426的包已经处理完了 |
| | | packQueue.Clear(); |
| | | for (int i = 0; i < newPackList.Count; i++) |
| | | { |
| | | var pack = newPackList[i]; |
| | | |
| | | packQueue.Enqueue(pack); |
| | | } |
| | | |
| | | |
| | | |
| | | // packQueue = new Queue<GameNetPackBasic>(newPackList); |
| | | |
| | | DistributeNextPackage(); |
| | | } |
| | | |
| | | // 专属于主线战斗的派发 |
| | | public bool DistributeNextPackage() |
| | | { |
| | | if (packQueue == null) |
| | | { |
| | | Debug.LogWarning("DistributeNextPackage: packQueue为空或已处理完毕"); |
| | | return false; |
| | | } |
| | | |
| | | if (packQueue.Count <= 0) |
| | | { |
| | | return false; |
| | | } |
| | | |
| | | GameNetPackBasic pack = null; |
| | | try |
| | | { |
| | | pack = packQueue.Dequeue(); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError("DistributeNextPackage: Peek异常 " + ex); |
| | | return false; |
| | | } |
| | | |
| | | try |
| | | { |
| | | if (pack is CustomHB426CombinePack combinePack) |
| | | { |
| | | combinePack.Distribute(); |
| | | } |
| | | else if (pack is CustomB421ActionPack actionPack) |
| | | { |
| | | actionPack.Distribute(); |
| | | } |
| | | else |
| | | { |
| | | PackageRegedit.Distribute(pack); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError("DistributeNextPackage: 分发包异常 " + ex); |
| | | // 出错时主动移除当前包,防止死循环 |
| | | if (packQueue.Count > 0) |
| | | { |
| | | packQueue.Dequeue(); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | return packQueue.Count > 0; |
| | | } |
| | | |
| | | public void OnConnected() |
| | | { |
| | | if (!allow) |
| | | { |
| | | allow = true; |
| | | packQueue.Clear(); |
| | | |
| | | // 重新发送要上一组战斗包的请求 |
| | | // TODO YYL |
| | | } |
| | | } |
| | | |
| | | #endregion |
| | | |
| | | #region 战报部分 |
| | | |
| | | protected Dictionary<string, Queue<GameNetPackBasic>> battleReportDict = new Dictionary<string, Queue<GameNetPackBasic>>(); |
| | | |
| | | protected Dictionary<string, List<ulong>> battlePackRelationList = new Dictionary<string, List<ulong>>(); |
| | | |
| | | public void PushPackage(string guid, GameNetPackBasic vNetPack) |
| | | { |
| | | Queue<GameNetPackBasic> queue = null; |
| | | |
| | | if (!battleReportDict.TryGetValue(guid, out queue)) |
| | | { |
| | | queue = new Queue<GameNetPackBasic>(); |
| | | battleReportDict.Add(guid, queue); |
| | | } |
| | | |
| | | queue.Enqueue(vNetPack); |
| | | |
| | | List<ulong> uidList = null; |
| | | |
| | | if (!battlePackRelationList.TryGetValue(guid, out uidList)) |
| | | { |
| | | uidList = new List<ulong>(); |
| | | battlePackRelationList.Add(guid, uidList); |
| | | } |
| | | |
| | | if (!uidList.Contains(vNetPack.packUID)) |
| | | { |
| | | uidList.Add(vNetPack.packUID); |
| | | } |
| | | } |
| | | |
| | | public void PushPackUID(string guid, ulong packUID) |
| | | { |
| | | List<ulong> uidList = null; |
| | | |
| | | if (!battlePackRelationList.TryGetValue(guid, out uidList)) |
| | | { |
| | | uidList = new List<ulong>(); |
| | | battlePackRelationList.Add(guid, uidList); |
| | | } |
| | | |
| | | if (!uidList.Contains(packUID)) |
| | | { |
| | | uidList.Add(packUID); |
| | | } |
| | | } |
| | | |
| | | public BattleField GetBattleField(ulong packUID) |
| | | { |
| | | return GetBattleField(GetGUID(packUID)); |
| | | } |
| | | |
| | | public BattleField GetBattleField(string guid) |
| | | { |
| | | BattleField battleField = null; |
| | | battleFields.TryGetValue(guid, out battleField); |
| | | return battleField; |
| | | } |
| | | |
| | | public string GetGUID(ulong packUID) |
| | | { |
| | | foreach (var kv in battlePackRelationList) |
| | | { |
| | | if (kv.Value.Contains(packUID)) |
| | | { |
| | | return kv.Key; |
| | | } |
| | | } |
| | | return string.Empty; |
| | | } |
| | | |
| | | public void DistributeNextReportPackage(string guid) |
| | | { |
| | | Queue<GameNetPackBasic> queue = null; |
| | | |
| | | |
| | | if (!battleReportDict.TryGetValue(guid, out queue)) |
| | | { |
| | | BattleDebug.LogError("DistributeNextReportPackage could not find queue for guid : " + guid); |
| | | return; |
| | | } |
| | | |
| | | var pack = queue.Dequeue(); |
| | | |
| | | // Debug.LogError("DistributeNextReportPackage for guid : " + guid + " pack type : " + pack.GetType()); |
| | | |
| | | try |
| | | { |
| | | if (pack is CustomHB426CombinePack combinePack) |
| | | { |
| | | combinePack.Distribute(); |
| | | } |
| | | else if (pack is CustomB421ActionPack actionPack) |
| | | { |
| | | actionPack.Distribute(); |
| | | } |
| | | else |
| | | { |
| | | PackageRegedit.Distribute(pack); |
| | | } |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | Debug.LogError("DistributeNextPackage: 分发包异常 " + ex); |
| | | // 出错时主动移除当前包,防止死循环 |
| | | if (packQueue.Count > 0) |
| | | { |
| | | packQueue.Dequeue(); |
| | | } |
| | | } |
| | | |
| | | if (queue.Count <= 0) |
| | | { |
| | | battleReportDict.Remove(guid); |
| | | battlePackRelationList.Remove(guid); |
| | | } |
| | | |
| | | } |
| | | #endregion |
| | | |
| | | public BattleField CreateBattleField(string guid, int MapID, int FuncLineID, JsonData extendData, List<TeamBase> redTeamList, List<TeamBase> blueTeamList) |
| | | { |
| | | BattleField battleField = null; |
| | | |
| | | bool isCreate = true; |
| | | if (battleFields.TryGetValue(guid, out battleField)) |
| | | { |
| | | //主线战场需一直存在 |
| | | if (string.IsNullOrEmpty(guid)) |
| | | { |
| | | isCreate = false; |
| | | } |
| | | else |
| | | { |
| | | BattleDebug.LogError("战场已存在 先进行销毁"); |
| | | battleField.Destroy(); |
| | | } |
| | | } |
| | | |
| | | if (isCreate) |
| | | { |
| | | battleField = BattleFieldFactory.CreateBattleField(guid, MapID, FuncLineID, extendData, redTeamList, blueTeamList); |
| | | |
| | | if (string.IsNullOrEmpty(guid)) |
| | | { |
| | | storyBattleField = (StoryBattleField)battleField; |
| | | } |
| | | battleFields.Add(guid, battleField); |
| | | onBattleFieldCreate?.Invoke(guid, battleField); |
| | | } |
| | | |
| | | |
| | | |
| | | battleField.Init(MapID, FuncLineID, extendData, redTeamList, blueTeamList); |
| | | |
| | | return battleField; |
| | | } |
| | | |
| | | public void DestroyBattleField(BattleField battleField) |
| | | { |
| | | if (battleField == null) |
| | | { |
| | | BattleDebug.LogError("DestroyBattleField called with null battleField"); |
| | | return; |
| | | } |
| | | |
| | | battleField.Release(); |
| | | |
| | | string guid = battleField.guid; |
| | | |
| | | battleFields.Remove(guid); |
| | | battleReportDict.Remove(guid); |
| | | battlePackRelationList.Remove(guid); |
| | | |
| | | if (storyBattleField == battleField) |
| | | { |
| | | storyBattleField = null; |
| | | } |
| | | |
| | | GameObject.DestroyImmediate(battleField.battleRootNode.gameObject); |
| | | |
| | | onBattleFieldDestroy?.Invoke(battleField.guid, battleField); |
| | | |
| | | } |
| | | |
| | | |
| | | // 目前支持 BYTE ReqType; // 0-停止战斗回城;1-设置消耗倍值;2-挑战关卡小怪;3-挑战关卡boss;4-继续战斗; |
| | | // 0-停止战斗回城 - 玩家主动点击回城时发送 |
| | | // 1-设置消耗倍值 - 玩家设置消耗倍值,对应到玩家useHarmerCount的值 |
| | | // 2-挑战关卡小怪 - 玩家点击开始战斗时发送,仅从休息状态到开始战斗时发送即可 |
| | | // 3-重定义暂未使用 |
| | | // 4-继续战斗 - 玩家主线战斗中(包含主线小怪、主线boss),前端表现完后端同步的战斗片段后,可再回复该值,后端会根据战斗逻辑及流程自动回复下一段的战斗片段封包,一直循环 |
| | | public void MainFightRequest(byte reqType, uint reqValue = 0) |
| | | { |
| | | CB413_tagCSMainFightReq req = new CB413_tagCSMainFightReq(); |
| | | req.ReqType = reqType; |
| | | req.ReqValue = reqValue; |
| | | |
| | | GameNetSystem.Instance.SendInfo(req); |
| | | if (reqType >= 2) |
| | | isWaitServerStory = true; |
| | | } |
| | | |
| | | |
| | | public void Run() |
| | | { |
| | | try |
| | | { |
| | | foreach (var battleField in battleFields) |
| | | { |
| | | battleField.Value?.Run(); |
| | | } |
| | | } |
| | | catch (System.Exception ex) |
| | | { |
| | | Debug.LogError(ex); |
| | | } |
| | | } |
| | | |