using System; using System.Collections.Generic; using BrewMonster.Network; using BrewMonster.Scripts.UI; using CSNetwork; using CSNetwork.GPDataType; using CSNetwork.Protocols; namespace BrewMonster.Scripts.Chat { public static class Chat_GameSession { private static List m_aPendingProtocols = new(); private static List m_aPendingPlayers = new(); public static void AddElemForPendingProtocols(Protocol p) { m_aPendingProtocols.Add(p); } // Add one player's id to a buffer in order to get his name later public static void AddChatPlayerID(int id) { if (EC_Game.GetGameRun().GetPlayerName(id, false) != null) { m_aPendingPlayers.Add(id); } } public static bool ShouldBlockByLevel(chatmessage p) { int levelBlock = EC_Game.GetConfigs().GetBlackListSettings().levelBlock; if (p.Srclevel > 0 && p.Srclevel < levelBlock) { if ((ChatChannel)p.Channel is ChatChannel.GP_CHAT_LOCAL or ChatChannel.GP_CHAT_WHISPER or ChatChannel.GP_CHAT_TRADE) { if (!EC_Game.GetGameRun().GetHostPlayer().IsOmitBlocking(p.Srcroleid)) { // should be filted by level return true; } } } return false; } /// /// [Port] OnPrtcChatMessage policy chat handling (EC_GameSession.cpp:4597-4621) /// Xử lý Policy Chat (chat từ NPC/hệ thống với tham số đặc biệt $A, $S, $I...). /// Nếu là Policy Chat: thay thế các ký tự đặc biệt, format tọa độ. /// Nếu không: chỉ convert ký tự ^ và & cho hiển thị an toàn. /// /// Protocol gốc (để add vào pending nếu name chưa ready) /// Chat message data /// ref: nội dung chat (đã qua FilterInvalidTags), sẽ bị thay đổi nếu là policy chat /// out: chuỗi đã convert sẵn cho hiển thị (tương đương ACHAR szMsg[1024] trong C++) /// true = xử lý xong, false = cần chờ (pending name từ server) public static bool PolicyResolver(Protocol pProtocol, chatmessage p, ref string strTemp, out string szMsg) { szMsg = strTemp; if (IsPolicyChat(p)) { // C++: CHAT_S2C::CreatPolicyChatParameter(p->data) CHAT_S2C.PolicyChatParameter pPolicyChatPara = CHAT_S2C.CreatPolicyChatParameter(p.Data); // C++: if (!pPolicyChatPara && p->data.size() > 0) → parse failed nhưng có data → hiển thị "???" if (pPolicyChatPara == null && p.Data.Size > 0) { strTemp = "???"; } else { // C++: if (pPolicyChatPara && !pPolicyChatPara->IsNameReady()) if (pPolicyChatPara != null && !pPolicyChatPara.IsNameReady()) { pPolicyChatPara.GetNameFromServer(); m_aPendingProtocols.Add(pProtocol); szMsg = strTemp; return false; } // Thay thế ký tự đặc biệt trong policy chat ($A → tên NPC, v.v.) strTemp = CECUIHelper.PolicySpecialCharReplace(strTemp, pPolicyChatPara); // Format tọa độ nếu channel hỗ trợ if (CanFormatCoordText(p)) { // TODO: strTemp = CECUIHelper.FormatCoordText(strTemp); } } // C++: wcsncpy(szMsg, strTemp, strLen) — Policy chat không cần convert ^ & szMsg = strTemp; } else { // C++: AUI_ConvertChatString(strTemp, szMsg, false) // Convert ký tự ^ và & cho hiển thị an toàn char[] convBuf = new char[1024]; string input = strTemp; AUICommon.AUI_ConvertChatString(ref input, ref convBuf, false); // Tìm null terminator và tạo string int len = Array.IndexOf(convBuf, '\0'); szMsg = len >= 0 ? new string(convBuf, 0, len) : new string(convBuf); } return true; } private static bool IsPolicyChat(chatmessage p) { bool bOK = false; switch (p.Channel) { case (byte)ChatChannel.GP_CHAT_LOCAL: // ÃæÏò¸Ã NPC ¿É¼ûÓòÖÐËùÓÐÍæ¼Ò if (p.Srcroleid == 0 || // £¨²ßÂÔº°»°Öбê¼Ç $A £© ISNPCID(p.Srcroleid)) { // £¨²ßÂÔº°»°ÖÐÎÞ±ê¼Ç £© bOK = true; } break; case (byte)ChatChannel.GP_CHAT_BATTLE: // ÃæÏò³ÇÕ½¸±±¾Ö¸¶¨ÕóÓª»òÈ«²¿Íæ¼Òº°»°£¨²ßÂÔº°»°Öбê¼Ç $F¡¢$T £© if (p.Srcroleid == 0) { // Ϊ·ÀÒÔºóÓб仯£¬Ôö¼Ó´ËÅÐ¶Ï bOK = true; } break; case (byte)ChatChannel.GP_CHAT_BROADCAST: // ÃæÏòÈ«ÌåÔÚÏßÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $S £© if (p.Srcroleid == 0) { bOK = true; } break; case (byte)ChatChannel.GP_CHAT_INSTANCE : // ÃæÏò¸±±¾ÖÐÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $I £©£¨ $X ±ê¼Çʱ p->srcroleid == 1£¬´Ëʱֻ³öÏÖÔÚÆÁÄ»ÖÐÑ룩 if (p.Srcroleid == 0 || p.Srcroleid == 1) { bOK = true; } break; } return bOK; } static bool ISNPCID(int id) { uint uid = (uint)id; return (uid & 0x80000000) != 0 && (uid & 0x40000000) == 0; } static bool CanFormatCoordText(chatmessage p) { bool bOK = false; switch (p.Channel) { case (byte)ChatChannel.GP_CHAT_LOCAL: // ÃæÏò¸Ã NPC ¿É¼ûÓòÖÐËùÓÐÍæ¼Ò if (p.Srcroleid == 0 || // £¨²ßÂÔº°»°Öбê¼Ç $A £© ISNPCID(p.Srcroleid)) { // £¨²ßÂÔº°»°ÖÐÎÞ±ê¼Ç £© bOK = true; } break; case (byte)ChatChannel.GP_CHAT_BATTLE: // ÃæÏò³ÇÕ½¸±±¾Ö¸¶¨ÕóÓª»òÈ«²¿Íæ¼Òº°»°£¨²ßÂÔº°»°Öбê¼Ç $F¡¢$T £© if (p.Srcroleid == 0) { // Ϊ·ÀÒÔºóÓб仯£¬Ôö¼Ó´ËÅÐ¶Ï bOK = true; } break; case (byte)ChatChannel.GP_CHAT_BROADCAST: // ÃæÏòÈ«ÌåÔÚÏßÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $S £© if (p.Srcroleid == 0) { bOK = true; } break; case (byte)ChatChannel.GP_CHAT_INSTANCE: //p->srcroleid == 1 ʱ²»½øÐÐ×ø±êµÄÌæ»» if (p.Srcroleid == 0) { bOK = true; } break; } return bOK; } public class AUICTranslate { protected string m_AString = string.Empty; protected string m_AWString = string.Empty; public AUICTranslate() { } public string Translate(string str) { // In original C++ this likely performs UI charset translation. // Here we simply return the same string or store it. m_AString = str; return m_AString; } public string Translate(ReadOnlySpan str) { m_AWString = new string(str); return m_AWString; } public string ReverseTranslate(string str) { // Reverse translation placeholder m_AString = str; return m_AString; } public string ReverseTranslate(ReadOnlySpan str) { m_AWString = new string(str); return m_AWString; } } } }