using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Text; using System; using LitJson; public partial class ChatManager : GameSystemManager { public ChatChannel nowChatChannel; // public Dictionary> talkDict = new Dictionary>(); public Dictionary chatBubbles = new Dictionary(); // public Dictionary chatChannelSendTime = new Dictionary(); public event Action OnUpdateTalkEvent; public event Action OnUpdateTalkCacheListEvent; public Dictionary chatChannelCD = new Dictionary(); public int[] areaMyColorArr; public Color32 areaMyColor; public int[] areaOtherColorArr; public Color32 areaOtherColor; public Dictionary chatChannelBulletColorArrDict = new Dictionary(); public Dictionary chatChannelBulletColorDict = new Dictionary(); public int[] defaultChannelBulletColorArr; public Color32 defaultChannelBulletColor; public int characterLimit; public int sysBubbleID; public int[] sysBubbleColorArr; public Color32 sysBubbleColor; public override void Init() { 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(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), }; areaOtherColorArr = ConfigParse.GetMultipleStr(config.Numerical2); areaOtherColor = new Color32() { 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(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(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 { return 3; } } public static int GetUTF8InfoLen(string msg) { return Encoding.UTF8.GetBytes(msg).Length; } public bool TryGetBubble(int id, out ChatBubbleData bubble) { return chatBubbles.TryGetValue(id, out bubble); } void ParseChatBubbleConfig() { var configs = ChatBubbleBoxConfig.GetValues(); for (int i = 0; i < configs.Count; i++) { 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; } if (!SatisfyNameLength(info, out errorCode)) { return false; } if (DirtyWordConfig.IsDirtWord(info) || UIHelper.HasSpecialCharac(info) || DirtyNameConfig.IsDirtName(info)) { errorCode = 3; return false; } return true; } public void ShowChatErrorTip(int _errorCode) { switch (_errorCode) { case 0: //空 SysNotifyMgr.Instance.ShowTip("ChatInfoNoNull"); break; 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 datas)) return 0; return Mathf.Max(datas.Count - 1, 0); } 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 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 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; //聊天数量上限时删除前多少条 public event Action OnDeleteTalkEvent; void TryDeleteTalkData(ChatChannel type) { if (!TryGetTalkData(type, out List datas)) return; if (datas.Count < maxTalkCount) return; datas.RemoveRange(0, deleteTalkCount); OnDeleteTalkEvent?.Invoke(type); } public int currentDay = -1; public void AddTalkData(ChatChannel type, TalkData data) { //如果超过限制先删除旧数据 TryDeleteTalkData(type); if (!TryGetTalkData(type, out List datas)) { talkDict[type] = new List(); } 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(); } 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(); } 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(); } 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 OnChatTabChangeEvent; // 频道入口的展示顺序 public readonly List tabShowList = new List() { 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.Concat("ChatTab", ((int)chatTab).ToString())); } public string GetChatTabSelectIcon(ChatTab chatTab, bool isSelect) { return StringUtility.Concat(isSelect ? "ChatTabSelect" : "ChatTabUnSelect", ((int)chatTab).ToString()); } #endregion #region 弹幕设置 private Dictionary bulletSettingDict = new Dictionary(); private string settingsKey { get { return StringUtility.Concat("BulletChatSettings_", PlayerDatas.Instance.PlayerId.ToString()); } } // 设置特定频道的弹幕开关状态 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 saveDict = new Dictionary(); 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 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 } public enum ChatTab { 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; } public class TalkData { 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; //附加信息 }