fix: Support hex RGB colors (#RRGGBB) in chat messages (fixes #2054)

Minecraft 1.16+ servers can send custom hex colors in JSON text
components ("color": "#RRGGBB"). MCC's ChatParser.Color2tag() only
recognized the 16 named colors, silently dropping hex values and
leaving gradient/custom-colored chat as uncolored plain text.

- ChatParser: recognize hex color values and emit internal §#rrggbb
  encoding; extend ColorCodeRegex to match the new format
- ClassicConsoleBackend: resolve §#rrggbb to ANSI escape codes via
  ColorHelper before passing to ConsoleInteractive (adapts to the
  configured ConsoleColorMode: 24-bit, 8-bit, 4-bit, or disable)
- McColorParser (TUI): parse §#rrggbb into exact SolidColorBrush
  for full RGB fidelity in Avalonia
- ChatBot.GetVerbatim(): skip the full 8-char §#rrggbb sequence
  instead of only 2 chars, preventing hex digits from leaking into
  stripped text

Made-with: Cursor
This commit is contained in:
BruceChen 2026-04-06 01:18:05 +08:00
parent 914c56fc6e
commit 7fd70ccf38
4 changed files with 80 additions and 6 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Media;
@ -63,6 +64,19 @@ namespace MinecraftClient.Tui
if (i > start)
AddRun(tb, text[start..i], currentColor, bold, italic, underline, strikethrough);
if (text[i + 1] == '#' && i + 8 <= text.Length
&& TryParseHexColor(text.AsSpan(i + 2, 6), out var hexBrush))
{
currentColor = hexBrush;
bold = false;
italic = false;
underline = false;
strikethrough = false;
i += 7;
start = i + 1;
continue;
}
char code = char.ToLower(text[i + 1]);
if (ColorMap.TryGetValue(code, out var brush))
@ -108,6 +122,20 @@ namespace MinecraftClient.Tui
return tb;
}
private static bool TryParseHexColor(ReadOnlySpan<char> hex, out IBrush brush)
{
if (hex.Length == 6
&& byte.TryParse(hex[..2], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte r)
&& byte.TryParse(hex[2..4], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte g)
&& byte.TryParse(hex[4..6], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out byte b))
{
brush = new SolidColorBrush(Color.FromRgb(r, g, b));
return true;
}
brush = Brushes.White;
return false;
}
private static void AddRun(TextBlock tb, string text, IBrush color,
bool bold, bool italic, bool underline, bool strikethrough)
{