Files
test/Assets/PerfectWorld/Scripts/Chat/ChatWireTmpCodec.cs
T
2026-04-09 16:33:52 +07:00

123 lines
4.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Text;
using System.Text.RegularExpressions;
using CSNetwork;
namespace BrewMonster.Scripts.Chat
{
/// <summary>
/// Chat 输入/协议:wireMarshalEditBoxText)↔ TMP &lt;sprite&gt; 显示。
/// Chat input/protocol: wire (MarshalEditBoxText) ↔ TMP &lt;sprite&gt; display.
/// </summary>
public static class ChatWireTmpCodec
{
private static readonly Regex SpriteTagRegex = new Regex(@"<sprite\s[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex OrphanSpriteFragmentRegex = new Regex(@"^\s*sprite\s[^>]*>", RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// Tạo một đoạn wire marshal cho một emotion (set:index) — gửi server đúng protocol.
/// Build one marshaled wire segment for a single emotion (set:index) — correct server protocol.
/// </summary>
public static string BuildMarshaledEmotionWire(int emotionSet, int emotionIndex)
{
var item = new EditBoxItemBase(AUICommon.EditboxItemType.enumEIEmotion);
item.SetName("W");
item.SetInfo(AUICommon.MarshalEmotionInfo(emotionSet, emotionIndex));
var items = new EditBoxItemsSet();
char c = items.AppendItem(item);
if (c == '\0')
return "";
string display = c.ToString();
return AUICommon.MarshalEditBoxText(display, items);
}
/// <summary>
/// TMP 正文(无频道前缀)→ wire marshal(用于发送)。
/// TMP body text (no channel prefix) → marshaled wire (for sending).
/// </summary>
public static string TmpBodyToWire(string tmpBody, IEmotionSpriteMap map)
{
if (string.IsNullOrEmpty(tmpBody))
return "";
if (map == null)
return tmpBody;
var sb = new StringBuilder(tmpBody.Length);
int last = 0;
foreach (Match m in SpriteTagRegex.Matches(tmpBody))
{
AppendSanitizedPlainText(sb, tmpBody, last, m.Index - last);
string tag = m.Value;
if (TryMatchSpriteTagToEmotion(map, tag, out int es, out int ei))
sb.Append(BuildMarshaledEmotionWire(es, ei));
else
sb.Append(tag);
last = m.Index + m.Length;
// Defensive: TMP_InputField can transiently expose an orphan fragment like
// `sprite anim="..."` right after a valid <sprite ...> tag when input updates race.
// Skip it so we do not leak malformed rich-text into wire text.
var orphan = OrphanSpriteFragmentRegex.Match(tmpBody, last);
if (orphan.Success)
last += orphan.Length;
}
AppendSanitizedPlainText(sb, tmpBody, last, tmpBody.Length - last);
return sb.ToString();
}
private static void AppendSanitizedPlainText(StringBuilder sb, string source, int start, int length)
{
if (length <= 0)
return;
int end = start + length;
for (int i = start; i < end; i++)
{
char ch = source[i];
if (!AUICommon.IsEditboxItemCode(ch))
sb.Append(ch);
}
}
/// <summary>
/// Khớp tag với EmotionTMPTagBuilder — duyệt (set,index) đủ nhỏ.
/// Match tag to EmotionTMPTagBuilder output — brute-force over (set,index) within reasonable bounds.
/// </summary>
public static bool TryMatchSpriteTagToEmotion(IEmotionSpriteMap map, string spriteTag, out int emotionSet, out int emotionIndex)
{
emotionSet = 0;
emotionIndex = 0;
if (map == null || string.IsNullOrEmpty(spriteTag))
return false;
string normalized = spriteTag.Trim();
for (int s = 0; s < AUICommon.AUIMANAGER_MAX_EMOTIONGROUPS; s++)
{
for (int e = 0; e < 512; e++)
{
if (!EmotionTMPTagBuilder.TryBuildEmotionTag(map, s, e, out string built))
continue;
if (built == normalized)
{
emotionSet = s;
emotionIndex = e;
return true;
}
}
}
return false;
}
/// <summary>
/// Wire → TMP 富文本(FilterEmotionSet + Unmarshal + ConvertInlineItemsToTmp)。
/// Wire → TMP rich text (FilterEmotionSet + Unmarshal + ConvertInlineItemsToTmp).
/// </summary>
public static string WireBodyToTmpForDisplay(string wireBody, IEmotionSpriteMap map, int cEmotion)
{
return ChatEmotionDisplayPipeline.ConvertWireBodyToTmpDisplay(wireBody, map, cEmotion);
}
}
}