diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs index 84fce2a20f..70b629d0c8 100644 --- a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using BrewMonster.Network; using BrewMonster.Scripts.UI; +using CSNetwork; using CSNetwork.GPDataType; using CSNetwork.Protocols; @@ -16,7 +17,7 @@ namespace BrewMonster.Scripts.Chat { 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) { @@ -25,7 +26,7 @@ namespace BrewMonster.Scripts.Chat m_aPendingPlayers.Add(id); } } - + public static bool ShouldBlockByLevel(chatmessage p) { int levelBlock = EC_Game.GetConfigs().GetBlackListSettings().levelBlock; @@ -46,73 +47,71 @@ namespace BrewMonster.Scripts.Chat return false; } - public static bool PolicyResolver(Protocol pProtocol, chatmessage p, ref string strTemp) + /// + /// [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)) { - // Todo: check logic + // C++: CHAT_S2C::CreatPolicyChatParameter(p->data) CHAT_S2C.PolicyChatParameter pPolicyChatPara = CHAT_S2C.CreatPolicyChatParameter(p.Data); - if (pPolicyChatPara != null && p.Data.Size > 0) + + // C++: if (!pPolicyChatPara && p->data.size() > 0) → parse failed nhưng có data → hiển thị "???" + if (pPolicyChatPara == null && p.Data.Size > 0) { - strTemp = ("???"); + 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)) { - //strTemp = CECUIHelper.FormatCoordText(strTemp); + // TODO: strTemp = CECUIHelper.FormatCoordText(strTemp); } } - //int strLen = strTemp.GetLength(); - //wcsncpy(szMsg, strTemp, strLen); - //szMsg[strLen] = 0; + + // C++: wcsncpy(szMsg, strTemp, strLen) — Policy chat không cần convert ^ & + szMsg = strTemp; } else { - //AUI_ConvertChatString(strTemp, szMsg, false); + // 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 void AUI_ConvertChatString(string pszChat, string pszConv, bool bName) - { - int i, nLen = 0; - if (pszChat != null || pszConv != null) - return; - - pszConv[0] = 0; - for( i = 0; i < (int)a_strlen(pszChat); i++ ) - { - if( pszChat[i] == '^' ) - { - pszConv[nLen] = '^'; - pszConv[nLen + 1] = '^'; - nLen += 2; - } - else if( pszChat[i] == '&' ) - { - pszConv[nLen] = '^'; - pszConv[nLen + 1] = '&'; - nLen += 2; - } - else - { - pszConv[nLen] = pszChat[i]; - nLen++; - } - } - pszConv[nLen] = 0; - }*/ - private static bool IsPolicyChat(chatmessage p) { bool bOK = false; @@ -240,4 +239,4 @@ namespace BrewMonster.Scripts.Chat } } } -} \ No newline at end of file +} diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs index cb9e652961..83c7157e53 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs @@ -343,7 +343,7 @@ namespace CSNetwork else { sb.Append('{').Append(argIndex++).Append('}'); - + i++; // Nhảy qua các ký tự định dạng (ví dụ: %02d, %ls, %f) while (i < format.Length && (char.IsDigit(format[i]) || format[i] == '.' || format[i] == 'l' || format[i] == 'u' || format[i] == 'd' || format[i] == 's' || format[i] == 'f' || format[i] == 'x')) @@ -366,6 +366,88 @@ namespace CSNetwork } return sb.ToString(); } + + /// + /// [Port] CECGameUIMan::FilterInvalidTags (EC_GameUIMan.cpp:6424) + /// Lọc bỏ các tag đặc biệt không hợp lệ trong nội dung chat nhận từ server. + /// - Emotion (biểu cảm): luôn giữ lại. + /// - IvtrItem (link vật phẩm): giữ hoặc loại tùy bFilterItem. + /// - Tất cả loại khác (image, coord...): luôn bị loại bỏ (thay bằng text thuần). + /// + public static string FilterInvalidTags(string szText, bool bFilterItem) + { + return AUI_FilterEditboxItem(szText, (EditBoxItemBase pItem) => + { + var type = pItem.GetType(); + + if (type == EditboxItemType.enumEIEmotion) + return false; + + if (type == EditboxItemType.enumEIIvtrlItem) + { + pItem.SetInfo(""); + return bFilterItem; + } + + return true; + }); + } + + /// + /// [Port] CECGameUIMan::AUI_FilterEditboxItem (EC_GameUIMan.cpp:6477) + /// Duyệt qua các EditboxItem trong text, filter trả về true thì item bị loại + /// (thay bằng tên hiển thị dạng text thuần), false thì giữ nguyên. + /// + public static string AUI_FilterEditboxItem(string szText, Func filter) + { + EditBoxItemsSet itemsSet = new EditBoxItemsSet(); + string strText = UnmarshalEditBoxText(szText, itemsSet); + + int nCount = itemsSet.GetItemCount(); + if (nCount == 0) + return szText; + + var it = itemsSet.GetItemIterator(); + int i = 0; + var itemsToFilter = new List>(); + + while (it.MoveNext() && i < nCount) + { + EditBoxItemBase pItem = it.Current.Value; + if (pItem != null && filter(pItem)) + { + itemsToFilter.Add(new KeyValuePair(it.Current.Key, pItem.GetName())); + } + i++; + } + + foreach (var kv in itemsToFilter) + { + strText = AUI_ReplaceEditboxItem(strText, kv.Key, kv.Value); + } + + return MarshalEditBoxText(strText, itemsSet); + } + + /// + /// [Port] CECGameUIMan::AUI_ReplaceEditboxItem (EC_GameUIMan.cpp:6431) + /// Thay thế ký tự EditboxItem code trong chuỗi bằng một đoạn text thay thế. + /// + public static string AUI_ReplaceEditboxItem(string szText, char cItem, string szSubText) + { + if (string.IsNullOrEmpty(szText) || !IsEditboxItemCode(cItem)) + return szText ?? ""; + + var sb = new StringBuilder(szText.Length); + for (int i = 0; i < szText.Length; i++) + { + if (szText[i] == cItem) + sb.Append(szSubText ?? ""); + else + sb.Append(szText[i]); + } + return sb.ToString(); + } } } diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index ee7525407a..913df0fd1c 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -2073,15 +2073,15 @@ namespace CSNetwork string szMsg = null; string strTemp = Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length); string strMsgOrigion = strTemp; - // Todo: Show Text on Ui Game - //strTemp = pGameUI.FilterInvalidTags(strTemp, pItem==NULL); - /*if (!Chat_GameSession.PolicyResolver(pProtocol, p, ref strTemp)) + // [Port] CECGameUIMan::FilterInvalidTags — Lọc các tag đặc biệt không hợp lệ + strTemp = AUICommon.FilterInvalidTags(strTemp, pItem == null); + if (!Chat_GameSession.PolicyResolver(pProtocol, p, ref strTemp, out szMsg)) { Debug.Log("[Cuong] 2"); return false; } - if (Chat_GameSession.HanldeGPChatSystem(p, bCalledagain)) + /*if (Chat_GameSession.HanldeGPChatSystem(p, bCalledagain)) { Debug.Log("[Cuong] 3"); }*/ @@ -2168,7 +2168,7 @@ namespace CSNetwork string str = string.Format( fmt, szName, - strTemp + szMsg ); // [Port] Gọi AddChatMessage để hiển thị lên UI Chat Box. // AddChatMessage bên trong đã tự publish ChatMessageEvent (cho ChatPanelUI) @@ -2192,7 +2192,7 @@ namespace CSNetwork string message = string.Format( template, pNPC.GetName(), - strTemp + szMsg ); EC_Game.GetGameRun().AddChatMessage( diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs index 3952f93eec..93dcc86fe8 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs @@ -546,7 +546,10 @@ namespace BrewMonster.UI if (showsAboveHead && idPlayer > 0) { - EventBus.PublishChannel(idPlayer, new EventChatMessageOnTopPlayer(idPlayer, pszMsg)); + // C++: pPlayer->SetLastSaidWords(strTemp, ...) — strTemp là nội dung chat thuần, + // KHÔNG bao gồm tên người chơi. Dùng pszMsgOrigion thay vì pszMsg. + string bubbleText = !string.IsNullOrEmpty(pszMsgOrigion) ? pszMsgOrigion : pszMsg; + EventBus.PublishChannel(idPlayer, new EventChatMessageOnTopPlayer(idPlayer, bubbleText)); } // C++: if( cChannel == GP_CHAT_BROADCAST && byFlag == 0 ) SetMarqueeMsg(strConverted); diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatCanvas.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatCanvas.prefab index e7d9c03282..513d2bdf52 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatCanvas.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatCanvas.prefab @@ -700,7 +700,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &4963429530816417249 RectTransform: m_ObjectHideFlags: 0 @@ -718,7 +718,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} m_AnchorMax: {x: 0.5, y: 0} - m_AnchoredPosition: {x: -396, y: 79} + m_AnchoredPosition: {x: -495, y: 55} m_SizeDelta: {x: 75, y: 75} m_Pivot: {x: 0.5, y: 0} --- !u!1 &4030418742857763219 @@ -892,7 +892,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0} m_AnchorMax: {x: 0.5, y: 0} - m_AnchoredPosition: {x: -346.5, y: 50} + m_AnchoredPosition: {x: -483, y: 26} m_SizeDelta: {x: 706.4468, y: 450} m_Pivot: {x: 0, y: 0} --- !u!222 &5213722908587404148 @@ -961,7 +961,7 @@ RectTransform: m_GameObject: {fileID: 4425286880128952433} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} + m_LocalScale: {x: 1, y: 1.5, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 5425127200754731771} @@ -2623,7 +2623,7 @@ GameObject: - component: {fileID: 6422370174043654984} - component: {fileID: 5572451279827723964} - component: {fileID: 7570271903590474125} - - component: {fileID: 1824650527810444928} + - component: {fileID: 1219475443647629663} m_Layer: 5 m_Name: Content m_TagString: Untagged @@ -2690,7 +2690,7 @@ MonoBehaviour: m_ChildScaleWidth: 0 m_ChildScaleHeight: 0 m_ReverseArrangement: 0 ---- !u!114 &1824650527810444928 +--- !u!114 &1219475443647629663 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -2739,8 +2739,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 8.5} + m_SizeDelta: {x: 0, y: -17} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &4906838759260176767 CanvasRenderer: @@ -2830,8 +2830,8 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -13.292114} - m_SizeDelta: {x: 0, y: -86.5841} + m_AnchoredPosition: {x: 0, y: -13.292} + m_SizeDelta: {x: 0, y: -86.584} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7307691078876554531 CanvasRenderer: diff --git a/Assets/Prefabs/ChatSystem/prefab_TextContents.prefab b/Assets/Prefabs/ChatSystem/prefab_TextContents.prefab index 767f05e756..4a502fb8cb 100644 --- a/Assets/Prefabs/ChatSystem/prefab_TextContents.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_TextContents.prefab @@ -110,9 +110,9 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 0, y: 1} - m_AnchoredPosition: {x: 399.5, y: -5} + m_AnchoredPosition: {x: 99, y: -5} m_SizeDelta: {x: 601, y: 0} - m_Pivot: {x: 0.5, y: 1} + m_Pivot: {x: 0, y: 1} --- !u!222 &7228077960814023056 CanvasRenderer: m_ObjectHideFlags: 0 @@ -250,7 +250,7 @@ GameObject: - component: {fileID: 1976417251556044024} - component: {fileID: -887576589064363463} - component: {fileID: 8910872808253115585} - - component: {fileID: 2909592183608440979} + - component: {fileID: 6178869782871973483} m_Layer: 5 m_Name: prefab_TextContents m_TagString: Untagged @@ -276,9 +276,9 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} - m_AnchoredPosition: {x: 0, y: 0} + m_AnchoredPosition: {x: -350, y: 88.255005} m_SizeDelta: {x: 700, y: 0} - m_Pivot: {x: 0.5, y: 0.5} + m_Pivot: {x: 0, y: 1} --- !u!222 &616079771158270572 CanvasRenderer: m_ObjectHideFlags: 0 @@ -371,7 +371,7 @@ MonoBehaviour: m_ChildScaleWidth: 0 m_ChildScaleHeight: 1 m_ReverseArrangement: 0 ---- !u!114 &2909592183608440979 +--- !u!114 &6178869782871973483 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 623583c253..ba6943c85a 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -52,7 +52,7 @@ namespace BrewMonster.Scripts.ChatUI if (strText.Length <= 0) { - ChangeFocus(); + //ChangeFocus(); return; } @@ -79,7 +79,7 @@ namespace BrewMonster.Scripts.ChatUI SaveHistory(resolvedChannel, strText, nPack, nSlot, now); - ChangeFocus(); + //ChangeFocus(); } // ===================================================== @@ -294,14 +294,14 @@ namespace BrewMonster.Scripts.ChatUI { CECUIManager.Instance.FilterBadWords(ref text); } - + private void AddChatMessage(string msg, ChatChannel channel, int idPlayer = -1, string pszPlayer = "", byte byFlag = 0) { string strModified = msg; - + // 1. Filter bad words CECUIManager.Instance.FilterBadWords(ref strModified); - + if (string.IsNullOrEmpty(strModified)) return; @@ -320,13 +320,13 @@ namespace BrewMonster.Scripts.ChatUI string colorHex = GetChannelColorHex(channel); string prefix = GetChannelPrefix(channel); string sender = string.IsNullOrEmpty(pszPlayer) ? "" : $"{pszPlayer}: "; - + string finalMsg = $"{prefix}{sender}{strModified}"; // 5. Publish event EventBus.Publish(new GameSession.ChatMessageEvent(finalMsg, (byte)channel)); - - Debug.Log(finalMsg); + + Debug.Log("[Cuong] AddChatMessage" + finalMsg); } private string GetChannelColorHex(ChatChannel channel) @@ -365,4 +365,4 @@ namespace BrewMonster.Scripts.ChatUI private int GetPlayerItemCount(int id) => 0; private int GetPlayerLevel() => 10; } -} \ No newline at end of file +}