hch
2 小时以前 c26c07703564106864e6c1af1b437f4060c6cbac
Main/System/Battle/BattleManager.cs
@@ -1,43 +1,498 @@
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);
        }
    }