Merge pull request 'Change UI Chat' (#256) from feature/chat into develop

Reviewed-on: https://git.pthub.vn/Unity/perfect-world-unity/pulls/256
This commit is contained in:
cuongnv
2026-03-19 07:19:07 +00:00
7 changed files with 159 additions and 75 deletions
@@ -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)
/// <summary>
/// [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à &amp; cho hiển thị an toàn.
/// </summary>
/// <param name="pProtocol">Protocol gốc (để add vào pending nếu name chưa ready)</param>
/// <param name="p">Chat message data</param>
/// <param name="strTemp">ref: nội dung chat (đã qua FilterInvalidTags), sẽ bị thay đổi nếu là policy chat</param>
/// <param name="szMsg">out: chuỗi đã convert sẵn cho hiển thị (tương đương ACHAR szMsg[1024] trong C++)</param>
/// <returns>true = xử lý xong, false = cần chờ (pending name từ server)</returns>
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
}
}
}
}
}
@@ -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();
}
/// <summary>
/// [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).
/// </summary>
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;
});
}
/// <summary>
/// [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.
/// </summary>
public static string AUI_FilterEditboxItem(string szText, Func<EditBoxItemBase, bool> 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<KeyValuePair<char, string>>();
while (it.MoveNext() && i < nCount)
{
EditBoxItemBase pItem = it.Current.Value;
if (pItem != null && filter(pItem))
{
itemsToFilter.Add(new KeyValuePair<char, string>(it.Current.Key, pItem.GetName()));
}
i++;
}
foreach (var kv in itemsToFilter)
{
strText = AUI_ReplaceEditboxItem(strText, kv.Key, kv.Value);
}
return MarshalEditBoxText(strText, itemsSet);
}
/// <summary>
/// [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ế.
/// </summary>
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();
}
}
}
@@ -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(
@@ -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);
@@ -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:
@@ -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}
+9 -9
View File
@@ -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 = $"<color=#{colorHex}>{prefix}{sender}{strModified}</color>";
// 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;
}
}
}