Compare commits

..

2 Commits

Author SHA1 Message Date
Yue-bin
de679f5dd1 📃 docs: v1.0.2 2025-05-25 02:10:36 +08:00
Yue-bin
1e9616d06d 🐞 fix: 修复了标签处理 2025-05-24 22:56:36 +08:00
4 changed files with 182 additions and 43 deletions

View File

@@ -14,11 +14,19 @@ public class CNText : BaseUnityPlugin
{
static ConfigEntry<bool> needReplaceTextComp;
static ConfigEntry<int> customTextFontSize;
static ConfigEntry<bool> removeUnsupportedTags;
static ConfigEntry<bool> closeUnclosedTags;
void Awake()
{
needReplaceTextComp = Config.Bind<bool>("OnlinePlayerUI", "NeedReplaceTextComp", true, "是否显示替换的头顶玩家名称文本组件");
customTextFontSize = Config.Bind<int>("OnlinePlayerUI", "CustomTextFontSize", 38, "替换后头顶玩家名称字号");
Harmony.CreateAndPatchAll(typeof(OnlinePlayerUIUpdatePatch));
removeUnsupportedTags = Config.Bind<bool>("OnlinePlayerUI", "RemoveUnsupportedTags", true, "是否移除不支持的标签");
closeUnclosedTags = Config.Bind<bool>("OnlinePlayerUI", "CloseUnclosedTags", true, "是否自动闭合未闭合的标签");
if (needReplaceTextComp.Value)
{
Harmony.CreateAndPatchAll(typeof(OnlinePlayerUIUpdatePatch));
}
}
static Font LoadCustomFont()
{
@@ -44,27 +52,6 @@ public class CNText : BaseUnityPlugin
return font;
}
static public string ReformatName(string name)
{
name = name.Replace("<#", "</Color><Color=#");
if (name.StartsWith("</Color>"))
{
name = name.Substring(8);
}
if (name.LastIndexOf("</Color>") < name.LastIndexOf("<Color=#"))
{
name += "</Color>";
}
if (name.IndexOf("</Color>") < name.LastIndexOf("<Color=#"))
{
name = name.Substring(0, name.IndexOf("</Color>")) + name.Substring(name.IndexOf("</Color>") + 8);
}
if (name.LastIndexOf("</I>") < name.LastIndexOf("<I>") || name.LastIndexOf("</i>") < name.LastIndexOf("<i>"))
{
name += "</i>";
}
return name;
}
[HarmonyPatch(typeof(OnlinePlayerUI), "Update")]
class OnlinePlayerUIUpdatePatch
{
@@ -100,17 +87,9 @@ public class CNText : BaseUnityPlugin
customText.alignment = TextAnchor.MiddleCenter;
}
customText.color = tmpPlayerText.color;
customText.text = ReformatName(tmpPlayerText.text);
if (needReplaceTextComp.Value)
{
tmpPlayerText.enabled = false;
customText.enabled = true;
}
else
{
tmpPlayerText.enabled = true;
customText.enabled = false;
}
customText.text = TMPToFontTextConverter.Convert(tmpPlayerText.text, removeUnsupportedTags.Value, closeUnclosedTags.Value);
tmpPlayerText.enabled = false;
customText.enabled = true;
customText.supportRichText = true;
}
}

153
TMPToFontTextConverter.cs Normal file
View File

@@ -0,0 +1,153 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class TMPToFontTextConverter
{
private static readonly Dictionary<string, string> ColorNameToHex = new Dictionary<string, string>()
{
{"white", "#FFFFFF"}, {"black", "#000000"},
{"red", "#FF0000"}, {"green", "#00FF00"},
{"blue", "#0000FF"}, {"yellow", "#FFFF00"},
{"cyan", "#00FFFF"}, {"magenta", "#FF00FF"},
{"gray", "#808080"}, {"grey", "#808080"}
};
public static string Convert(string input, bool removeUnsupportedTags = true, bool closeUnclosedTags = true)
{
var output = PreprocessColorTags(input);
output = ProcessColorTags(output);
output = RemoveUnsupportedTags(output, removeUnsupportedTags);
return closeUnclosedTags ? CloseUnclosedTags(output) : output;
}
private static string PreprocessColorTags(string input)
{
// 转换简写颜色标签格式(带#和不带#
return Regex.Replace(input, @"<(#?[0-9a-fA-F]{3,8})>", match =>
{
string hex = match.Groups[1].Value;
return $"<color={(hex.StartsWith("#") ? hex : "#" + hex)}>";
});
}
private static string ProcessColorTags(string input)
{
// 匹配所有颜色标签进行标准化处理
return Regex.Replace(input, @"<color=(.*?)>", match =>
{
string colorValue = match.Groups[1].Value.Trim();
// 处理颜色名称
if (ColorNameToHex.TryGetValue(colorValue.ToLower(), out var hex))
return $"<color={hex}>";
// 处理十六进制格式
if (colorValue.StartsWith("#"))
{
string cleanHex = colorValue.Replace("#", "");
return FormatHexColor(cleanHex);
}
// 处理RGBA格式
if (colorValue.StartsWith("rgba"))
return ConvertRGBA(colorValue);
return $"<color={colorValue}>"; // 保留原始格式
}, RegexOptions.IgnoreCase);
}
private static string FormatHexColor(string hex)
{
hex = hex.ToUpper();
switch (hex.Length)
{
case 3: // #RGB → RRGGBB
return $"<color=#{new string(hex[0], 2)}{new string(hex[1], 2)}{new string(hex[2], 2)}>";
case 6: // RRGGBB → RRGGBBFF
return $"<color=#{hex}FF>";
case 8: // RRGGBBAA → 保持原样
return $"<color=#{hex}>";
default: // 无效格式保持原样
return $"<color=#{hex}>";
}
}
private static string RemoveUnsupportedTags(string input, bool removeUnsupported)
{
if (!removeUnsupported) return input;
// 删除不支持的标签及其内容(保留内容)
string[] unsupportedTags = { "font", "s", "u", "align" };
foreach (var tag in unsupportedTags)
{
input = Regex.Replace(input, $@"<{tag}=.*?>", "", RegexOptions.IgnoreCase);
input = Regex.Replace(input, $@"<\/{tag}>", "", RegexOptions.IgnoreCase);
}
return input;
}
private static string CloseUnclosedTags(string input)
{
Stack<string> tagStack = new Stack<string>();
StringBuilder output = new StringBuilder();
int index = 0;
while (index < input.Length)
{
if (input[index] == '<')
{
int tagEnd = input.IndexOf('>', index);
if (tagEnd == -1) break;
string fullTag = input.Substring(index, tagEnd - index + 1);
bool isClosing = fullTag.StartsWith("</");
string tagType = Regex.Match(fullTag, @"</?(\w+)").Groups[1].Value.ToLower();
output.Append(fullTag);
// 处理标签栈
if (!isClosing && !tagType.EndsWith("/"))
{
tagStack.Push(tagType); // 记录基础标签类型
}
else if (isClosing)
{
// 正确匹配最近打开的标签
if (tagStack.Count > 0 && tagStack.Peek() == tagType)
tagStack.Pop();
}
index = tagEnd + 1;
}
else
{
output.Append(input[index++]);
}
}
// 补全剩余标签(保持打开顺序)
while (tagStack.Count > 0)
{
string tag = tagStack.Pop();
output.Append($"</{tag}>");
}
return output.ToString();
}
private static string ConvertRGBA(string rgba)
{
var matches = Regex.Matches(rgba, @"\d+\.?\d*");
if (matches.Count < 3) return "#FFFFFFFF";
StringBuilder hex = new StringBuilder("#");
for (int i = 0; i < 3; i++)
hex.Append((int.Parse(matches[i].Value) * 255).ToString("X2"));
hex.Append(matches.Count > 3 ?
((int)(float.Parse(matches[3].Value) * 255)).ToString("X2") : "FF");
return $"<color={hex}>";
}
}

View File

@@ -27,10 +27,16 @@
└─StreamingAssets
```
**其中 `StreamingAssets`为新增的目录**
**其中 `StreamingAssets`为新增的目录**
## 更新日志
### v1.0.2
fix:
- 标签解析,重构了标签转换器
### v1.0.1
fix:

View File

@@ -4,7 +4,7 @@
<TargetFramework>net35</TargetFramework>
<AssemblyName>stick.plugins.cntext</AssemblyName>
<Product>cntext</Product>
<Version>1.0.1</Version>
<Version>1.0.2</Version>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>latest</LangVersion>
<RestoreAdditionalProjectSources>
@@ -24,13 +24,14 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework.TrimEnd(`0123456789`))' == 'net'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.2"
PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp">
<HintPath>lib/Assembly-CSharp.dll</HintPath>
</Reference>
<Reference Include="Assembly-CSharp">
<HintPath>lib/Assembly-CSharp.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -39,13 +40,13 @@
</Reference>
</ItemGroup>
<ItemGroup>
<ItemGroup>
<Reference Include="UnityEngine.UI">
<HintPath>lib/UnityEngine.UI.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ItemGroup>
<Reference Include="TextMeshPro">
<HintPath>lib/TextMeshPro-1.0.55.56.0b9.dll</HintPath>
</Reference>