From 72299d0b71468c1f3a63ff3fd7f407b998ce4e42 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Fri, 6 Mar 2026 16:45:35 +0700 Subject: [PATCH] Add base script for chat --- .../PerfectWorld/Scripts/Chat/CECPateText.cs | 239 ++++++ .../Scripts/Chat/CECPateText.cs.meta | 2 + Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs | 26 + .../Scripts/Chat/CECPlayer.cs.meta | 3 + Assets/PerfectWorld/Scripts/Chat/CHAT_S2C.cs | 226 ++++-- .../Scripts/Chat/Chat_GameSession.cs | 544 +++++++++++++ .../Scripts/Chat/Chat_GameSession.cs.meta | 2 + .../PerfectWorld/Scripts/Chat/Chat_Helper.cs | 17 + .../Scripts/Chat/Chat_Helper.cs.meta | 2 + Assets/PerfectWorld/Scripts/Chat/TestChat.cs | 23 + .../Scripts/Chat/TestChat.cs.meta | 3 + Assets/PerfectWorld/Scripts/NPC/CECNPC.cs | 29 + .../Scripts/Network/CSNetwork/AUICommon.cs | 753 ++++++++++++++++++ .../Scripts/Network/CSNetwork/GameSession.cs | 138 +++- .../Scripts/UI/GamePlay/EC_GameUIMan.cs | 7 + Assets/Scripts/CECGameRun.cs | 13 + Assets/Scripts/ChatInputHandler.cs | 11 +- 17 files changed, 1953 insertions(+), 85 deletions(-) create mode 100644 Assets/PerfectWorld/Scripts/Chat/CECPateText.cs create mode 100644 Assets/PerfectWorld/Scripts/Chat/CECPateText.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs create mode 100644 Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs create mode 100644 Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs create mode 100644 Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs.meta create mode 100644 Assets/PerfectWorld/Scripts/Chat/TestChat.cs create mode 100644 Assets/PerfectWorld/Scripts/Chat/TestChat.cs.meta diff --git a/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs b/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs new file mode 100644 index 0000000000..d7a5c6fbd5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs @@ -0,0 +1,239 @@ +using System.Collections.Generic; +using BrewMonster.Network; +using BrewMonster.Scripts.Managers; +using BrewMonster.UI; +using UnityEngine; + +namespace BrewMonster.Scripts.Chat +{ + public class CECPateText + { + protected List m_aTextStrs; + //protected List<> m_aEmotions; + //protected List m_aItems; + //protected EditBoxItemSet m_ItemSet; + + // Text item type + public enum ETextType + { + TYPE_TEXT = 0, + TYPE_EMOTION, + TYPE_BOOTHNAME, + } + + // Text item + public struct ITEM + { + public int iType; // Text type + public int iIndex; // Index of item + public int iExtX; // Extent + public int iExtY; + + public int iLine; + + //A3DCOLOR clItem; + public Color clItem; + }; + + public int SetText(string szText, + bool bIncEmotion, + out string pstrTextConverted, + bool bEllipsis = true, + EC_IvtrItem pIvtrItem = null) + { + // Clear old content + Clear(); + + pstrTextConverted = null; + + if (string.IsNullOrEmpty(szText)) + return 0; + + CECGameUIMan pGameUI = + EC_Game.GetGameRun().GetUIManager().GetInGameUIMan(); + + /*string str = pGameUI.AUI_FilterEditboxItem( + szText, + CECGameUIMan.AUI_EditboxItemMaskFilter(1 << (int)enumEICoord) + ); + + string strName; + A3DCOLOR clrName; + + pGameUI.TransformNameColor(pIvtrItem, out strName, out clrName); + + str = UnmarshalEditBoxText(str, m_ItemsSet, 0, strName, clrName); + + szText = str; + pstrTextConverted = str; + + int iAddedChar = 0; + + if (!bIncEmotion) + { + int iLen = szText.Length; + + if (iLen > m_iMaxLineLen) + { + string sub = szText.Substring(0, m_iMaxLineLen); + + if (bEllipsis) + sub += "..."; + + CreateTextItem(sub, -1, 0); + + iAddedChar = m_iMaxLineLen; + } + else + { + CreateTextItem(szText, -1, 0); + iAddedChar = iLen; + } + } + else + { + int i = 0; + int iStart = 0; + int iEnd = 0; + int iLenCnt = 0; + + bool bTooLong = false; + int iLine = 0; + + while (i < szText.Length) + { + char ch = szText[i]; + + if (IsEditboxItemCode(ch)) + { + if (iEnd > iStart) + CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine); + + EditBoxItemBase pItem = m_ItemsSet.GetItemByChar(ch); + + if (pItem != null) + { + if (pItem.GetType() == enumEIEmotion) + { + int nSet = 0; + int nIndex = 0; + + UnmarshalEmotionInfo(pItem.GetInfo(), out nSet, out nIndex); + + CreateEmotionItem(nSet, nIndex, iLine); + + iLenCnt += 2; + } + else + { + string szName = pItem.GetName(); + + CreateTextItem(szName, -1, iLine, pItem.GetColor()); + + iLenCnt += szName.Length; + } + } + + i++; + iStart = i; + iEnd = i; + + goto CheckLength; + } + + iEnd++; + i++; + iLenCnt++; + + CheckLength: + + if (iLenCnt > m_iMaxLineLen) + { + if (iLine + 1 >= m_iMaxLines) + { + bTooLong = true; + break; + } + + if (iEnd > iStart) + CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine); + + iStart = i; + iLine++; + iLenCnt = 0; + } + } + + iAddedChar = i; + + if (iEnd > iStart) + { + if (bTooLong) + { + string strEnd = szText.Substring(iStart, iEnd - iStart); + + if (bEllipsis) + strEnd += "..."; + + CreateTextItem(strEnd, -1, iLine); + } + else + { + CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine); + } + } + else if (bTooLong) + { + if (bEllipsis) + CreateTextItem("...", -1, iLine); + } + } + + // Calculate extent + + m_iExtX = 0; + m_iExtY = 0; + + int iLineExtX = 0; + int iLastLine = 0; + + for (int i = 0; i < m_aItems.GetSize(); i++) + { + ITEM item = m_aItems[i]; + + if (item.iLine != iLastLine) + { + iLastLine = item.iLine; + + if (m_iExtX < iLineExtX) + m_iExtX = iLineExtX; + + iLineExtX = item.iExtX; + } + else + { + iLineExtX += item.iExtX; + } + + if (m_iExtY < item.iExtY) + m_iExtY = item.iExtY; + } + + m_iLines = iLastLine + 1; + + if (m_iExtX < iLineExtX) + m_iExtX = iLineExtX;*/ + //return iAddedChar; + + return 0; + } + + public void Clear() + { + m_aTextStrs.Clear(); + /*m_aEmotions.Clear(); + m_aItems.Clear(); + m_ItemsSet.Clear();*/ + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs.meta b/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs.meta new file mode 100644 index 0000000000..b9484999d0 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/CECPateText.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f6eb4b59e25704044a88b0055abb0e99 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs new file mode 100644 index 0000000000..83780b3d6c --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs @@ -0,0 +1,26 @@ +using BrewMonster.Scripts.Managers; +using CSNetwork; + +namespace BrewMonster.Scripts.Chat +{ + public abstract partial class CECPlayer : CECObject + { + private CECPateText m_pPateLastWords1; + private CECPateText m_pPateLastWords2; + private CECCounter m_strLastSayCnt; + + // Set last said words + public void SetLastSaidWords(string szWords, int nEmotionSet, EC_IvtrItem pItem) + { + if (m_pPateLastWords1 == null || m_pPateLastWords2 == null) + return; + + string str = AUICommon.FilterEmotionSet(szWords, nEmotionSet); + szWords = str; + + m_pPateLastWords1.SetText(szWords, true , out var newStr,true, pItem); + m_pPateLastWords2.Clear(); + m_strLastSayCnt.Reset(); + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs.meta b/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs.meta new file mode 100644 index 0000000000..26eed1206d --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/CECPlayer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2c109cbbaf8d4ce19a93990a5f5883f6 +timeCreated: 1772699598 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/CHAT_S2C.cs b/Assets/PerfectWorld/Scripts/Chat/CHAT_S2C.cs index 647f807bd0..c817c1b316 100644 --- a/Assets/PerfectWorld/Scripts/Chat/CHAT_S2C.cs +++ b/Assets/PerfectWorld/Scripts/Chat/CHAT_S2C.cs @@ -1,16 +1,29 @@ using System; using System.Buffers.Binary; using System.Runtime.InteropServices; +using BrewMonster.Network; using BrewMonster.Scripts.Managers; using CSNetwork; using CSNetwork.GPDataType; namespace BrewMonster.Scripts.Chat { + struct chat_policy_parameter + { + short cmd_id; + + int parameter_mask; + //if(parameter_mask & CHAT_PARAMETER_ROLEID) 1 int insert here + //if(parameter_mask & CHAT_PARAMETER_LOCALVAL0) 1 int insert here + //if(parameter_m ask & CHAT_PARAMETER_LOCALVAL1) 1 int insert here + //if(parameter_mask & CHAT_PARAMETER_LOCALVAL2) 1 int insert here + }; + public struct chat_item_base { public short cmd_id; }; + public struct chat_equip_item { public short cmd_id; @@ -22,16 +35,16 @@ namespace BrewMonster.Scripts.Chat public int LenghtHeader() { - return Marshal.SizeOf()+ (Marshal.SizeOf() * 3) + Marshal.SizeOf(); + return Marshal.SizeOf() + (Marshal.SizeOf() * 3) + Marshal.SizeOf(); } } - + struct chat_generalcard_collection { public short cmd_id; public int card_id; }; - + public static class CHAT_S2C { public enum EChatS2CCommand : short @@ -40,7 +53,7 @@ namespace BrewMonster.Scripts.Chat CHAT_GENERALCARD_COLLECTION, CHAT_POLICYCHAT_PARAMETER, } - + public static EC_IvtrItem CreateChatItem(Octets data) { EC_IvtrItem pIvtrItem = null; @@ -51,18 +64,7 @@ namespace BrewMonster.Scripts.Chat { chat_equip_item pItemInfo = default; var sz = pItemInfo.LenghtHeader(); - - var newByte = GPDataTypeHelper.ContentBytes(data.Size, 0); - pItemInfo.cmd_id = GPDataTypeHelper.FromBytes(newByte); - newByte = GPDataTypeHelper.ContentBytes(data.Size, 2); - pItemInfo.type = GPDataTypeHelper.FromBytes(newByte); - newByte = GPDataTypeHelper.ContentBytes(data.Size, 6); - pItemInfo.expire_date = GPDataTypeHelper.FromBytes(newByte); - newByte = GPDataTypeHelper.ContentBytes(data.Size, 10); - pItemInfo.proc_type = GPDataTypeHelper.FromBytes(newByte); - newByte = GPDataTypeHelper.ContentBytes(data.Size, (10 + sizeof(ushort))); - pItemInfo.content_length = GPDataTypeHelper.FromBytes(newByte); - + if (data.Size >= sz && sz + pItemInfo.content_length == data.Size) { if (pItemInfo.cmd_id == (short)EChatS2CCommand.CHAT_EQUIP_ITEM) @@ -76,14 +78,17 @@ namespace BrewMonster.Scripts.Chat } } } - else if(pInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION) + else if (pInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION) { - chat_generalcard_collection pItemInfo = GPDataTypeHelper.FromBytes(data.ByteArray); + chat_generalcard_collection pItemInfo = + GPDataTypeHelper.FromBytes(data.ByteArray); if (data.Size > Marshal.SizeOf()) { - if (pItemInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION){ - pIvtrItem = EC_IvtrItem.CreateItem(pItemInfo.card_id, 0, 1); - if (pIvtrItem != null){ + if (pItemInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION) + { + pIvtrItem = EC_IvtrItem.CreateItem(pItemInfo.card_id, 0, 1); + if (pIvtrItem != null) + { pIvtrItem.GetDetailDataFromLocal(); } } @@ -93,57 +98,150 @@ namespace BrewMonster.Scripts.Chat return pIvtrItem; } - } - - public ref struct PacketReader - { - private ReadOnlySpan _buffer; - private int _offset; - public PacketReader(ReadOnlySpan buffer) + [System.Flags] + public enum ChatParameterMask { - _buffer = buffer; - _offset = 0; + CHAT_PARAMETER_ROLEID = 0x00000001, + CHAT_PARAMETER_LOCALVAL0 = 0x00000002, + CHAT_PARAMETER_LOCALVAL1 = 0x00000004, + CHAT_PARAMETER_LOCALVAL2 = 0x00000008 } - public int Remaining => _buffer.Length - _offset; - - public short ReadInt16() + public class PolicyChatParameter { - Ensure(2); - short value = BinaryPrimitives.ReadInt16LittleEndian(_buffer.Slice(_offset)); - _offset += 2; - return value; + public int role_id = -1; + public string name = string.Empty; + public ChatParameterMask parameter_mask = 0; + + private int localval_0 = -1; + private int localval_1 = -1; + private int localval_2 = -1; + + EC_Game g_pGame; + + public bool HasRoleID() + { + return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_ROLEID) != 0; + } + + public bool HasLocalValue(int valueNum) + { + switch (valueNum) + { + case 0: + return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL0) != 0; + case 1: + return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL1) != 0; + case 2: + return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL2) != 0; + default: + return false; + } + } + + public void GetNameFromServer() + { + if (role_id != -1 && HasRoleID()) + { + int[] arr = new int[1] { role_id }; + UnityGameSession.Instance.GameSession.CmdCache.SendGetPlayerBriefInfo(1, arr, 1); + } + } + + public void SetLocalValue(int localValue, int id) + { + switch (id) + { + case 0: + localval_0 = localValue; + parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL0; + break; + case 1: + localval_1 = localValue; + parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL1; + break; + case 2: + localval_2 = localValue; + parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL2; + break; + } + } + + public bool TryGetLocalValue(int localVariableID, out int localVariable) + { + localVariable = -1; + + if (!HasLocalValue(localVariableID)) + return false; + + switch (localVariableID) + { + case 0: localVariable = localval_0; break; + case 1: localVariable = localval_1; break; + case 2: localVariable = localval_2; break; + } + + return true; + } + + public bool IsNameReady() + { + return HasRoleID() ? !string.IsNullOrEmpty(name) : true; + } } - public ushort ReadUInt16() + public static PolicyChatParameter CreatPolicyChatParameter(Octets data) { - Ensure(2); - ushort value = BinaryPrimitives.ReadUInt16LittleEndian(_buffer.Slice(_offset)); - _offset += 2; - return value; - } + PolicyChatParameter result = null; + if (data.Size >= Marshal.SizeOf(typeof(chat_item_base))) + { + chat_item_base pInfo = GPDataTypeHelper.FromBytes(data.ByteArray); + if (pInfo.cmd_id == (short)EChatS2CCommand.CHAT_POLICYCHAT_PARAMETER) + { + if (data.Size >= Marshal.SizeOf(typeof(chat_policy_parameter))) + { + Byte pData = GPDataTypeHelper.FromBytes(data.ByteArray, + Marshal.SizeOf(typeof(chat_policy_parameter))); + result = new PolicyChatParameter(); + // Todo: Check logic + result.parameter_mask = + GPDataTypeHelper.FromBytes(data.ByteArray, sizeof(short)); + /*int paraMask = result.parameter_mask; + if (paraMask & ChatParameterMask.CHAT_PARAMETER_ROLEID){ + if (pData + sizeof(int) > data.end()){ + return null; + } + result->SetRoleId(*(int*)pData); + pData += sizeof(int); + } + if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL0){ + if (pData + sizeof(int) > data.end()){ + return null; + } + result->SetLocaValue(*(int*)pData, 0); + pData += sizeof(int); + } + if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL1){ + if (pData + sizeof(int) > data.end()){ + return null; + } + result.SetLocaValue((int)pData, 1); + pData += sizeof(int); + } + if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL2){ + if (pData + sizeof(int) > data.end()){ + return null; + } + // Check logic + result.SetLocalValue((int)pData, 2); + pData += sizeof(int); + }*/ + } + } + } - public int ReadInt32() - { - Ensure(4); - int value = BinaryPrimitives.ReadInt32LittleEndian(_buffer.Slice(_offset)); - _offset += 4; - return value; - } - - public ReadOnlySpan ReadBytes(int count) - { - Ensure(count); - var span = _buffer.Slice(_offset, count); - _offset += count; - return span; - } - - private void Ensure(int size) - { - if (Remaining < size) - throw new InvalidOperationException("Packet out of range"); + return result; } } -} +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs new file mode 100644 index 0000000000..20346f8e42 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs @@ -0,0 +1,544 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using BrewMonster.Network; +using BrewMonster.Scripts.UI; +using BrewMonster.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; + } + + public static bool PolicyResolver(Protocol pProtocol, chatmessage p, ref string strTemp) + { + if (IsPolicyChat(p)) + { + // Todo: check logic + CHAT_S2C.PolicyChatParameter pPolicyChatPara = CHAT_S2C.CreatPolicyChatParameter(p.Data); + if (pPolicyChatPara != null && p.Data.Size > 0) + { + strTemp = ("???"); + } + else + { + if (pPolicyChatPara != null && !pPolicyChatPara.IsNameReady()) + { + pPolicyChatPara.GetNameFromServer(); + m_aPendingProtocols.Add(pProtocol); + return false; + } + + strTemp = CECUIHelper.PolicySpecialCharReplace(strTemp, pPolicyChatPara); + if (CanFormatCoordText(p)) + { + //strTemp = CECUIHelper.FormatCoordText(strTemp); + } + } + //int strLen = strTemp.GetLength(); + //wcsncpy(szMsg, strTemp, strLen); + //szMsg[strLen] = 0; + } + else + { + //AUI_ConvertChatString(strTemp, szMsg, false); + } + + 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; + 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 static bool HanldeGPChatSystem(chatmessage p, bool bCalledagain) + { + /*string strMsg = ""; + switch (p.Srcroleid) + { + case 1: //CMSG_BIDSTART + case 2: //CMSG_BIDEND + case 3: //CMSG_BATTLESTART + case 4: //CMSG_BATTLEEND + case 6: //CMSG_BONUSSEND + case 7: //CMSG_SPECIAL + { + if (!bCalledagain) + { + List pending; + if (!OnBattleChatMessage(p, pending)) + { + if (pending.GetSize() > 0) + { + GetFactionInfo(pending.GetSize(), pending.GetData()); + m_aPendingProtocols.Add(pProtocol); + return false; + } + } + } + else + { + if (!OnBattleChatMessage(p, NULL)) + { + return false; + } + } + + return true; + } + } + + return true; + } + + public static bool OnBattleChatMessage(chatmessage p, AArray pPendingFactions) + { + var pGameUI = g_pGame.GetGameRun().GetUIManager().GetInGameUIMan(); + var pDlgGuildMap = (CDlgGuildMap)pGameUI.GetDialog("Win_GuildMap"); + + var info = GlobalData.GetDomainInfos(); + var pMan = g_pGame.GetFactionMan(); + + string strMsg = string.Empty; + + if (pPendingFactions != null) + { + pPendingFactions.RemoveAll(); + } + + switch (p.srcroleid) + { + case 1: // CMSG_BIDSTART + { + strMsg = pGameUI.GetStringFromTable(4001); + break; + } + + case 2: // CMSG_BIDEND + { + int group = p.msg.Size / (sizeof(short) + 2 * sizeof(uint)); + strMsg = pGameUI.GetStringFromTable(4002); + + if (group == 0) + break; + + int idFaction = g_pGame.GetGameRun().GetHostPlayer().GetFactionID(); + + string strAttacker = ""; + string strDefender = ""; + string strAnnounce = ""; + + pDlgGuildMap.ClearMessage(); + + var o = new OctetsStream(p.msg); + + for (; group > 0; group--) + { + short id = o.ReadShort(); + uint owner = o.ReadUInt(); + uint challenger = o.ReadUInt(); + + var finfo = pMan.GetFaction(challenger); + + if ((int)challenger == idFaction) + strAttacker = pGameUI.GetStringFromTable(4500); + else if (finfo != null) + strAttacker = string.Format(pGameUI.GetStringFromTable(4499), finfo.GetName()); + else if (pPendingFactions != null) + pPendingFactions.UniquelyAdd((int)challenger); + + if (string.IsNullOrEmpty(strAttacker)) + { + strMsg = ""; + continue; + } + + for (int i = 0; i < info.Count; i++) + { + if (info[i].id == id) + { + if (info[i].id_owner != 0) + { + if (info[i].id_owner == idFaction) + strDefender = pGameUI.GetStringFromTable(4500); + else + { + var finfo2 = pMan.GetFaction(info[i].id_owner); + + if (finfo2 != null) + strDefender = string.Format( + pGameUI.GetStringFromTable(4499), + finfo2.GetName()); + else if (pPendingFactions != null) + pPendingFactions.UniquelyAdd(info[i].id_owner); + } + } + + if (string.IsNullOrEmpty(strDefender)) + { + strAnnounce = string.Format( + pGameUI.GetStringFromTable(4511), + strAttacker, + info[i].name); + } + else + { + strAnnounce = string.Format( + pGameUI.GetStringFromTable(4512), + strAttacker, + strDefender, + info[i].name); + } + + if (pPendingFactions == null || pPendingFactions.GetSize() == 0) + { + pDlgGuildMap.AddMessage(strAnnounce); + } + + break; + } + } + + strAttacker = ""; + strDefender = ""; + } + + if ((pPendingFactions != null && pPendingFactions.GetSize() > 0) || string.IsNullOrEmpty(strMsg)) + { + pDlgGuildMap.ClearMessage(); + strMsg = ""; + } + + break; + } + + case 3: // CMSG_BATTLESTART + { + if (p.msg.Size < 2) + return false; + + var os = new OctetsStream(p.msg); + + byte id = os.ReadByte(); + byte result = os.ReadByte(); + + for (int i = 0; i < info.Count; i++) + { + if (info[i].id == id) + { + strMsg = string.Format( + pGameUI.GetStringFromTable(4003), + info[i].name); + + strMsg += pGameUI.GetStringFromTable(3900 + result); + break; + } + } + + break; + } + + case 4: // CMSG_BATTLEEND + { + if (p.msg.Size < 1) + return false; + + var os = new OctetsStream(p.msg); + + byte id = os.ReadByte(); + + for (int i = 0; i < info.Count; i++) + { + if (info[i].id == id) + { + strMsg = string.Format( + pGameUI.GetStringFromTable(4004), + info[i].name); + break; + } + } + + break; + } + + case 6: // CMSG_BONUSSEND + { + if (p.msg.Size < sizeof(uint) * 3) + return false; + + var os = new OctetsStream(p.msg); + + uint money = os.ReadUInt(); + uint id = os.ReadUInt(); + uint count = os.ReadUInt(); + + var item = CECIvtrItem.CreateItem(id, 0, count); + + strMsg = string.Format( + pGameUI.GetStringFromTable(4006), + item.GetName(), + item.GetCount()); + + break; + } + + case 7: // CMSG_SPECIAL + { + if (p.msg.Size < sizeof(short)) + return false; + + var os = new OctetsStream(p.msg); + + short id = os.ReadShort(); + + for (int i = 0; i < info.Count; i++) + { + if (info[i].id == id) + { + uint fid = info[i].id_owner; + + if (fid > 0) + { + var finfo = pMan.GetFaction(fid); + + if (finfo != null) + { + strMsg = string.Format( + pGameUI.GetStringFromTable(4007), + info[i].name, + info[i].name, + finfo.GetName()); + } + else if (pPendingFactions != null) + { + pPendingFactions.UniquelyAdd((int)fid); + } + } + else + { + strMsg = string.Format( + pGameUI.GetStringFromTable(4005), + info[i].name); + } + + break; + } + } + + break; + } + + default: + return false; + } + + if ((pPendingFactions != null && pPendingFactions.GetSize() > 0) || string.IsNullOrEmpty(strMsg)) + { + return false; + } + + g_pGame.GetGameRun().AddChatMessage( + strMsg, + ChatChannel.GP_CHAT_BROADCAST, + p.srcroleid, + null, + 1, + p.emotion);*/ + + return true; + + } + + 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; + } + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs.meta b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs.meta new file mode 100644 index 0000000000..e9f0f60376 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_GameSession.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7c774ca3c7d345e4ea0fbb7397bf1d88 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs b/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs new file mode 100644 index 0000000000..678b0d3f6b --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace BrewMonster.Scripts.Chat +{ + public static class Chat_Helper + { + public static Color32 ToColor32(uint c) + { + byte a = (byte)((c >> 24) & 0xFF); + byte r = (byte)((c >> 16) & 0xFF); + byte g = (byte)((c >> 8) & 0xFF); + byte b = (byte)(c & 0xFF); + + return new Color32(r, g, b, a); + } + } +} diff --git a/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs.meta b/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs.meta new file mode 100644 index 0000000000..5cea223658 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/Chat_Helper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5ef4426a7625eb74aa802808e2223421 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/TestChat.cs b/Assets/PerfectWorld/Scripts/Chat/TestChat.cs new file mode 100644 index 0000000000..2b9f6d00e5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/TestChat.cs @@ -0,0 +1,23 @@ +using UnityEngine; + +namespace BrewMonster.Scripts.Chat +{ + public class TestChat : MonoSingleton + { + public ChatInputHandler testChat; + public string text; + protected override void Awake() + { + base.Awake(); + DontDestroyOnLoad(gameObject); + } + + private void Update() + { + if (Input.GetKeyDown(KeyCode.C)) + { + testChat.Send(text); + } + } + } +} \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Chat/TestChat.cs.meta b/Assets/PerfectWorld/Scripts/Chat/TestChat.cs.meta new file mode 100644 index 0000000000..f858494c50 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Chat/TestChat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 71c6bd36af2b4627838a10eb95f147b6 +timeCreated: 1772782953 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs index cb6419e427..bd8b9b3ec1 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECNPC.cs @@ -5,6 +5,7 @@ using CSNetwork.GPDataType; using ModelRenderer.Scripts.Common; using System; using System.Threading.Tasks; +using BrewMonster.Scripts.Chat; using UnityEngine; public class CECNPC : CECObject @@ -1600,6 +1601,34 @@ public class CECNPC : CECObject // Get NPC name color public virtual uint GetNameColor() { return 0xffffff00; } + + CECPateText m_pPateLastWords1; + CECPateText m_pPateLastWords2; + public void SetLastSaidWords(string words, int timeShow = -1) + { + if (m_pPateLastWords1 == null) + { + m_pPateLastWords1 = new CECPateText(); + } + + if (m_pPateLastWords2 == null) + { + m_pPateLastWords2 = new CECPateText(); + } + + if (words == null) + return; + + int len1 = m_pPateLastWords1.SetText(words, true, out string strWords, false, null); + + /* + if (len1 < strWords.Length) + m_pPateLastWords2.SetText(strWords.Substring(len1), true, true); + else + m_pPateLastWords2.Clear(); + + m_iLastSayCnt = timeShow > 0 ? timeShow : 20000;*/ + } } public struct ClearComActFlagEvent { diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs index 9bb345d99f..9262f072c3 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs @@ -1,11 +1,33 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using CSNetwork; namespace CSNetwork { public static class AUICommon { + public const char AUICOMMON_ITEM_CODE_START = (char)0xE000; + public const char AUICOMMON_ITEM_CODE_END = (char)0xE3FF; + public const int AUIMANAGER_MAX_EMOTIONGROUPS = 32; + + public const int MAX_EDITBOX_ITEM_NUM = + AUICOMMON_ITEM_CODE_END - AUICOMMON_ITEM_CODE_START + 1; + + public const int MAXNUM_CUSTOM_ITEM = 255; + + public enum EditboxItemType + { + enumEIEmotion = 0, + enumEIIvtrlItem, + enumEICoord, + enumEIImage, + enumEIScriptItem, + enumEICustom, + enumEINum = enumEICustom + MAXNUM_CUSTOM_ITEM + } + public static void AUI_ConvertChatString(string pszChat, char[] pszConv, bool bName) { if (string.IsNullOrEmpty(pszChat) || pszConv == null) @@ -49,5 +71,736 @@ namespace CSNetwork pszConv[nLen] = '\0'; } + public static string FilterEmotionSet(string szText, int nEmotionSet) + { + EditBoxItemsSet itemsSet = new EditBoxItemsSet(); + + string strOrgText = UnmarshalEditBoxText(szText, itemsSet); + + int nCount = itemsSet.GetItemCount(); + + if (nCount == 0) + return szText; + + var it = itemsSet.GetItemIterator(); + + for (int i = 0; i < nCount; i++) + { + EditBoxItemBase pItem = it.Current.Value; + + if (pItem.GetType() == EditboxItemType.enumEIEmotion) + { + int nSet = 0; + int nIndex = 0; + + UnmarshalEmotionInfo(pItem.GetInfo(), ref nSet, ref nIndex); + + pItem.SetInfo(MarshalEmotionInfo(nEmotionSet, nIndex)); + } + + it.MoveNext(); + } + + return MarshalEditBoxText(strOrgText, itemsSet); + } + + public static string MarshalEmotionInfo(int nEmotionSet, int nIndex) + { + return nEmotionSet.ToString() + ":" + nIndex.ToString(); + } + + public static void UnmarshalEmotionInfo(string szText, ref int nEmotionSet, ref int nIndex) + { + if (string.IsNullOrEmpty(szText)) + return; + + var parts = szText.Split(':'); + + if (parts.Length >= 2) + { + int.TryParse(parts[0], out nEmotionSet); + int.TryParse(parts[1], out nIndex); + } + + // a_ClampFloor(nEmotionSet, 0); + if (nEmotionSet < 0) + nEmotionSet = 0; + + // a_ClampFloor(nIndex, 0); + if (nIndex < 0) + nIndex = 0; + + if (nEmotionSet >= AUIMANAGER_MAX_EMOTIONGROUPS) + nEmotionSet = 0; + } + + public static string MarshalEditBoxText(string text, EditBoxItemsSet itemsSet) + { + var sb = new StringBuilder(text.Length * 2); + + int start = 0; + + for (int i = 0; i < text.Length; i++) + { + char ch = text[i]; + + if (IsEditboxItemCode(ch)) + { + sb.Append(text, start, i - start + 1); + + var item = itemsSet.GetItemByChar(ch); + if (item != null) + sb.Append(item.Serialize()); + + start = i + 1; + } + } + + if (start < text.Length) + sb.Append(text, start, text.Length - start); + + return sb.ToString(); + } + + public static bool IsEditboxItemCode(char ch) + { + return ch >= AUICOMMON_ITEM_CODE_START && ch <= AUICOMMON_ITEM_CODE_END; + } + + public static string UnmarshalEditBoxText(string szText, EditBoxItemsSet itemsSet) + { + return UnmarshalEditBoxText( + szText, + itemsSet, + 0, + null, + 0, + 0, + null, + 0, + false, + false, + 0 + ); + } + + public static string UnmarshalEditBoxText( + string? sztext, + EditBoxItemsSet itemsSet, + int msgIndex, + string ivtrItem, + uint clIvtrItem, + int itemMask, + EditboxScriptItem[]? scriptItems, + int scriptItemCount, + bool underLine, + bool sameColor, + uint clUnderLine) + { + if (sztext == null) + return ""; + + var scriptInfo = new AUI_UNMARSH_SCRIPTITEM_INFO + { + ScriptItems = scriptItems, + ScriptItemCount = scriptItemCount + }; + + var underlineInfo = new AUI_UNMARSH_UNDERLINE_INFO + { + UnderLine = underLine, + SameColor = sameColor, + UnderLineColor = clUnderLine + }; + + return UnmarshalEditBoxTextEx( + sztext, + itemsSet, + msgIndex, + ivtrItem, + clIvtrItem, + itemMask, + scriptInfo, + underlineInfo + ); + } + + public static string UnmarshalEditBoxTextEx( + string text, + EditBoxItemsSet itemsSet, + int msgIndex, + string ivtrItem, + uint clIvtrItem, + int itemMask, + AUI_UNMARSH_SCRIPTITEM_INFO? scriptInfo, + AUI_UNMARSH_UNDERLINE_INFO? underlineInfo) + { + if (text == null) + return ""; + + int start = 0; + int i = 0; + int curScriptIndex = 0; + + var sb = new StringBuilder(); + + while (i < text.Length) + { + char ch = text[i]; + + if (IsEditboxItemCode(ch)) + { + if (i > start) + sb.Append(text, start, i - start); + + i++; + + EditBoxItemBase? item = EditBoxItemBase.Unserialize(text, ref i); + start = i; + + if (item != null) + { + if ((itemMask & (1 << (int)item.GetType())) != 0) + { + char newChar = itemsSet.AppendItem(item); + + if (newChar != '\0') + { + sb.Append(newChar); + + item.SetMsgIndex(msgIndex); + + if (underlineInfo != null) + { + item.SetUnderLine( + underlineInfo.UnderLine, + underlineInfo.SameColor, + underlineInfo.UnderLineColor); + } + + switch (item.GetType()) + { + case EditboxItemType.enumEIIvtrlItem: + + item.SetName(ivtrItem); + item.SetColor(clIvtrItem); + break; + + case EditboxItemType.enumEIScriptItem: + + if (scriptInfo != null && + curScriptIndex < scriptInfo.ScriptItemCount) + { + var sItem = scriptInfo.ScriptItems![curScriptIndex]; + + item.SetName(sItem.Name); + item.SetColor(sItem.Color); + + var data = sItem.Name; + if (data != null) + item.SetExtraData(sItem.Data, sItem.GetDataSize()); + + curScriptIndex++; + } + + break; + } + } + } + } + } + else + { + i++; + } + } + + if (i > start) + sb.Append(text, start, i - start); + + return sb.ToString(); + } + } +} + +public class AUI_UNMARSH_SCRIPTITEM_INFO +{ + public EditboxScriptItem[]? ScriptItems; + public int ScriptItemCount; +} + +public class AUI_UNMARSH_UNDERLINE_INFO +{ + public bool UnderLine; + public bool SameColor; + public uint UnderLineColor; +} + +public class EditBoxItemsSet +{ + const char AUICOMMON_ITEM_CODE_START = '\u0001'; + const char AUICOMMON_ITEM_CODE_END = '\u0010'; + protected Dictionary m_Items = new(); + protected int[] m_ItemsCount = new int[(int)AUICommon.EditboxItemType.enumEINum]; + protected char m_cNextItemChar; + + public EditBoxItemsSet() + { + Array.Clear(m_ItemsCount, 0, m_ItemsCount.Length); + m_cNextItemChar = AUICommon.AUICOMMON_ITEM_CODE_START; + } + + public EditBoxItemsSet(EditBoxItemsSet itemsset) + { + this.Assign(itemsset); + } + + public void Assign(EditBoxItemsSet src) + { + m_Items.Clear(); + + foreach (var kv in src.m_Items) + { + m_Items[kv.Key] = new EditBoxItemBase(kv.Value); + } + + Array.Copy(src.m_ItemsCount, m_ItemsCount, m_ItemsCount.Length); + m_cNextItemChar = src.m_cNextItemChar; + } + + public void Release() + { + m_Items.Clear(); + + Array.Clear(m_ItemsCount, 0, m_ItemsCount.Length); + + m_cNextItemChar = AUICommon.AUICOMMON_ITEM_CODE_START; + } + + public int GetItemCount() + { + return m_Items.Count; + } + + public Dictionary.Enumerator GetItemIterator() + { + return m_Items.GetEnumerator(); + } + + public EditBoxItemBase? GetItemByChar(char ch) + { + if (!IsEditboxItemCode(ch)) + throw new Exception("Invalid editbox item char"); + + if (m_Items.TryGetValue(ch, out var item)) + return item; + + return null; + } + + public bool IsEditboxItemCode(char ch) + { + return ch >= AUICOMMON_ITEM_CODE_START && ch <= AUICOMMON_ITEM_CODE_END; + } + + public int GetItemCountByType(AUICommon.EditboxItemType type) + { + return m_ItemsCount[(int)type]; + } + + public void DelItemByChar(char ch) + { + if (m_Items.TryGetValue(ch, out var item)) + { + m_ItemsCount[(int)item.GetType()]--; + m_Items.Remove(ch); + } + } + + public char AppendItem(EditBoxItemBase pItem) + { + if (m_Items.Count >= AUICommon.MAX_EDITBOX_ITEM_NUM) + return '\0'; + + char cur = m_cNextItemChar; + + do + { + if (m_Items.ContainsKey(m_cNextItemChar)) + { + m_cNextItemChar = EditboxGetNextChar(m_cNextItemChar); + } + else + { + m_Items[m_cNextItemChar] = pItem; + m_ItemsCount[(int)pItem.GetType()]++; + + char ret = m_cNextItemChar; + m_cNextItemChar = EditboxGetNextChar(m_cNextItemChar); + + return ret; + } + } while (m_cNextItemChar != cur); + + return '\0'; + } + + public char EditboxGetNextChar(char cur) + { + if (cur >= AUICOMMON_ITEM_CODE_END) + return AUICOMMON_ITEM_CODE_START; + else + return (char)(cur + 1); + } + + public char AppendItem(AUICommon.EditboxItemType type, uint cl, string szName, string szInfo) + { + // Implement theo logic C++ gốc + throw new NotImplementedException(); + } + + public int GetTotalExtraDataSize() + { + int sz = 0; + + foreach (var item in m_Items.Values) + { + sz += item.GetExtraDataSize(); + } + + return sz; + } +} + +public class EditBoxItemBase +{ + protected AUICommon.EditboxItemType m_type; + protected uint m_dwColor; + protected string m_strName = ""; + protected string m_strInfo = ""; + + protected int m_nMsgIndex; + protected int m_nImageIndex; + protected int m_nImageFrame; + protected float m_fImageScale; + + protected byte[]? m_pExtraData; + protected int m_uExtraDataSize; + + protected bool m_bUnderLine; + protected bool m_bSameColor; + protected uint m_dwUnderLineColor; + + protected static EditBoxItemBase?[] m_mapCustomType = new EditBoxItemBase[AUICommon.MAXNUM_CUSTOM_ITEM]; + + public EditBoxItemBase(AUICommon.EditboxItemType type) + { + m_type = type; + m_dwColor = 0xffffffff; + + m_nMsgIndex = 0; + m_nImageIndex = 0; + m_nImageFrame = 0; + m_fImageScale = 1.0f; + + m_bUnderLine = false; + m_bSameColor = true; + m_dwUnderLineColor = 0; + + RegisterCustomType(type); + } + + public EditBoxItemBase(EditBoxItemBase src) + { + Assign(src); + } + + public bool UnserializeContent(string text, ref int index) + { + int szNext; + int szEnd = text.IndexOf("<", index); + + if (szEnd == -1) + return false; + + if (m_type == AUICommon.EditboxItemType.enumEICoord) + { + szNext = szEnd + 1; + + if (!TryParseInt(text, szNext, out int color)) + return false; + + SetColor((uint)color); + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szNext = szEnd + 2; + + if (!TryParseInt(text, szNext, out int underline)) + return false; + + m_bUnderLine = underline != 0; + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szNext = szEnd + 2; + + if (!TryParseInt(text, szNext, out int underlineColor)) + return false; + + m_dwUnderLineColor = (uint)underlineColor; + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + m_bSameColor = (m_dwUnderLineColor == m_dwColor); + + szNext = szEnd + 2; + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + SetName(text.Substring(szNext, szEnd - szNext)); + + szEnd += 1; + } + else if (m_type == AUICommon.EditboxItemType.enumEIImage) + { + szNext = szEnd + 1; + + if (!TryParseUInt(text, szNext, out uint color)) + return false; + + SetColor(color); + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szNext = szEnd + 2; + + if (!TryParseInt(text, szNext, out int imageIndex)) + return false; + + SetImageIndex(imageIndex); + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szNext = szEnd + 2; + + if (!TryParseInt(text, szNext, out int frame)) + return false; + + SetImageFrame(frame); + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szNext = szEnd + 2; + + if (!float.TryParse(text.Substring(szNext), out float f)) + return false; + + SetImageScale(f); + + szEnd = text.IndexOf("><", szNext); + if (szEnd == -1) return false; + + szEnd += 1; + } + else if (m_type == AUICommon.EditboxItemType.enumEIEmotion) + { + SetName("W"); + } + + szNext = szEnd + 1; + + szEnd = text.IndexOf('>', szNext); + + if (szEnd == -1) + return false; + + SetInfo(text.Substring(szNext, szEnd - szNext)); + + index = szEnd + 1; + + return true; + } + + private bool TryParseInt(string text, int start, out int value) + { + int end = start; + + while (end < text.Length && char.IsDigit(text[end])) + end++; + + return int.TryParse(text.Substring(start, end - start), out value); + } + + private bool TryParseUInt(string text, int start, out uint value) + { + int end = start; + + while (end < text.Length && char.IsDigit(text[end])) + end++; + + return uint.TryParse(text.Substring(start, end - start), out value); + } + + protected void RegisterCustomType(AUICommon.EditboxItemType type) + { + if (type >= AUICommon.EditboxItemType.enumEICustom && + type < AUICommon.EditboxItemType.enumEINum) + { + int index = (int)type - (int)AUICommon.EditboxItemType.enumEICustom; + + if (m_mapCustomType[index] == null) + m_mapCustomType[index] = this; + } + } + + protected virtual EditBoxItemBase Create() + { + return new EditBoxItemBase(m_type); + } + + protected static EditBoxItemBase? GetCustomItemFromType(AUICommon.EditboxItemType type) + { + if (type >= AUICommon.EditboxItemType.enumEICustom && + type < AUICommon.EditboxItemType.enumEINum) + { + return m_mapCustomType[(int)type - (int)AUICommon.EditboxItemType.enumEICustom]; + } + + return null; + } + + public uint GetColor() => m_dwColor; + public AUICommon.EditboxItemType GetType() => m_type; + public string GetName() => m_strName; + public string GetInfo() => m_strInfo; + public int GetMsgIndex() => m_nMsgIndex; + public int GetImageIndex() => m_nImageIndex; + public int GetImageFrame() => m_nImageFrame; + public float GetImageScale() => m_fImageScale; + public bool GetUnderLine() => m_bUnderLine; + public bool GetSameColor() => m_bSameColor; + public uint GetUnderLineColor() => m_dwUnderLineColor; + + public void SetColor(uint cl) => m_dwColor = cl; + public void SetName(string name) => m_strName = name; + public void SetInfo(string info) => m_strInfo = info; + public void SetMsgIndex(int n) => m_nMsgIndex = n; + public void SetImageIndex(int n) => m_nImageIndex = n; + public void SetImageFrame(int n) => m_nImageFrame = n; + public void SetImageScale(float f) => m_fImageScale = f; + + public byte[]? GetExtraData() => m_pExtraData; + public int GetExtraDataSize() => m_uExtraDataSize; + + public void SetUnderLine(bool underline, bool sameColor = true, uint underlineColor = 0) + { + m_bUnderLine = underline; + m_bSameColor = sameColor; + m_dwUnderLineColor = underlineColor; + } + + public void SetExtraData(byte[] data, int size) + { + m_pExtraData = new byte[size]; + Array.Copy(data, 0, m_pExtraData, 0, size); + m_uExtraDataSize = size; + } + + public virtual string Serialize() + { + return ""; + } + + public static EditBoxItemBase Unserialize(string text, ref int index) + { + int start = text.IndexOf('<', index); + if (start == -1) + return null; + + start++; + + int endType = text.IndexOf('>', start); + if (endType == -1) + return null; + + string typeStr = text.Substring(start, endType - start); + + if (!int.TryParse(typeStr, out int type)) + return null; + + if (type < 0 || type >= (int)AUICommon.EditboxItemType.enumEINum) + return null; + + index = endType + 1; + + EditBoxItemBase pItem = EditBoxItemBase.GetCustomItemFromType((AUICommon.EditboxItemType)type); + + EditBoxItemBase pItemNew; + + if (pItem == null) + { + pItemNew = new EditBoxItemBase((AUICommon.EditboxItemType)type); + } + else + { + pItemNew = pItem.Create(); + } + + if (!pItemNew.UnserializeContent(text, ref index)) + { + return null; + } + + return pItemNew; + } + + public void Assign(EditBoxItemBase src) + { + m_type = src.m_type; + m_dwColor = src.m_dwColor; + m_strName = src.m_strName; + m_strInfo = src.m_strInfo; + + m_nMsgIndex = src.m_nMsgIndex; + m_nImageIndex = src.m_nImageIndex; + m_nImageFrame = src.m_nImageFrame; + m_fImageScale = src.m_fImageScale; + + if (src.m_pExtraData != null) + { + SetExtraData(src.m_pExtraData, src.m_uExtraDataSize); + } + else + { + m_pExtraData = null; + m_uExtraDataSize = 0; + } + + RegisterCustomType(m_type); + } +} + +public class EditboxScriptItem +{ + public string Name { get; set; } = ""; + public uint Color { get; set; } + public byte[]? Data { get; private set; } + + public void SetData(byte[] data) + { + Data = data.ToArray(); // deep copy + } + + public int GetDataSize() + { + return Data?.Length ?? 0; } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index ea7eb1393e..1a042e82fd 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -14,9 +14,12 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using BrewMonster.Scripts; using BrewMonster.Scripts.Chat; using BrewMonster.Scripts.Managers; +using BrewMonster.Scripts.UI; using UnityEngine; +using CECPlayer = BrewMonster.CECPlayer; using CommandID = CSNetwork.GPDataType.CommandID; namespace CSNetwork @@ -1583,30 +1586,129 @@ namespace CSNetwork CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan(); chatmessage p = (chatmessage)pProtocol; - var channel = (ChatChannel)p.Channel; + //var channel = (ChatChannel)p.Channel; + Debug.Log("[Cuong] reciver chat channel: " + p.Channel); - int levelBlock = EC_Game.GetConfigs().GetBlackListSettings().levelBlock; - - if (p.Srclevel > 0 && p.Srclevel < levelBlock) + if (Chat_GameSession.ShouldBlockByLevel(p)) { - - if (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; - } - } + Debug.Log("[Cuong] 1"); + return true; } EC_IvtrItem pItem = CHAT_S2C.CreateChatItem(p.Data); - string strTemp = System.Text.Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length); + + string szMsg = null; + string strTemp = Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length); + //string strTemp = Encoding.Unicode.GetString(p.Msg.ToArray()); + string strMsgOrigion = strTemp; + // Todo: Show Text on Ui Game + //strTemp = pGameUI.FilterInvalidTags(strTemp, pItem==NULL); + if (!Chat_GameSession.PolicyResolver(pProtocol, p, ref strTemp)) + { + Debug.Log("[Cuong] 2"); + return false; + } - _logger.Log(LogType.Warning, $"HoangDev : OnPrtcChatMessage :{strTemp}"); - EventBus.Publish(new ChatMessageEvent(strTemp)); + /* + if (Chat_GameSession.HanldeGPChatSystem(p, bCalledagain)) + { + Debug.Log("[Cuong] 3"); + }*/ + + if (p.Channel is (byte)ChatChannel.GP_CHAT_BROADCAST + or (byte)ChatChannel.GP_CHAT_SYSTEM || p.Srcroleid == 0) + { + if (p.Channel == (byte)ChatChannel.GP_CHAT_SYSTEM && p.Srcroleid > 0) + { + Debug.Log("[Cuong] 4"); + } + else + { + Debug.Log("[Cuong] 5"); + EC_Game.GetGameRun().AddChatMessage(szMsg, p.Channel, p.Srcroleid, null, 0, p.Emotion); + } + }else if (p.Channel == (byte)ChatChannel.GP_CHAT_INSTANCE && p.Srcroleid == 1) + { + Debug.Log("[Cuong] 6"); + Chat_GameSession.AUICTranslate trans; + //EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().AddHeartBeatHint(trans.Translate(szMsg )); + } + else + { + Debug.Log("[Cuong] Other"); + CECStringTab pStrTab = EC_Game.GetFixedMsgs(); + + if (ISPLAYERID(p.Srcroleid)) + { + string szName = EC_Game.GetGameRun().GetPlayerName(p.Srcroleid, false); + Debug.Log("[Cuong] Other Msg" + szName); + if (szName == null) + { + if (!bCalledagain) + { + Chat_GameSession.AddElemForPendingProtocols(pProtocol); + Chat_GameSession.AddChatPlayerID(p.Srcroleid); + } + return false; + } + else + { + char[] szText = new char[80]; + AUICommon.AUI_ConvertChatString(szName, szText, false); + + string str = string.Format( + pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT), + szName, + szMsg + ); + EC_Game.GetGameRun().AddChatMessage(str, p.Channel, p.Srcroleid, + null, 0, p.Emotion, pItem, strMsgOrigion); + + // Set player's last said words + CECPlayer pPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(p.Srcroleid); + if (pPlayer != null) + { + if (p.Channel == (byte)ChatChannel.GP_CHAT_SUPERFARCRY) { + if (strTemp.Length > 8) + strTemp = strTemp.Substring(0, strTemp.Length - 8); + else + strTemp = ""; + } + //pPlayer.SetLastSaidWords(strTemp, p.Emotion, pItem); + EventBus.Publish(new ChatMessageEvent(strTemp)); + } + } + } + else if(ISNPCID(p.Srcroleid)) + { + CECNPC pNPC = EC_Game.GetGameRun().GetWorld().GetNPCMan().GetNPC(p.Srcroleid); + + if (pNPC) + { + string str; + string template = pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT2); + + string message = string.Format( + template, + pNPC.GetName(), + szMsg + ); + + EC_Game.GetGameRun().AddChatMessage( + message, + p.Channel, + p.Srcroleid, + null, + 0, + p.Emotion + ); + + CECUIHelper.RemoveNameFlagFromNPCChat(strTemp, out szMsg); + + pNPC.SetLastSaidWords(szMsg); + } + } + } return true; } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs index d040e6e43c..c9a5e7a63a 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs @@ -364,7 +364,14 @@ namespace BrewMonster.UI } m_pDlgPetList.OnInitDialog(); } + + /*CDlgPopMsg m_pDlgPopMsg; + void AddHeartBeatHint(string pszMsg) + { + m_pDlgPopMsg->Add(pszMsg); + }*/ } + public enum EC_GAMEUI_ICONS : byte { ICONS_ACTION = 0, diff --git a/Assets/Scripts/CECGameRun.cs b/Assets/Scripts/CECGameRun.cs index 54f0468a02..678345d06e 100644 --- a/Assets/Scripts/CECGameRun.cs +++ b/Assets/Scripts/CECGameRun.cs @@ -926,6 +926,19 @@ public partial class CECGameRun } public CECPendingActionArray GetPendingLogOut(){ return m_pendingLogout; } + + Dictionary m_ID2NameTab = new(); + + public string GetPlayerName(int cid, bool autoGen) + { + if (m_ID2NameTab.TryGetValue(cid, out var name)) + return name; + + if (autoGen) + return $"P-{cid}"; + + return null; + } } public enum GameState diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 18f7e7f761..568607a111 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -6,6 +6,7 @@ using TMPro; using BrewMonster.Network; using CSNetwork; using CSNetwork.GPDataType; +using EditorAttributes; public class ChatInputHandler : MonoSingleton { @@ -32,10 +33,11 @@ public class ChatInputHandler : MonoSingleton // Khi nhấn Enter private void OnSubmit(GameSession.ChatMessageEvent text) { - if (!string.IsNullOrWhiteSpace(text.context)) + Debug.Log("[Cuong] receiver chat:" + text); + /*if (!string.IsNullOrWhiteSpace(text.context)) { HandleUserInput(text.context); - } + }*/ } // Khi nhấn nút Gửi @@ -60,11 +62,14 @@ public class ChatInputHandler : MonoSingleton inputField.ActivateInputField(); // focus lại để tiếp tục gõ } - private void Send(string text) + [Button] + public void Send(string text) { Debug.Log("Người dùng vừa gõ: " + text); // TODO: xử lý text (ví dụ: thêm vào chat box, gửi network,...) UnityGameSession.SendChatData((byte)ChatChannel.GP_CHAT_LOCAL, text,0,0); + Debug.Log("[Cuong] send ChatChannel:" + (byte)ChatChannel.GP_CHAT_LOCAL); + Debug.Log("[Cuong] send ChatChannel:" + ChatChannel.GP_CHAT_LOCAL); } }