feat(chunk-renderer): add skeleton + per-ctor salt + player-hide helpers

Extracts v1.5.6's `ChatLogWindow.HidePlayerInString` / `HashPlayer` into
a standalone `Ui/Components/ChunkRenderer` class. C1 lands the skeleton
(ctor + DI-deps + salt + two pure helpers); C2 will add DrawChunks/DrawChunk
text-path; C3 will add DrawIcon + EmoteCache integration.

Salt is per-ctor random matching v1.5.6 session-random behavior — hashed
player names change every plugin reload to avoid stable cross-session
linkage (Spec §6.5 decision).

GameFunctions injected via ctor (not static Plugin.Functions) because
Plugin.Functions is a non-static internal property — injection is the
correct Components-layer pattern for this dependency.

Not yet DI-registered (Sub-Task F) and not yet consumed by MessageList
(Sub-Task H) — class compiles standalone.
This commit is contained in:
2026-05-27 08:19:08 +02:00
parent 94fdef38ad
commit 7e541ac842
@@ -0,0 +1,48 @@
using HellionChat.Themes;
using HellionChat.Util;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.Components;
internal sealed class ChunkRenderer
{
private readonly ThemeRegistry _themes;
private readonly FontManager _fonts;
private readonly ILogger<ChunkRenderer> _logger;
private readonly GameFunctions.GameFunctions _gameFunctions;
private readonly string _salt;
public ChunkRenderer(
ThemeRegistry themes,
FontManager fonts,
ILogger<ChunkRenderer> logger,
GameFunctions.GameFunctions gameFunctions
)
{
_themes = themes;
_fonts = fonts;
_logger = logger;
_gameFunctions = gameFunctions;
// Per-ctor random matches v1.5.6 ChatLogWindow behavior — hashed player
// names change every plugin reload to avoid stable cross-session linkage.
_salt = new Random().Next().ToString();
// Field references kept for C2 consumption; remove no-ops when DrawChunks lands.
_ = _themes;
_ = _fonts;
_ = _logger;
}
private string HidePlayerInString(string str, string playerName, uint worldId)
{
var expected = _gameFunctions.Chat.AbbreviatePlayerName(playerName);
var hash = HashPlayer(playerName, worldId);
return str.Replace(playerName, expected).Replace(expected, hash);
}
private string HashPlayer(string playerName, uint worldId)
{
var hashCode = $"{_salt}{playerName}{worldId}".GetHashCode();
return $"Player {hashCode:X8}";
}
}