lcy
2025-12-03 b9a6e7e896b451e9c915e782a1789b2afe079cc9
Main/System/Chat/ChatManager.cs
@@ -3,1447 +3,660 @@
using UnityEngine;
using System.Text;
using System;
using System.Text.RegularExpressions;
using LitJson;
using System.Linq;
public partial class ChatManager : GameSystemManager<ChatManager>
{
    public ChatChannel nowChatChannel;
public class ChatManager : GameSystemManager<ChatManager>
{
    public int CHAT_INFO_CNT = 50;
    //<ChannelType,TalkData>
    public Dictionary<ChatChannel, List<TalkData>> talkDict = new Dictionary<ChatChannel, List<TalkData>>();
    public Dictionary<int, ChatBubbleData> chatBubbles = new Dictionary<int, ChatBubbleData>();
    //<ChannelType,时间戳>
    public Dictionary<int, int> chatChannelSendTime = new Dictionary<int, int>();
    public event Action<ChatChannel, TalkData> OnUpdateTalkEvent;
    public event Action OnUpdateTalkCacheListEvent;
    public const int CHAT_TIP_CNT = 4;
    public const int CHAT_CELL_CNT = 6;
    public const int CHAT_INFO_LIMIT = 50;
    public Dictionary<int, int> chatChannelCD = new Dictionary<int, int>();
    public int[] areaMyColorArr;
    public Color32 areaMyColor;
    public int[] areaOtherColorArr;
    public Color32 areaOtherColor;
    public Dictionary<int, int[]> chatChannelBulletColorArrDict = new Dictionary<int, int[]>();
    public Dictionary<ChatChannel, Color32> chatChannelBulletColorDict = new Dictionary<ChatChannel, Color32>();
    public readonly int BugleItem = 0;//喇叭物品id
    static StringBuilder sb = new StringBuilder();
    /// <summary>
    /// 聊天信息
    /// </summary>
    private bool _lockUpdate = true;
    public bool lockUpdate {
        get {
            return _lockUpdate;
        }
        set {
            _lockUpdate = value;
            if (ChatFriend != null && ChatFriend.IsOpen)
            {
                ChatFriend.OnSetLock();
            }
        }
    }
    private Dictionary<ChatInfoType, List<ChatData>> chatDics = new Dictionary<ChatInfoType, List<ChatData>>();
    private Dictionary<ChatInfoType, bool> chatOpenDics = new Dictionary<ChatInfoType, bool>();
    public List<ChatData> chatDisplayList = new List<ChatData>();
    #region 私聊
    private Dictionary<int, List<ChatFriendData>> pteChatDics = new Dictionary<int, List<ChatFriendData>>();
    public event Action OnPteChatChangeEvent;
    public event Action<int> SelectRecentlyEvent;
    public static event OnChatPteRefresh OnRefreshPteChat;
    private int pteChatId = 0;
    public int PteChatID {
        get { return pteChatId; }
        set {
            if (pteChatId == value)
            {
                return;
            }
            pteChatId = value;
            if (OnPteChatChangeEvent != null)
            {
                OnPteChatChangeEvent();
            }
            if (ChatFriend != null && ChatFriend.IsOpen)
            {
                ChatFriend.RefreshChatInfo();
            }
        }
    }
    private string pteChatName = string.Empty;
    public string PteChatName {
        get { return pteChatName; }
        set { pteChatName = value; }
    }
    private ChatFriend m_ChatFriend;
    public ChatFriend ChatFriend {
        get { return m_ChatFriend; }
    }
    public void ClearPteChat(int playerId)
    {
        var id = (int)PlayerDatas.Instance.PlayerId + playerId;
        if (pteChatDics.ContainsKey(id))
        {
            pteChatDics.Remove(id);
            if (OnRefreshPteChat != null)
            {
                OnRefreshPteChat(null);
            }
        }
    }
    public void SelectRecentlyChat(int playerId)
    {
        if (SelectRecentlyEvent != null)
        {
            SelectRecentlyEvent(playerId);
        }
    }
    #endregion
    private List<ChatData> chatlist = new List<ChatData>();
    private List<ChatData> chatUpList = new List<ChatData>();
    public delegate void OnChatRefresh(ChatInfoType type);
    public static event OnChatRefresh OnRefreshChat;
    public delegate void OnChatSelfRefresh(ChatData data);
    public static event OnChatSelfRefresh OnRefreshSelf;
    public delegate void OnChatPteRefresh(ChatFriendData data);
    public ChatInfoType presentChatType {
        get; set;
    }
    public event Action OnClickCloseChatEvent;
    public event Action<bool> OnChatExtentOpenEvent;
    public event Action<ChatData> chatFloatUpdate;
    // TeamModel teamModel {
    //     get {
    //         return ModelCenter.Instance.GetModel<TeamModel>();
    //     }
    // }
    // FriendsModel friendModel {
    //     get { return ModelCenter.Instance.GetModel<FriendsModel>(); }
    // }
    // EquipGemModel equipGemModel { get { return ModelCenter.Instance.GetModel<EquipGemModel>(); } }
    // EquipStrengthModel equipStrengthModel { get { return ModelCenter.Instance.GetModel<EquipStrengthModel>(); } }
    // EquipStarModel equipStarModel { get { return ModelCenter.Instance.GetModel<EquipStarModel>(); } }
    // EquipTrainModel equipTrainModel { get { return ModelCenter.Instance.GetModel<EquipTrainModel>(); } }
    // EquipModel equipModel { get { return ModelCenter.Instance.GetModel<EquipModel>(); } }
    PackManager packManager => PackManager.Instance;
    // DungeonAssistModel dungeonAssistModel { get { return ModelCenter.Instance.GetModel<DungeonAssistModel>(); } }
    public Dictionary<ChatInfoType, List<string>> achievementRandoms = new Dictionary<ChatInfoType, List<string>>();
    List<string> assistThankLanguages = new List<string>();
    Int2 assistThankLevelLimit = new Int2(150, 200);
    public ChatManager()
    {
        chatOpenDics.Add(ChatInfoType.System, true);
        chatOpenDics.Add(ChatInfoType.World, true);
        chatOpenDics.Add(ChatInfoType.CrossServer, true);
        chatOpenDics.Add(ChatInfoType.Area, true);
        chatOpenDics.Add(ChatInfoType.Team, true);
        chatOpenDics.Add(ChatInfoType.Invite, true);
        chatOpenDics.Add(ChatInfoType.Trumpet, true);
        chatOpenDics.Add(ChatInfoType.Fairy, true);
        chatOpenDics.Add(ChatInfoType.default1, true);
        lockUpdate = true;
        presentChatType = ChatInfoType.World;
        IsExtentOpen = false;
        // var _funcCfg = FuncConfigConfig.Get("BugleItem");
        // BugleItem = int.Parse(_funcCfg.Numerical1);
        DTC0102_tagCDBPlayer.switchAccountEvent += SwitchAccountEvent;
        // TODO YYL
        // StageLoad.Instance.onStageLoadFinish += OnStageLoadFinish;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += PlayerLoginOkEvent;
        InitChatRedpoints();
        // FuncConfigConfig _cfg = FuncConfigConfig.Get("RandomWord");
        // try
        // {
        //     achievementRandoms.Add(ChatInfoType.World, new List<string>(ConfigParse.GetMultipleStr(_cfg.Numerical1)));
        //     achievementRandoms.Add(ChatInfoType.Fairy, new List<string>(ConfigParse.GetMultipleStr(_cfg.Numerical2)));
        //     var json = LitJson.JsonMapper.ToObject(_cfg.Numerical3);
        //     foreach (var key in json.Keys)
        //     {
        //         var type = int.Parse(key);
        //         m_TaskRandomChats.Add(type, new List<string>(LitJson.JsonMapper.ToObject<string[]>(json[key].ToJson())));
        //     }
        //     assistThankLanguages.AddRange(ConfigParse.GetMultipleStr(_cfg.Numerical4));
        //     if (!string.IsNullOrEmpty(_cfg.Numerical5))
        //     {
        //         var levelArray = ConfigParse.GetMultipleStr<int>(_cfg.Numerical5);
        //         assistThankLevelLimit = new Int2(levelArray[0], levelArray[1]);
        //     }
        // }
        // catch (Exception e)
        // {
        //     Debug.LogError(e.Message);
        // }
    }
    private void PlayerLoginOkEvent()
    {
        UpdateRedpoint(ChatInfoType.Team);
        UpdateRedpoint(ChatInfoType.Fairy);
    }
    private void SwitchAccountEvent()
    {
        ClearAllChatInfo();
    }
    void ChatReport(ChatInfoType chatType, string content, string toPlayer)
    {
        try
        {
            if (ChatCenter.Instance.IsChatBanned || ChatCenter.Instance.IsClientBan(chatType) ||
                IsInviteChat(content) || KillRegex.IsMatch(content))
            {
                return;
            }
            var channelName = string.Empty;
            switch (chatType)
            {
                case ChatInfoType.World:
                    channelName = Language.Get("ChatType_World");
                    break;
                case ChatInfoType.Area:
                    channelName = Language.Get("ChatType_Area");
                    break;
                case ChatInfoType.CrossServer:
                    channelName = Language.Get("ChatType_CrossServer");
                    break;
                case ChatInfoType.Team:
                    channelName = Language.Get("ChatType_Team");
                    break;
                case ChatInfoType.Invite:
                    channelName = Language.Get("ChatType_Invite");
                    break;
                case ChatInfoType.Trumpet:
                    channelName = Language.Get("ChatType_Trumpet");
                    break;
                case ChatInfoType.Fairy:
                    channelName = Language.Get("ChatType_Fairy");
                    break;
                case ChatInfoType.Friend:
                    channelName = Language.Get("PlayerDetail_PrivateChat");
                    break;
                case ChatInfoType.default1:
                    channelName = Language.Get("ChatType_default1");
                    break;
                default:
                    return;
            }
            // TODO YYL
            // OperationLogCollect.Instance.ChatReport(content, channelName, chatType == ChatInfoType.Friend ? toPlayer : string.Empty, chatType);
        }
        catch (Exception e)
        {
            Debug.Log(e.StackTrace + e.Message);
        }
    }
    public void SendChatInfo(ChatInfoType type, string msg, ChatExtraData? info = null , bool isDirtyWord = true,  bool isChatInfoCount = true)
    {
        bool isDirty = false;
        bool isVoice = ChatCenter.s_VoiceRegex.IsMatch(msg);
        if (CheckEmptyChat(msg))
        {
            SysNotifyMgr.Instance.ShowTip("CanootTalk14");
            return;
        }
        ChatReport(type, msg, PteChatName);
        if (!isVoice && !InviteRegex.IsMatch(msg))
        {
            if (isDirtyWord)
            {
                isDirty = DirtyWordConfig.IsDirtWord(msg);
                if (isDirty)
                    msg = DirtyWordConfig.IsDirtWord(msg, '*');
            }
            var length = GetChatMessageLength(msg);
            if (isChatInfoCount)
            {
                if (length > CHAT_INFO_CNT)
                {
                    ServerTipDetails.DisplayNormalTip(Language.Get("L1014"));
                    return;
                }
            }
            if (itemPlaceList.Count > 5)
            {
                SysNotifyMgr.Instance.ShowTip("ChatSendItemLimit");
                return;
            }
        }
        LanguageVerify.toPlayer = (uint)PteChatID;
        LanguageVerify.toPlayerName = PteChatName;
        LanguageVerify.Instance.VerifyChat(msg, type, (bool ok, string result) =>
         {
             if (ok)
             {
                 ChatCenter.RecentlyChat _recentlyChat = null;
                 if (!isDirty && !isVoice)
                 {
                     _recentlyChat = ChatCenter.Instance.SaveRecentlyChat(result);
                 }
                 msg = CheckHasItem(result, _recentlyChat);
                 ChatCenter.Instance.recentlyChat = null;
                 if (ChatCenter.Instance.IsChatBanned || ChatCenter.Instance.IsClientBan(type))
                 {
                     var toPlayer = PteChatID;
                     if (info.HasValue && info.Value.infoint1 == 0)
                     {
                         toPlayer = info.Value.infoint1;
                     }
                     ChatCenter.Instance.HandleChatBanned(type, msg, toPlayer);
                     return;
                 }
                 switch (type)
                 {
                     case ChatInfoType.World:
                     case ChatInfoType.Fairy:
                         if (IsAssitChat(msg) != 0)
                         {
                            //  TODO YYL
                            // teamModel.RequestAssistAutoMatch();
                         }
                         break;
                 }
                 switch (type)
                 {
                     case ChatInfoType.World:
                         {
                             C0201_tagCTalkGong chatPack = new C0201_tagCTalkGong();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Area:
                         {
                             C0207_tagCTalkArea chatPack = new C0207_tagCTalkArea();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.CrossServer:
                         {
                             C0208_tagCTalkCountry chatPack = new C0208_tagCTalkCountry();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Team:
                         {
                             C0205_tagCTalkDui chatPack = new C0205_tagCTalkDui();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Friend:
                         {
                             var _toPlayer = PteChatID;
                             if (info.HasValue && info.Value.infoint1 == 0)
                             {
                                 _toPlayer = info.Value.infoint1;
                             }
                             if (_toPlayer == 0)
                             {
                                 SysNotifyMgr.Instance.ShowTip("NoChatTarget");
                                 return;
                             }
                             SendFriendChat(msg, _toPlayer);
                         }
                         break;
                     case ChatInfoType.Fairy:
                         {
                             C0203_tagCTalkBang chatPack = new C0203_tagCTalkBang();
                             chatPack.Len = (ushort)GetUTF8InfoLen(msg);
                             chatPack.Content = msg;
                             GameNetSystem.Instance.SendInfo(chatPack);
                         }
                         break;
                     case ChatInfoType.Trumpet:
                         {
                             if (info.HasValue)
                             {
                                 CA217_tagCMPYSpeaker _pak = new CA217_tagCMPYSpeaker();
                                 _pak.SpeakerType = 1;
                                 _pak.IsUseGold = 0;
                                 _pak.ItemIndex = (byte)info.Value.infoint1;
                                 _pak.TextLen = (ushort)GetUTF8InfoLen(msg);
                                 _pak.Text = msg;
                                 GameNetSystem.Instance.SendInfo(_pak);
                             }
                         }
                         break;
                     case ChatInfoType.default1:
                         {
                             if (PlayerDatas.Instance.baseData.faction == 0)
                             {
                                 SysNotifyMgr.Instance.ShowTip("FactionChatLimit");
                                 return;
                             }
                             CA216_tagCMPyTalk _pak = new CA216_tagCMPyTalk();
                             _pak.TalkType = 100;
                             _pak.Len = (ushort)GetUTF8InfoLen(msg);
                             _pak.Content = msg;
                             GameNetSystem.Instance.SendInfo(_pak);
                         }
                         break;
                 }
             }
         });
    }
    int GetChatMessageLength(string message)
    {
        message = WordAnalysis.Color_Start_Regex.Replace(message, string.Empty);
        message = WordAnalysis.Color_End_Regex.Replace(message, string.Empty);
        return message.Length;
    }
    bool CheckEmptyChat(string msg)
    {
        if (string.IsNullOrEmpty(msg.Replace(" ", string.Empty)))
        {
            return true;
        }
        return false;
    }
    /// <summary>
    /// 世界频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0201_tagTalkGong vNetData, uint time = 0)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = null;
        var content = vNetData.Content;
        if (IsInviteChat(vNetData.Content))
        {
            content = InviteRegex.Replace(vNetData.Content, "");
            // if (teamModel.myTeam.GetIndexOfMember((int)vNetData.PlayerID) != -1)
            // {
            //     content = StringUtility.Contact("<color=#f8983b>", vNetData.Name, "</color>", ":", content);
            // }
            // else
            {
                content = StringUtility.Contact("<color=#f8983b>", vNetData.Name, "</color>", ":", content, string.Format("<color=#00ff00><a>{0}|invite={1}</a></color>", Language.Get("L1013"), vNetData.PlayerID));
            }
            chatData = new ChatInviteData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
            KeepChatInfo(chatData);
            return;
        }
        if (IsAssitChat(content, true) == 2 && vNetData.PlayerID != PlayerDatas.Instance.baseData.PlayerID)
        {
            content = StringUtility.Contact(content, Language.Get("InviteTeam", vNetData.PlayerID));
        }
        chatData = new ChatWorldData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        if (time != 0)
        {
            chatData.createTime = TimeUtility.GetTime(time);
        }
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 区域频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0207_tagTalkArea vNetData)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = new ChatAreaData(vNetData.Content, (int)vNetData.PlayerID, vNetData.SrcName, vNetData.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    // 阵营频道
    public void RevChatInfo(HA707_tagMCPyTalk vNetData)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(vNetData.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        ChatData chatData = new ChatFactionData(vNetData.Content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 跨服聊天
    /// </summary>
    /// <param name="package"></param>
    public void RevChatInfo(H0208_tagTalkCountry package)
    {
        //  TODO YYL
        // if (friendModel.GetFirendInfo(package.PlayerID, (byte)GroupType.Balcklist) != null)//黑名单拦截
        // {
        //     return;
        // }
        if (!FuncOpen.Instance.IsFuncOpen(162))
        {
            return;
        }
        ChatData chatData = new ChatCrossServerData(package.Content, (int)package.PlayerID, package.Name, package.Extras);
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 喇叭喊话
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(HA9A3_tagGCPYSpeakerContent vNetData)
    {
        // ChatData chatData = new ChatTrumpetData(vNetData.Text, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras, vNetData.SpeakerType, vNetData.AccID);
        // LocalChatHistory.Save(chatData as ChatUeseData);
        // KeepChatInfo(chatData);
        // ServerTipDetails.ShowTrumpetTip(chatData as ChatTrumpetData);
    }
    /// <summary>
    /// 家族频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0203_tagTalkBang vNetData, uint time = 0)
    {
        var content = vNetData.Content;
        if (IsAssitChat(content, true) == 1 && vNetData.PlayerID != PlayerDatas.Instance.baseData.PlayerID)
        {
            content = StringUtility.Contact(content, Language.Get("InviteTeam", vNetData.PlayerID));
        }
        ChatData chatData = new ChatFamilyData(content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        if (time != 0)
        {
            chatData.createTime = TimeUtility.GetTime(time);
        }
        LocalChatHistory.Save(chatData as ChatUeseData);
        KeepChatInfo(chatData);
        ReceiveNewChat(ChatInfoType.Fairy);
    }
    /// <summary>
    /// 后端缓存
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(HB311_tagGCTalkCache package)
    {
        for (int i = 0; i < package.Count; i++)
        {
            var data = package.InfoList[i];
            if (data.ChannelType == 1)
            {
                RevChatInfo(new H0201_tagTalkGong()
                {
                    socketType = package.socketType,
                    Content = data.Content,
                    Extras = data.Extras,
                    ExtraValue = 0,
                    Name = data.Name,
                    NameLen = data.NameLen,
                    Len = data.Len,
                    PlayerID = data.PlayerID,
                }, data.Time);
            }
            else if (data.ChannelType == 2)
            {
                RevChatInfo(new H0203_tagTalkBang()
                {
                    socketType = package.socketType,
                    Content = data.Content,
                    Extras = data.Extras,
                    ExtraValue = 0,
                    Len = data.Len,
                    Name = data.Name,
                    NameLen = data.NameLen,
                    PlayerID = data.PlayerID,
                });
            }
        }
    }
    /// <summary>
    /// 好友私聊
    /// </summary>
    /// <param name="vNetData"></param>
    public void SetChatFreind(ChatFriend inst)
    {
        m_ChatFriend = inst;
    }
    public void RevChatInfo(H0206_tagTalkMi vNetData)
    {
        if (Regex.IsMatch(vNetData.Content, KILL_IDENTIFY))
        {
            if (vNetData.PlayerID == PlayerDatas.Instance.baseData.PlayerID)
            {
                return;
            }
        }
        ChatFriendData chatData = new ChatFriendData(vNetData.Content, (int)vNetData.PlayerID, vNetData.SrcName, vNetData.Extras, vNetData.ToName, vNetData.TalkType, vNetData.ToPlayerID);
        FitterChat(chatData);
        AddPteChat(chatData, false);
        LocalChatHistory.Save(chatData as ChatUeseData);
        if (chatData.player == PlayerDatas.Instance.baseData.PlayerID)
        {
            if (OnRefreshSelf != null)
            {
                OnRefreshSelf(chatData);
            }
        }
        if (chatData.IsSound)
        {
            // ChatCenter.Instance.CheckAutoPlayVoice(chatData);
        }
    }
    private void AddPteChat(ChatFriendData chatData, bool isLocal)
    {
        List<ChatFriendData> list = null;
        pteChatDics.TryGetValue(chatData.player + chatData.toPlayer, out list);
        if (list != null)
        {
            if (list.Count > CHAT_INFO_CNT)
            {
                ChatUeseData outData = list[0];
                list.RemoveAt(0);
                outData = null;
            }
            list.Add(chatData);
        }
        else
        {
            list = new List<ChatFriendData>();
            list.Add(chatData);
            pteChatDics.Add(chatData.player + chatData.toPlayer, list);
        }
        if (OnRefreshPteChat != null && !isLocal)
        {
            OnRefreshPteChat(chatData);
        }
    }
    /// <summary>
    /// 队伍频道
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H0205_tagTalkDui vNetData)
    {
        ChatData chatData = new ChatTeamData(vNetData.Content, (int)vNetData.PlayerID, vNetData.Name, vNetData.Extras);
        KeepChatInfo(chatData);
        ReceiveNewChat(ChatInfoType.Team);
    }
    /// <summary>
    /// GM
    /// </summary>
    /// <param name="vNetData"></param>
    public void RevChatInfo(H3202_tagServerResponse vNetData)
    {
        ChatData chatData = new ChatSystemData(vNetData.Message);
        KeepChatInfo(chatData);
    }
    /// <summary>
    /// 系统提示
    /// </summary>
    /// <param name="msg"></param>
    public void RevChatInfo(string msg)
    {
        ChatData chatData = new ChatSystemData(msg);
        KeepChatInfo(chatData);
    }
    public void RevChatInfo(string msg, ArrayList infoList, ChatInfoType type = ChatInfoType.System)
    {
        ChatData chatData = null;
        switch (type)
        {
            case ChatInfoType.System:
                {
                    chatData = new ChatSystemData(msg);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.FairyTip:
            case ChatInfoType.FairyQuestion:
                {
                    chatData = new ChatFamilyData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.TeamTip:
                {
                    chatData = new ChatTeamData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
            case ChatInfoType.default1:
            case ChatInfoType.default2:
                {
                    chatData = new ChatFactionData(msg, 0, string.Empty, string.Empty, type);
                    chatData.infoList.AddRange(infoList);
                }
                break;
        }
        if (chatData != null)
        {
            KeepChatInfo(chatData);
        }
    }
    /// <summary>
    /// 获取频道聊天数据
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public List<ChatData> GetChatInfo(ChatInfoType type)
    {
        List<ChatData> list = null;
        chatDics.TryGetValue(type, out list);
        return list;
    }
    /// <summary>
    /// 获取好友聊天数据
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public List<ChatFriendData> GetChatInfo(int player)
    {
        List<ChatFriendData> list = null;
        pteChatDics.TryGetValue(player + (int)PlayerDatas.Instance.baseData.PlayerID, out list);
        return list;
    }
    /// <summary>
    /// 获取主界面的聊天数据
    /// </summary>
    /// <returns></returns>
    public List<ChatData> GetChatInfo()
    {
        return chatlist;
    }
    public List<ChatData> GetChatUpInfo()
    {
        return chatUpList;
    }
    public ChatData GetChatInfo(ChatInfoType type, int index)
    {
        List<ChatData> list = null;
        chatDics.TryGetValue(type, out list);
        if (list != null)
        {
            return list[index];
        }
        return null;
    }
    public void KeepLocalChat(ChatData chat)
    {
        if (chat.type == ChatInfoType.Friend)
        {
            FitterChat(chat);
            AddPteChat(chat as ChatFriendData, true);
        }
        else
        {
            KeepChatInfo(chat);
        }
    }
    private void KeepChatInfo(ChatData data)
    {
        KeepAllTypeChat(data);
        List<ChatData> list = null;
        chatDics.TryGetValue(data.type, out list);
        if (list != null)
        {
            if (list.Count >= CHAT_INFO_CNT)
            {
                ChatData outData = list[0];
                list.RemoveAt(0);
                outData = null;
            }
            list.Add(data);
        }
        else
        {
            list = new List<ChatData>();
            list.Add(data);
            chatDics.Add(data.type, list);
        }
        if (chatFloatUpdate != null)
        {
            chatFloatUpdate(data);
        }
        if (OnRefreshChat != null)
            OnRefreshChat(data.type);
        if (data.type != ChatInfoType.System && (data as ChatUeseData).player == PlayerDatas.Instance.baseData.PlayerID)
        {
            if (OnRefreshSelf != null)
                OnRefreshSelf(data);
        }
        // if ((data is ChatUeseData)
        //     && (data as ChatUeseData).IsSound)
        // {
        //     ChatCenter.Instance.CheckAutoPlayVoice(data);
        // }
    }
    private void KeepAllTypeChat(ChatData data)
    {
        if (!FitterChat(data))
        {
            if (chatlist.Count >= CHAT_TIP_CNT)
            {
                ChatData outData = chatlist[0];
                chatlist.RemoveAt(0);
            }
            if (chatUpList.Count >= CHAT_INFO_CNT)
            {
                ChatData outData = chatUpList[0];
                chatUpList.RemoveAt(0);
            }
            chatlist.Add(data);
            chatUpList.Add(data);
        }
    }
    private bool FitterChat(ChatData data)
    {
        if (data.type == ChatInfoType.System)
        {
            return true;
        }
        if (IsItemChat(data.content))
        {
            string content = data.content;
            MatchCollection matchArray = HrefAnalysis.EquipDetailRegex.Matches(content);
            int index = 0;
            for (int i = 0; i < matchArray.Count; i++)
            {
                data.richText.Append(content.Substring(index, matchArray[i].Index - index));
                index = matchArray[i].Index + matchArray[i].Length;
                string detail = matchArray[i].Groups[1].Value;
                var itemplusArray = detail.Split('|');
                if (itemplusArray.Length > 1)
                {
                    var itemId = int.Parse(itemplusArray[0]);
                    ItemConfig itemConfig = ItemConfig.Get(itemId);
                    if (itemConfig != null)
                    {
                        try
                        {
                            int[] equipGems = null;
                            if (itemplusArray.Length >= 8)
                            {
                                equipGems = new int[4];
                                for (int j = 0; j < equipGems.Length; j++)
                                {
                                    equipGems[j] = int.Parse(itemplusArray[4 + j]);
                                }
                            }
                            ItemTipUtility.CustomEquipWash equipWash = new ItemTipUtility.CustomEquipWash();
                            if (itemplusArray.Length >= 15)
                            {
                                equipWash.LV = int.Parse(itemplusArray[11]);
                                equipWash.Value = new int[3];
                                equipWash.Value[0] = int.Parse(itemplusArray[12]);
                                equipWash.Value[1] = int.Parse(itemplusArray[13]);
                                equipWash.Value[2] = int.Parse(itemplusArray[14]);
                            }
                            List<int> suitPlaces = null;
                            if (itemplusArray.Length > 15)
                            {
                                suitPlaces = new List<int>();
                                for (int j = 0; j < 8; j++)
                                {
                                    var place = int.Parse(itemplusArray[15 + j]);
                                    if (place != 0)
                                    {
                                        suitPlaces.Add(place);
                                    }
                                }
                            }
                            int[] suitLevels = null;
                            if (itemplusArray.Length > 15)
                            {
                                suitLevels = new int[3];
                                for (int j = 0; j < 3; j++)
                                {
                                    suitLevels[j] = int.Parse(itemplusArray[23 + j]);
                                }
                            }
                            int[] placeStars = null;
                            if (itemplusArray.Length > 15)
                            {
                                placeStars = new int[8];
                                for (int j = 0; j < 8; j++)
                                {
                                    placeStars[j] = int.Parse(itemplusArray[26 + j]);
                                }
                            }
    public int[] defaultChannelBulletColorArr;
    public Color32 defaultChannelBulletColor;
    public int characterLimit;
    public int sysBubbleID;
    public int[] sysBubbleColorArr;
    public Color32 sysBubbleColor;
                            ItemTipUtility.CustomItemPlus itemplus = new ItemTipUtility.CustomItemPlus()
    public override void Init()
                            {
                                ItemID = itemId,
                                count = int.Parse(itemplusArray[1]),
                                Equipped = int.Parse(itemplusArray[2]),
                                UserData = itemplusArray[3],
                                Stone = equipGems,
                                PlusLV = itemplusArray.Length >= 9 ? int.Parse(itemplusArray[8]) : 0,
                                Star = itemplusArray.Length >= 10 ? int.Parse(itemplusArray[9]) : 0,
                                EvolveLV = itemplusArray.Length >= 11 ? int.Parse(itemplusArray[10]) : 0,
                                Wash = equipWash,
                                Equips = null,
                                suitPlaces = suitPlaces == null ? null : suitPlaces.ToArray(),
                                suitLevels = suitLevels,
                                placeStars = placeStars,
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitializeEvent;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
        GuildManager.Instance.EnterOrQuitGuildEvent += EnterOrQuitGuildEvent;
        var config = FuncConfigConfig.Get("TalkCD");
        chatChannelCD = ConfigParse.ParseIntDict(config.Numerical1);
        config = FuncConfigConfig.Get("TalkColor");
        areaMyColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical1);
        areaMyColor = new Color32()
        {
            r = (byte)(areaMyColorArr.Length > 0 ? areaMyColorArr[0] : 0),
            g = (byte)(areaMyColorArr.Length > 1 ? areaMyColorArr[1] : 0),
            b = (byte)(areaMyColorArr.Length > 2 ? areaMyColorArr[2] : 0),
            a = (byte)(areaMyColorArr.Length > 3 ? areaMyColorArr[3] : 255),
                            };
                            string append = string.Format("<a><Word info=item id={0} itemplus={1} chatsend=1/>|showitem={0} itemplus={1}</a>",
                                      itemId, LitJson.JsonMapper.ToJson(itemplus));
                            append = UIHelper.AppendColor(itemConfig.ItemColor, append);
                            data.richText.Append(append);
                        }
                        catch (Exception e)
        areaOtherColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical2);
        areaOtherColor = new Color32()
                        {
                            data.richText.Append(itemConfig.ItemName);
                            Debug.Log(e.Message);
            r = (byte)(areaOtherColorArr.Length > 0 ? areaOtherColorArr[0] : 0),
            g = (byte)(areaOtherColorArr.Length > 1 ? areaOtherColorArr[1] : 0),
            b = (byte)(areaOtherColorArr.Length > 2 ? areaOtherColorArr[2] : 0),
            a = (byte)(areaOtherColorArr.Length > 3 ? areaOtherColorArr[3] : 255),
        };
        chatChannelBulletColorArrDict = ConfigParse.ParseIntArrayDict(config.Numerical3);
        foreach (var kv in chatChannelBulletColorArrDict)
        {
            if (!IsValidChatChannel(kv.Key))
                continue;
            chatChannelBulletColorDict[(ChatChannel)kv.Key] = new Color32()
            {
                r = (byte)(kv.Value.Length > 0 ? kv.Value[0] : 0),
                g = (byte)(kv.Value.Length > 1 ? kv.Value[1] : 0),
                b = (byte)(kv.Value.Length > 2 ? kv.Value[2] : 0),
                a = (byte)(kv.Value.Length > 3 ? kv.Value[3] : 0),
            };
        }
        defaultChannelBulletColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical4);
        defaultChannelBulletColor = new Color32()
        {
            r = (byte)(defaultChannelBulletColorArr.Length > 0 ? defaultChannelBulletColorArr[0] : 0),
            g = (byte)(defaultChannelBulletColorArr.Length > 1 ? defaultChannelBulletColorArr[1] : 0),
            b = (byte)(defaultChannelBulletColorArr.Length > 2 ? defaultChannelBulletColorArr[2] : 0),
            a = (byte)(defaultChannelBulletColorArr.Length > 3 ? defaultChannelBulletColorArr[3] : 255),
        };
        config = FuncConfigConfig.Get("TalkLimit");
        characterLimit = int.Parse(config.Numerical1);
        config = FuncConfigConfig.Get("TalkBubble");
        sysBubbleID = int.Parse(config.Numerical1);
        sysBubbleColorArr = ConfigParse.GetMultipleStr<int>(config.Numerical2);
        sysBubbleColor = new Color32()
        {
            r = (byte)(sysBubbleColorArr.Length > 0 ? sysBubbleColorArr[0] : 0),
            g = (byte)(sysBubbleColorArr.Length > 1 ? sysBubbleColorArr[1] : 0),
            b = (byte)(sysBubbleColorArr.Length > 2 ? sysBubbleColorArr[2] : 0),
            a = (byte)(sysBubbleColorArr.Length > 3 ? sysBubbleColorArr[3] : 255),
        };
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= OnBeforePlayerDataInitializeEvent;
        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
        GuildManager.Instance.EnterOrQuitGuildEvent -= EnterOrQuitGuildEvent;
    }
    //被踢出/退出工会时,切换聊天频道
    private void EnterOrQuitGuildEvent(bool obj)
    {
        if (!obj)
        {
            nowChatChannel = ChatChannel.World;
            nowChatTab = ChatTab.World;
                        }
                    }
    private void OnBeforePlayerDataInitializeEvent()
    {
        talkDict.Clear();
        ParseChatBubbleConfig();
        nowChatChannel = ChatChannel.World;
        nowChatTab = ChatTab.World;
    }
    private void OnPlayerLoginOk()
    {
        LoadBulletSettings();
    }
    public void AddChatChannelSendTime(ChatChannel chatChannel, int time)
    {
        chatChannelSendTime[(int)chatChannel] = time;
    }
    public bool TryGetChatChannelSendTime(ChatChannel chatChannel, out int time)
    {
        return chatChannelSendTime.TryGetValue((int)chatChannel, out time);
    }
    public bool TryGetChatChannelSendCD(ChatChannel chatChannel, out int cd)
    {
        return chatChannelCD.TryGetValue((int)chatChannel, out cd);
    }
    public bool IsCanSend(ChatChannel chatChannel, out int remainingSeconds)
    {
        remainingSeconds = 0;
        // 没有配置的不限制
        if (!TryGetChatChannelSendCD(chatChannel, out int cd))
            return true;
        // 没有发送过
        if (!TryGetChatChannelSendTime(chatChannel, out int time) || time <= 0)
            return true;
        DateTime endDateTime = TimeUtility.GetTime((uint)(cd + time + 1));
        TimeSpan remainingTime = endDateTime - TimeUtility.ServerNow;
        remainingSeconds = (int)remainingTime.TotalSeconds;
        if (remainingSeconds <= 0)
            return true;
        return false;
    }
    // 0-系统 1-日期 2-自己 3-其他玩家
    public int GetTalkDataType(TalkData talkData)
    {
        if (talkData.isSystem)
        {
            return 0;
        }
        else if (talkData.isDate)
        {
            return 1;
        }
        else if (talkData.PlayerID == PlayerDatas.Instance.PlayerId)
        {
            return 2;
                }
                else
                {
                    data.richText.Append(matchArray[i].Value);
                }
            }
            data.richText.Append(content.Substring(index, content.Length - index));
        }
        if (data.type == ChatInfoType.Friend)
        {
            return true;
        }
        if (data.content.Equals(string.Empty))
        {
            return true;
        }
        return !chatOpenDics[data.type];
    }
    public Dictionary<ChatInfoType, bool> GetChatOpen()
    {
        return chatOpenDics;
    }
    #region 组队邀请
    private const string INVITE_IDENTIFY = "<i>";
    public static Regex InviteRegex = new Regex(@INVITE_IDENTIFY, RegexOptions.Singleline);
    public void SendInvite(string msg)
    {
        SendChatInfo(ChatInfoType.World, StringUtility.Contact(msg, INVITE_IDENTIFY));
    }
    public bool IsInviteChat(string msg)
    {
        return InviteRegex.IsMatch(msg);
    }
    #endregion
    #region 发送物品
    public List<ItemModel> itemPlaceList = new List<ItemModel>();
    public bool IsItemChat(string msg)
    {
        return HrefAnalysis.EquipDetailRegex.IsMatch(msg);
    }
    public string CheckHasItem(string msg, ChatCenter.RecentlyChat _recently)
    {
        return string.Empty;
        // if (!HrefAnalysis.EquipRegex.IsMatch(msg))
        // {
        //     return msg;
        // }
        // sb.Length = 0;
        // MatchCollection matchArray = HrefAnalysis.EquipRegex.Matches(msg);
        // int index = 0;
        // for (int i = 0; i < matchArray.Count; i++)
        // {
        //     sb.Append(msg.Substring(index, matchArray[i].Index - index));
        //     if (ChatCenter.Instance.recentlyChat != null)
        //     {
        //         if (i < ChatCenter.Instance.recentlyChat.itemIndexs.Count)
        //         {
        //             var _index = ChatCenter.Instance.recentlyChat.itemIndexs[i];
        //             sb.Append(" ");
        //             sb.Append(ChatCenter.Instance.recentlyChat.itemInfos[_index]);
        //             sb.Append(" ");
        //         }
        //         index = matchArray[i].Index + matchArray[i].Length;
        //         continue;
        //     }
        //     var _length = sb.Length;
        //     if (i < itemPlaceList.Count)
        //     {
        //         var itemConfig = ItemConfig.Get((int)itemPlaceList[i].itemId);
        //         if (itemConfig.ItemName == matchArray[i].Groups[1].Value)
        //         {
        //             bool equip = itemPlaceList[i].packType == PackType.Equip;
        //             sb.Append("#item#");
        //             AppendValue(sb, itemPlaceList[i].itemId);
        //             AppendValue(sb, itemPlaceList[i].count);
        //             AppendValue(sb, equip ? 1 : 0);
        //             AppendValue(sb, itemPlaceList[i].itemInfo.userData);
        //             if (equip)
        //             {
        //                 var position = new Int2(itemConfig.LV, itemConfig.EquipPlace);
        //                 int[] equipGems;
        //                 equipGemModel.TryGetEquipGems(itemPlaceList[i].gridIndex, out equipGems);
        //                 for (int j = 0; j < EquipGemModel.EQUIPGEM_HOLE_COUNT; j++)
        //                 {
        //                     AppendValue(sb, equipGems != null && j < equipGems.Length ? equipGems[j] : 0);
        //                 }
        //                 var strengthLevel = equipStrengthModel.GetStrengthLevel(itemConfig.LV, itemConfig.EquipPlace);
        //                 AppendValue(sb, strengthLevel);
        //                 var starLevel = equipStarModel.GetEquipStarLevel(new Int2(itemConfig.LV, itemConfig.EquipPlace));
        //                 AppendValue(sb, starLevel);
        //                 var evolveLevel = equipStrengthModel.GetStrengthEvolveLevel(position.x, position.y);
        //                 AppendValue(sb, evolveLevel);
        //                 AppendValue(sb, equipTrainModel.GetTrainLevel(position));
        //                 var property = equipTrainModel.GetTrainedProperties(position);
        //                 AppendValue(sb, property.x);
        //                 AppendValue(sb, property.y);
        //                 AppendValue(sb, property.z);
        //                 for (int place = 1; place <= 12; place++)
        //                 {
        //                     if (place > 8)
        //                     {
        //                         continue;
        //                     }
        //                     var equipGuid = equipModel.GetEquip(new Int2(position.x, place));
        //                     if (!string.IsNullOrEmpty(equipGuid))
        //                     {
        //                         var equipItem = packManager.GetItemByGuid(equipGuid);
        //                         if (equipItem.config.SuiteiD > 0)
        //                         {
        //                             AppendValue(sb, place);
        //                             continue;
        //                         }
        //                     }
        //                     AppendValue(sb, 0);
        //                 }
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.TwoSuit));
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.FiveSuit));
        //                 AppendValue(sb, equipModel.GetSuitLevel(itemConfig.LV, EquipSuitType.EightSuit));
        //                 for (int place = 1; place <= 8; place++)
        //                 {
        //                     var equipGuid = equipModel.GetEquip(new Int2(position.x, place));
        //                     if (!string.IsNullOrEmpty(equipGuid))
        //                     {
        //                         var equipItem = packManager.GetItemByGuid(equipGuid);
        //                         if (ItemLogicUtility.Instance.IsSuitEquip(equipItem.itemId))
        //                         {
        //                             AppendValue(sb, equipStarModel.GetStarLevel(new Int2(position.x, place)));
        //                         }
        //                         else
        //                         {
        //                             AppendValue(sb, -1);
        //                         }
        //                     }
        //                     else
        //                     {
        //                         AppendValue(sb, -1);
        //                     }
        //                 }
        //             }
        //             sb.Remove(sb.Length - 1, 1);
        //             sb.Append("#item#");
        //             if (_recently != null)
        //             {
        //                 _recently.Add(itemConfig.ItemName, sb.ToString().Substring(_length));
        //             }
        //         }
        //         else
        //         {
        //             sb.Append(matchArray[i].Value);
        //         }
        //     }
        //     else
        //     {
        //         sb.Append(matchArray[i].Value);
        //     }
        //     index = matchArray[i].Index + matchArray[i].Length;
        // }
        // sb.Append(msg.Substring(index, msg.Length - index));
        // return sb.ToString();
    }
    void AppendValue(StringBuilder sb, object _object)
    {
        sb.Append(_object);
        sb.Append('|');
    }
    #endregion
    #region 好友私聊
    public static Regex KillRegex = new Regex(@KILL_IDENTIFY, RegexOptions.Singleline);
    public const string KILL_IDENTIFY = "<k>";
    public void SendFriendChat(string msg, int player)
    {
        C0209_tagCTalkMiFix chatPack = new C0209_tagCTalkMiFix();
        chatPack.TalkType = 1;
        chatPack.PlayerID = (uint)player;
        chatPack.Len = (ushort)GetUTF8InfoLen(msg);
        chatPack.Content = msg;
        GameNetSystem.Instance.SendInfo(chatPack);
    }
    #endregion
    /// <summary>
    /// 清屏
    /// </summary>
    public void ClearAllChatInfo()
    {
        chatDics.Clear();
        chatlist.Clear();
        chatUpList.Clear();
        pteChatDics.Clear();
        if (OnRefreshChat != null)
        {
            OnRefreshChat(presentChatType);
            return 3;
        }
    }
    public void ClearChatInfo(ChatInfoType type)
    {
        List<ChatData> list = null;
        if (chatDics.TryGetValue(type, out list))
        {
            list.Clear();
            if (OnRefreshChat != null)
            {
                OnRefreshChat(type);
            }
        }
    }
    public static int GetUTF8InfoLen(string msg)
    {
        return Encoding.UTF8.GetBytes(msg).Length;
    }
    public void CloseChatBtnClick()
    public bool TryGetBubble(int id, out ChatBubbleData bubble)
    {
        if (OnClickCloseChatEvent != null)
        {
            OnClickCloseChatEvent();
        }
        return chatBubbles.TryGetValue(id, out bubble);
    }
    public bool IsExtentOpen { get; set; }
    public void OpenChatExtent(bool open)
    void ParseChatBubbleConfig()
    {
        if (OnChatExtentOpenEvent != null)
        var configs = ChatBubbleBoxConfig.GetValues();
        for (int i = 0; i < configs.Count; i++)
        {
            OnChatExtentOpenEvent(open);
            var config = configs[i];
            if (chatBubbles.ContainsKey(config.ID))
            {
                continue;
        }
            var bubble = new ChatBubbleData();
            bubble.id = config.ID;
            bubble.leftPadding = new RectOffset()
            {
                left = config.LeftOffset.Length > 0 ? config.LeftOffset[0] : 0,
                right = config.LeftOffset.Length > 1 ? config.LeftOffset[1] : 0,
                top = config.LeftOffset.Length > 2 ? config.LeftOffset[2] : 0,
                bottom = config.LeftOffset.Length > 3 ? config.LeftOffset[3] : 0,
            };
            bubble.rifhtPadding = new RectOffset()
            {
                left = config.RightOffset.Length > 0 ? config.RightOffset[0] : 0,
                right = config.RightOffset.Length > 1 ? config.RightOffset[1] : 0,
                top = config.RightOffset.Length > 2 ? config.RightOffset[2] : 0,
                bottom = config.RightOffset.Length > 3 ? config.RightOffset[3] : 0,
            };
            bubble.myColor = new Color32()
            {
                r = (byte)(config.MyColor.Length > 0 ? config.MyColor[0] : 0),
                g = (byte)(config.MyColor.Length > 1 ? config.MyColor[1] : 0),
                b = (byte)(config.MyColor.Length > 2 ? config.MyColor[2] : 0),
                a = (byte)(config.MyColor.Length > 3 ? config.MyColor[3] : 255),
            };
            bubble.otherColor = new Color32()
            {
                r = (byte)(config.OtherColor.Length > 0 ? config.OtherColor[0] : 0),
                g = (byte)(config.OtherColor.Length > 1 ? config.OtherColor[1] : 0),
                b = (byte)(config.OtherColor.Length > 2 ? config.OtherColor[2] : 0),
                a = (byte)(config.OtherColor.Length > 3 ? config.OtherColor[3] : 255),
            };
            bubble.top = config.Top;
            chatBubbles.Add(config.ID, bubble);
        }
    }
    public bool SatisfyNameLength(string name, out int error)
    {
        error = 0;
        int length = Encoding.Default.GetBytes(name).Length;
        int maxlength = characterLimit;  //纯中文字数
        //var minlength = 3;
        if (length > maxlength)
        {
            error = 1;
        }
        // else if (length < minlength)
        // {
        //     error = 2;
        // }
        return error == 0;
    }
    public bool CheckChatLimit(string info, out int errorCode)
    {
        errorCode = 0;
        if (string.IsNullOrEmpty(info))
        {
            errorCode = 0;
            return false;
    }
    #region 陌生人聊天
    public event Action OpenPteChatEvent;
    public void OpenFriendWin()
        if (!SatisfyNameLength(info, out errorCode))
    {
        if (OpenPteChatEvent != null)
        {
            OpenPteChatEvent();
        }
    }
    #endregion
    #region 主界面聊天频道显示设置
    public void SetChatChannelShow(ChatInfoType type, bool open)
    {
        if (chatOpenDics.ContainsKey(type))
        {
            chatOpenDics[type] = open;
        }
            return false;
    }
    public void GetChatChannelShow()
        if (DirtyWordConfig.IsDirtWord(info) || UIHelper.HasSpecialCharac(info)
            || DirtyNameConfig.IsDirtName(info))
    {
        chatOpenDics[ChatInfoType.World] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelWorld);
        chatOpenDics[ChatInfoType.Area] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelArea);
        chatOpenDics[ChatInfoType.Fairy] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelGrad);
        chatOpenDics[ChatInfoType.Invite] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelATeam);
        chatOpenDics[ChatInfoType.System] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelSystem);
        chatOpenDics[ChatInfoType.Team] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelTeam);
        chatOpenDics[ChatInfoType.Trumpet] = ChatSetting.Instance.GetBool(ChatBoolType.ChannelBugle);
        chatOpenDics[ChatInfoType.CrossServer] = true;
        chatOpenDics[ChatInfoType.default1] = true;
            errorCode = 3;
            return false;
    }
    #endregion
    #region 日常跳转
    private string[] realmRandomChats = new string[2] { "DailyQuestRealmTalk1", "DailyQuestRealmTalk2" };
    private string[] dungeonRandomChats = new string[2] { "DailyQuestAssitmTalk1", "DailyQuestAssitmTalk2" };
    public bool openFromDaily { get; set; }
    public string GetAssitRandomChat(ChatInfoType _type)
    {
        int _index = UnityEngine.Random.Range(0, 2);
        switch (_type)
        {
            case ChatInfoType.World:
                if (_index == 0)
                {
                    return Language.Get(dungeonRandomChats[0]);
                }
                else
                {
                    return Language.Get(dungeonRandomChats[1], PlayerDatas.Instance.baseData.FightPower);
                }
            case ChatInfoType.Fairy:
                return Language.Get(realmRandomChats[_index]);
        }
        return string.Empty;
        return true;
    }
    public bool openFromFairyTask { get; set; }
    Dictionary<int, List<string>> m_TaskRandomChats = new Dictionary<int, List<string>>();
    public string GetTaskRandomChat(ChatInfoType _type)
    public void ShowChatErrorTip(int _errorCode)
    {
        if (m_TaskRandomChats.ContainsKey((int)_type))
        switch (_errorCode)
        {
            var list = m_TaskRandomChats[(int)_type];
            var index = UnityEngine.Random.Range(0, list.Count);
            return Language.Get(list[index]);
        }
        return string.Empty;
    }
    public bool needCheckAssitChat { get; set; }
    public int IsAssitChat(string message, bool force = false)
    {
        int assitChat = 0;
        if (needCheckAssitChat || force)
        {
            for (int i = 0; i < 2; i++)
            {
                if (message.Equals(Language.Get(realmRandomChats[i])))
                {
                    assitChat = 1;
            case 0:
                //空
                SysNotifyMgr.Instance.ShowTip("ChatInfoNoNull");
                    break;
                }
                if (message.Equals(Language.Get(dungeonRandomChats[i])))
                {
                    assitChat = 2;
            case 1:
                // 长度过长
                SysNotifyMgr.Instance.ShowTip("NameError2", 7);
                break;
            case 3:
                // 脏字
                SysNotifyMgr.Instance.ShowTip("NameSensitive");
                    break;
                }
            }
    public int GetJumpIndex(ChatChannel type)
    {
        if (!TryGetTalkData(type, out List<TalkData> datas))
            return 0;
        return Mathf.Max(datas.Count - 1, 0);
        }
        return assitChat;
    public bool IsValidChatChannel(int channelType)
    {
        return Enum.IsDefined(typeof(ChatChannel), channelType);
    }
    public bool TryGetChatData(ChatChannel type, int index, out TalkData data)
    {
        data = null;
        if (!TryGetTalkData(type, out List<TalkData> datas) || datas == null)
            return false;
        if (index < 0 || index >= datas.Count)
            return false;
        data = datas[index];
        return true;
    }
    public bool TryGetTalkData(ChatChannel type, out List<TalkData> datas)
    {
        return talkDict.TryGetValue(type, out datas);
    }
    public void SendChatInfo(ChatChannel type, string content)
    {
        SendChatPack((int)type, content);
    }
    public void SendChatPack(int channelType, string content)
    {
        CB320_tagCSTalk pack = new CB320_tagCSTalk();
        pack.ChannelType = (byte)channelType;
        pack.Content = content;
        pack.Len = (ushort)GetUTF8InfoLen(content);
        GameNetSystem.Instance.SendInfo(pack);
    }
    public readonly int maxTalkCount = 1000;  //聊天数量上限
    public readonly int deleteTalkCount = 300;  //聊天数量上限时删除前多少条
    void TryDeleteTalkData(ChatChannel type)
    {
        if (!TryGetTalkData(type, out List<TalkData> datas))
            return;
        if (datas.Count < maxTalkCount)
            return;
        datas.RemoveRange(0, deleteTalkCount);
    }
    public int currentDay = -1;
    public void AddTalkData(ChatChannel type, TalkData data)
    {
        //如果超过限制先删除旧数据
        TryDeleteTalkData(type);
        if (!TryGetTalkData(type, out List<TalkData> datas))
        {
            talkDict[type] = new List<TalkData>();
        }
        talkDict[type].Add(data);
        OnUpdateTalkEvent?.Invoke(type, data);
    }
    public bool TryAddDate(int allSeconds, ChatChannel type)
    {
        DateTime talkTime = TimeUtility.GetTime((uint)allSeconds);
        if (talkTime.Day != currentDay)
        {
            currentDay = talkTime.Day;
            AddTalkData(type, new TalkData()
            {
                ChannelType = (byte)type,
                isDate = true,
                Content = Language.Get("Chat09", talkTime.Month, talkTime.Day),
                TalkTime = (uint)allSeconds,
            });
            return true;
        }
        return false;
    }
    public void AddSysData(string msg, ArrayList infoList, ChatChannel type)
    {
        int allSeconds = TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type);
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        AddTalkData(type, new TalkData()
        {
            ChannelType = (byte)type,
            isSystem = true,
            Content = msg,
            BubbleBox = 1,
            TalkTime = (uint)allSeconds,
            InfoList = new ArrayList(infoList),
        });
    }
    public void UpdateTalk(HB310_tagMCTalk vNetData)
    {
        if (!IsValidChatChannel(vNetData.ChannelType))
            return;
        ChatChannel type = (ChatChannel)vNetData.ChannelType;
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        int allSeconds = TimeUtility.AllSeconds;
        // 如果隔天,增加日期行
        TryAddDate(allSeconds, type);
        TalkData talkData = new TalkData()
        {
            ChannelType = vNetData.ChannelType,
            Name = UIHelper.ServerStringTrim(vNetData.Name),
            PlayerID = vNetData.PlayerID,
            Content = UIHelper.ServerStringTrim(vNetData.Content),
            BubbleBox = vNetData.BubbleBox,
            LV = vNetData.LV,
            RealmLV = vNetData.RealmLV,
            TitleID = vNetData.TitleID,
            Job = vNetData.Job,
            Face = vNetData.Face,
            FacePic = vNetData.FacePic,
            ServerID = vNetData.ServerID,
            TalkTime = (uint)allSeconds,
        };
        AddTalkData(type, talkData);
    }
    public void UpdateTalkCacheList(HB311_tagMCTalkCacheList vNetData)
    {
        if (!IsValidChatChannel(vNetData.ChannelType))
            return;
        ChatChannel type = (ChatChannel)vNetData.ChannelType;
        if (!talkDict.ContainsKey(type))
        {
            talkDict[type] = new List<TalkData>();
        }
        if (vNetData.InfoList.IsNullOrEmpty())
            return;
        foreach (var info in vNetData.InfoList)
        {
            // 如果隔天,增加日期行
            TryAddDate((int)info.TalkTime, type);
            AddTalkData(type, new TalkData()
            {
                ChannelType = vNetData.ChannelType,
                Name = info.Name,
                PlayerID = info.PlayerID,
                Content = info.Content,
                BubbleBox = info.BubbleBox,
                LV = info.LV,
                Job = info.Job,
                RealmLV = info.RealmLV,
                TitleID = info.TitleID,
                Face = info.Face,
                FacePic = info.FacePic,
                ServerID = info.ServerID,
                TalkTime = info.TalkTime,
            });
        }
        OnUpdateTalkCacheListEvent?.Invoke();
    }
    #region 标签页
    // 当前展示的频道入口
    private ChatTab m_NowChatTab;
    public ChatTab nowChatTab
    {
        get { return m_NowChatTab; }
        set
        {
            if (m_NowChatTab == value)
                return;
            m_NowChatTab = value;
            OnChatTabChangeEvent?.Invoke(value);
        }
    }
    public event Action<ChatTab> OnChatTabChangeEvent;
    // 频道入口的展示顺序
    public readonly List<ChatTab> tabShowList = new List<ChatTab>()
    {
        ChatTab.World,
        ChatTab.Guild,
        // ChatTab.Person,
        // ChatTab.BlackList,
    };
    public bool IsTabOpen(ChatTab chatTab, bool isTip = false)
    {
        if (!tabShowList.Contains(chatTab))
            return false;
        switch (chatTab)
        {
            case ChatTab.World:
                return true;
            case ChatTab.Guild:
                //没有公会
                if (!PlayerDatas.Instance.fairyData.HasFairy)
                {
                    if (isTip)
                        SysNotifyMgr.Instance.ShowTip("NoGuild");
                    return false;
                }
                return true;
            default:
                return false;
        }
    }
    public bool IsSelectChatTab(ChatTab chatTab)
    {
        return nowChatTab == chatTab;
    }
    public bool IsValidChatTab(int chatTab)
    {
        return Enum.IsDefined(typeof(ChatTab), chatTab);
    }
    public string GetChatTabName(ChatTab chatTab)
    {
        return Language.Get(StringUtility.Contact("ChatTab", (int)chatTab));
    }
    public string GetChatTabSelectIcon(ChatTab chatTab, bool isSelect)
    {
        return StringUtility.Contact(isSelect ? "ChatTabSelect" : "ChatTabUnSelect", (int)chatTab);
    }
    #endregion
    #region 弹幕设置
    private Dictionary<ChatChannel, bool> bulletSettingDict = new Dictionary<ChatChannel, bool>();
    private string settingsKey { get { return StringUtility.Contact("BulletChatSettings_", PlayerDatas.Instance.PlayerId); } }
    // 设置特定频道的弹幕开关状态
    public void SetBulletSetting(ChatChannel channelType, bool isEnabled)
    {
        bulletSettingDict[channelType] = isEnabled;
        SaveBulletSettings();
    }
    // 获取特定频道的弹幕开关状态
    public bool GetBulletSetting(ChatChannel channelType)
    {
        if (bulletSettingDict.TryGetValue(channelType, out bool value))
        {
            return value;
        }
        return true;
    }
    // 保存弹幕设置到本地
    private void SaveBulletSettings()
    {
        Dictionary<int, int> saveDict = new Dictionary<int, int>();
        foreach (var kvp in bulletSettingDict)
        {
            saveDict[(int)kvp.Key] = kvp.Value ? 1 : 0;
        }
        string jsonStr = JsonMapper.ToJson(saveDict);
        LocalSave.SetString(settingsKey, jsonStr);
    }
    // 从本地读取弹幕设置
    private void LoadBulletSettings()
    {
        bulletSettingDict.Clear();
        // 使用 LocalSave 读取 JSON 字符串
        string jsonStr = LocalSave.GetString(settingsKey);
        if (string.IsNullOrEmpty(jsonStr) || jsonStr == "{}")
        {
            // 如果没有保存的数据,设置默认值
            InitializeDefaultBulletSettings();
            return;
        }
        Dictionary<int, int> loadDict = ConfigParse.ParseIntDict(jsonStr);
        foreach (var kvp in loadDict)
        {
            if (Enum.IsDefined(typeof(ChatChannel), kvp.Key))
            {
                bulletSettingDict[(ChatChannel)kvp.Key] = kvp.Value == 1;
            }
        }
    }
    // 初始化默认弹幕设置 默认所有频道开启
    private void InitializeDefaultBulletSettings()
    {
        foreach (ChatChannel channelType in Enum.GetValues(typeof(ChatChannel)))
        {
            bulletSettingDict[channelType] = true;
        }
        SaveBulletSettings();
    }
    #endregion
    #region 宝石炫耀跳转
    public bool openFromGem { get; set; }
    public int flauntGemId { get; set; }
    public bool flauntGemBind { get; set; }
    public string GetGemFlauntChat()
    {
        var config = ItemConfig.Get(flauntGemId);
        if (config != null)
        {
            var itemInfo = new ItemInfo();
            itemInfo.itemId = flauntGemId;
            var item = new ItemModel(PackType.Item, itemInfo);
            var tip = string.Format("[{0}]", config.ItemName);
            itemPlaceList.Add(item);
            return Language.Get("GemLookTalk", tip);
        }
        return string.Empty;
    }
    #endregion
    #region 仙缘红点
    Dictionary<ChatInfoType, Redpoint> chatSocialRedpoints = new Dictionary<ChatInfoType, Redpoint>();
    Dictionary<ChatInfoType, int> unReadChatCounts = new Dictionary<ChatInfoType, int>();
    public void InitChatRedpoints()
    {
        chatSocialRedpoints.Add(ChatInfoType.Fairy, new Redpoint(MainRedDot.RedPoint_FriendChatKey, 2502));
        chatSocialRedpoints.Add(ChatInfoType.Team, new Redpoint(MainRedDot.RedPoint_FriendChatKey, 2503));
        unReadChatCounts.Add(ChatInfoType.Fairy, 0);
        unReadChatCounts.Add(ChatInfoType.Team, 0);
    }
    public void ViewChat(ChatInfoType type)
public enum ChatTab
    {
        if (unReadChatCounts.ContainsKey(type))
        {
            unReadChatCounts[type] = 0;
            UpdateRedpoint(type);
    World = 0,      //世界
    Guild = 1,      //公会
    Person = 2,     //私聊
    BlackList = 3,  //黑名单
        }
public enum ChatChannel
{
    World = 0,          //世界
    CrossServer = 1,    //跨服
    Area = 2,           // 区域
    Guild = 3,          // 公会
    Friend = 4,             // 好友
    Team = 5,               //队伍
    Person = 6,             //私聊
}
public class ChatBubbleData
{
    public int id;
    public RectOffset leftPadding;
    public RectOffset rifhtPadding;
    public Color32 myColor;
    public Color32 otherColor;
    public int top;
    }
    void ReceiveNewChat(ChatInfoType type)
public class TalkData
    {
        // TODO YYL
        // switch (type)
        // {
        //     case ChatInfoType.Team:
        //         if (!UIManager.Instance.IsOpened<TeamChatWin>()
        //             && (!UIManager.Instance.IsOpened<ChatWin>() || presentChatType != ChatInfoType.Team))
        //         {
        //             unReadChatCounts[ChatInfoType.Team] = Mathf.Min(unReadChatCounts[ChatInfoType.Team] + 1, 99);
        //         }
        //         break;
        //     case ChatInfoType.Fairy:
        //         if (!UIManager.Instance.IsOpened<FairyChatWin>()
        //             && (!UIManager.Instance.IsOpened<ChatWin>() || presentChatType != ChatInfoType.Fairy))
        //         {
        //             unReadChatCounts[ChatInfoType.Fairy] = Mathf.Min(unReadChatCounts[ChatInfoType.Fairy] + 1, 99);
        //         }
        //         break;
        // }
        UpdateRedpoint(type);
    }
    public void UpdateRedpoint(ChatInfoType type)
    {
        if (chatSocialRedpoints.ContainsKey(type))
        {
            var redpoint = chatSocialRedpoints[type];
            if (unReadChatCounts[type] > 0)
            {
                redpoint.state = RedPointState.Quantity;
                redpoint.count = unReadChatCounts[type];
            }
            else
            {
                redpoint.state = RedPointState.None;
            }
        }
        var socialRed = MainRedDot.Instance.redPointFriendChat;
        if (chatSocialRedpoints[ChatInfoType.Fairy].state == RedPointState.Quantity
            || chatSocialRedpoints[ChatInfoType.Team].state == RedPointState.Quantity)
        {
            socialRed.count = unReadChatCounts[ChatInfoType.Fairy] > 0 ?
                unReadChatCounts[ChatInfoType.Fairy] : unReadChatCounts[ChatInfoType.Team];
        }
        else
        {
            socialRed.count = 0;
        }
    }
    public RedPointState GetSocialChatRedpoint(ChatInfoType type)
    {
        if (chatSocialRedpoints.ContainsKey(type))
        {
            return chatSocialRedpoints[type].state;
        }
        return RedPointState.None;
    }
    #endregion
    #region 协助感谢
    public void SendThank2AssistPlayer(int playerId)
    {
        // TODO YYL
        // if (PlayerDatas.Instance.baseData.LV >= assistThankLevelLimit.x)
        // {
        //     return;
        // }
        // var assistPlayerInfo = dungeonAssistModel.GetAssistPlayerInfo(playerId);
        // if (assistPlayerInfo != null)
        // {
        //     if (assistPlayerInfo.LV >= assistThankLevelLimit.y)
        //     {
        //         return;
        //     }
        // }
        // var languageKeyIndex = UnityEngine.Random.Range(0, assistThankLanguages.Count);
        // if (assistThankLanguages.Count > 0)
        // {
        //     SendFriendChat(Language.Get(assistThankLanguages[languageKeyIndex]), playerId);
        // }
    }
    #endregion
}
public struct ChatExtraData
{
    public int infoint1;
    public ChatExtraData(int info1)
    {
        this.infoint1 = info1;
    }
    public static ChatExtraData Default {
        get {
            return new ChatExtraData(0);
        }
    }
}
public enum ChatInfoType
{
    System,//系统消息
    World, //世界频道
    Area,  //区域频道
    Team,  //队伍
    Invite,//组队
    Trumpet,//喇叭
    Fairy,//仙盟
    Friend,//私聊
    CrossServer,//跨服
    FairyQuestion,
    FairyTip,
    TeamTip,
    //后续IL开发添加预设
    default1,   //阵营(在跨服地图往跨服发)
    default2,   //阵营信息提示
    default3,
    default4,
    default5,
    default6,
    default7,
    default8,
    default9,
    default10,
    public byte ChannelType;    // 0-世界;1-跨服;3- 仙盟
    public bool isSystem = false;       //系统消息
    public bool isDate = false;          //分割日期
    public byte NameLen;
    public string Name;
    public uint PlayerID;
    public string Content;
    public uint BubbleBox;    //聊天气泡框
    public ushort LV;    //等级
    public byte Job;    //职业
    public byte RealmLV;    //境界
    public uint TitleID;    //称号
    public uint Face;    //基本脸型
    public uint FacePic;    //头像框
    public uint ServerID;    //所属区服ID
    public uint TalkTime;        //该聊天发送时间戳
    public ArrayList InfoList;     //附加信息
}