using System.Collections.Generic; using UnityEngine; using LitJson; using System; public class BattleManager : GameSystemManager { public StoryBattleField storyBattleField = null; // 同时只能有一场战斗在进行 guid, battlefield protected Dictionary battleFields = new Dictionary(); public float[] speedGear; //战斗倍数对应的实际速率 public int speedIndex { get { return QuickSetting.Instance.GetQuickSettingValue(QuickSettingType.BattleSpeed, 0); } set { QuickSetting.Instance.SetQuickSetting(QuickSettingType.BattleSpeed, value); QuickSetting.Instance.SendPackage(); } } public Action onBattleFieldCreate; public Action onBattleFieldDestroy; public bool isWaitServerStory = false; //主线等服务端回报 0425 public override void Init() { base.Init(); LogicEngine.Instance.OnUpdate += Run; DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk; DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += BeforePlayerInit; ParseConfig(); } void ParseConfig() { var config = FuncConfigConfig.Get("AutoGuaji"); speedGear = JsonMapper.ToObject(config.Numerical4); } public override void Release() { base.Release(); LogicEngine.Instance.OnUpdate -= Run; DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk; DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= BeforePlayerInit; } 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 blueTeamList = null) { if (null == storyBattleField) { var redTeamList = new List(); TeamBase storyTeam = TeamManager.Instance.GetTeam(TeamType.Story); redTeamList.Add(storyTeam); CreateBattleField(string.Empty, MapID, FuncLineID, extendData, redTeamList, blueTeamList); } else { // storyBattleField } } public void OnBattleClose(BattleField _battleField) { } #region 截断网络派发包 只收入当前包的后续 b425是主线的 非主线的包并不会走b425 private bool allow = true; private Queue packQueue = new Queue(); public bool IsCanDistributePackage(GameNetPackBasic _package) { if (_package is HB425_tagSCTurnFightReportSign) { 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); } } return allow; } protected int continousEmptyCount = 0; // 连续空包计数 protected void AnalysisPackQueueAndDistribute() { // 建议前端做一个防范机制:当连续多次请求得到空的战斗片段封包时(不包含B425标记的开始跟结束封包,即开始跟中间没有任何封包),强制自动帮玩家回城休息, // 原因可能前后端数据不一致bug(比如战锤可能后端没有了,前端认为还有)或者 后端有bug导致没有处理战斗 // 为防止死循环,可强制回城休息,让玩家重新点击关卡战斗或挑战boss, // 正常情况下在战锤足够时理论上都可以一直循环刷怪,如果连续多次没有战斗片段封包,比如限制个连续10次以内,就可以理解为异常了 const int MaxContinousEmptyCount = 10; // 连续空包最大次数 List packQueueSnapshot = new List(packQueue); List newPackList = ParseBattlePackList(string.Empty, packQueueSnapshot); Action printNewPack = () => { string temp = "After AnalysisPackQueueAndDistribute newPackList count: " + newPackList.Count + "\n"; foreach (var pack in newPackList) { if (pack is CustomHB426CombinePack b426Pack) { temp += " pack type is " + pack.GetType().Name + " tag is " + (b426Pack.startTag != null ? b426Pack.startTag.Tag : "null") + "\n"; } else if (pack is CustomB421ActionPack b421Pack) { temp += " pack type is " + pack.GetType().Name + " guid is " + b421Pack.guid + "\n"; } else { temp += " pack type is " + pack.GetType().Name + "\n"; } } Debug.LogWarning(temp); }; printNewPack(); // HashSet skipIndexes = new HashSet(); // // 这里已经是按照Dequeue的顺序了 // for (int i = 0; i < packQueueSnapshot.Count; i++) // { // if (skipIndexes.Contains(i)) continue; // GameNetPackBasic pack = packQueueSnapshot[i]; // // 碰到B421 截断 往下收集b421里的全部内容 // if (pack is HB421_tagMCTurnFightObjAction) // { // HB421_tagMCTurnFightObjAction b421Pack = pack as HB421_tagMCTurnFightObjAction; // List b421PackList = new List(); // 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(newPackList); DistributeNextPackage(); } public static List ParseBattlePackList(string guid, List packQueueSnapshot) { var list = CustomHB426CombinePack.CombineToSkillPackFromList(guid, packQueueSnapshot); string str = "ParseBattlePackList \n"; for (int i = 0; i < list.Count; i++) { str += " " + list[i].GetType().Name + "\n"; } // BattleDebug.LogError(str); return list; } // 专属于主线战斗的派发 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> battleReportDict = new Dictionary>(); protected Dictionary> battlePackRelationList = new Dictionary>(); public void PushPackage(string guid, GameNetPackBasic vNetPack) { Queue queue = null; if (!battleReportDict.TryGetValue(guid, out queue)) { queue = new Queue(); battleReportDict.Add(guid, queue); } queue.Enqueue(vNetPack); List uidList = null; if (!battlePackRelationList.TryGetValue(guid, out uidList)) { uidList = new List(); battlePackRelationList.Add(guid, uidList); } if (!uidList.Contains(vNetPack.packUID)) { uidList.Add(vNetPack.packUID); } } public void PushPackUID(string guid, ulong packUID) { List uidList = null; if (!battlePackRelationList.TryGetValue(guid, out uidList)) { uidList = new List(); battlePackRelationList.Add(guid, uidList); } if (!uidList.Contains(packUID)) { uidList.Add(packUID); } } public BattleField GetBattleField(ulong packUID) { string guid = GetGUID(packUID); BattleField battleField = GetBattleField(GetGUID(packUID)); if (battleField == null || battleField.rejectNewPackage) { return null; } return battleField; } 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 queue = null; if (!battleReportDict.TryGetValue(guid, out queue)) { BattleDebug.LogError("DistributeNextReportPackage could not find queue for guid : " + guid); return; } if (queue.Count <= 0) { 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); } } #endregion public BattleField CreateBattleField(string guid, int MapID, int FuncLineID, JsonData extendData, List redTeamList, List 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) { // Debug.LogError("MainFightRequest reqType " + reqType + " reqValue " + reqValue); 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 { List keys = new List(battleFields.Keys); for (int i = keys.Count - 1; i >= 0; i--) { var battleField = battleFields[keys[i]]; battleField?.Run(); } } catch (System.Exception ex) { Debug.LogError(ex); } } public BattleField GetBattleFieldByMapID(int v) { foreach (var battleField in battleFields) { if (battleField.Value.MapID == v) { return battleField.Value; } } return null; } }