From b5ec2145c1c49daa871b513776aed54ffd6a94d3 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Mon, 22 Jul 2024 22:22:57 +1000 Subject: [PATCH] fix: censor abbreviated player names in screenshot mode Best effort. --- ChatTwo/GameFunctions/Chat.cs | 46 +++++++++++++++++++++++++++++++++++ ChatTwo/Ui/ChatLogWindow.cs | 24 +++++++++++++++--- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/ChatTwo/GameFunctions/Chat.cs b/ChatTwo/GameFunctions/Chat.cs index 626f11f..d6a5c78 100755 --- a/ChatTwo/GameFunctions/Chat.cs +++ b/ChatTwo/GameFunctions/Chat.cs @@ -3,6 +3,7 @@ using ChatTwo.Code; using ChatTwo.GameFunctions.Types; using ChatTwo.Resources; using ChatTwo.Util; +using Dalamud.Game.Config; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Hooking; using Dalamud.Memory; @@ -57,6 +58,17 @@ internal sealed unsafe class Chat : IDisposable internal bool UsesTellTempChannel { get; set; } internal InputChannel? PreviousChannel { get; private set; } + private enum PlayerNameDisplayType : uint + { + FullName = 0, + SurnameAbbreviated = 1, + ForenameAbbreviated = 2, + Initials = 3 + } + + private long LastPlayerNameDisplayTypeRefresh; + private PlayerNameDisplayType CurrentPlayerNameDisplayType = PlayerNameDisplayType.FullName; + internal Chat(Plugin plugin) { Plugin = plugin; @@ -444,4 +456,38 @@ internal sealed unsafe class Chat : IDisposable return wasValid; } + + private PlayerNameDisplayType GetNameDisplayType() + { + var ok = Plugin.GameConfig.TryGet(UiConfigOption.LogNameType, out uint type); + if (!ok || !Enum.IsDefined(typeof(PlayerNameDisplayType), type)) + return PlayerNameDisplayType.FullName; + return (PlayerNameDisplayType) type; + } + + internal string AbbreviatePlayerName(string playerName) + { + if (LastPlayerNameDisplayTypeRefresh + 5 * 1000 < Environment.TickCount64) + { + LastPlayerNameDisplayTypeRefresh = Environment.TickCount64; + CurrentPlayerNameDisplayType = GetNameDisplayType(); + } + + if (CurrentPlayerNameDisplayType == PlayerNameDisplayType.FullName) + return playerName; + + var split = playerName.Split(' '); + if (split.Length != 2) + return playerName; + return CurrentPlayerNameDisplayType switch + { + PlayerNameDisplayType.SurnameAbbreviated => + $"{split.First()} {split.Last().FirstOrDefault('A')}.", + PlayerNameDisplayType.ForenameAbbreviated => + $"{split.First().FirstOrDefault('A')}. {split.Last()}", + PlayerNameDisplayType.Initials => + $"{split.First().FirstOrDefault('A')}. {split.Last().FirstOrDefault('A')}.", + _ => playerName + }; + } } diff --git a/ChatTwo/Ui/ChatLogWindow.cs b/ChatTwo/Ui/ChatLogWindow.cs index 344b7a9..4be04d0 100644 --- a/ChatTwo/Ui/ChatLogWindow.cs +++ b/ChatTwo/Ui/ChatLogWindow.cs @@ -546,6 +546,8 @@ public sealed class ChatLogWindow : Window { var playerName = TellTarget.Name; if (ScreenshotMode) + // Note: don't use HidePlayerInString here because + // abbreviation settings do not affect this. playerName = HashPlayer(TellTarget.Name, TellTarget.World); var world = WorldSheet.GetRow(TellTarget.World)?.Name?.RawString ?? "???"; @@ -594,6 +596,8 @@ public sealed class ChatLogWindow : Window { if (!string.IsNullOrWhiteSpace(tellPlayerName) && tellWorldId != 0) { + // Note: don't use HidePlayerInString here because + // abbreviation settings do not affect this. var playerName = HashPlayer(tellPlayerName, tellWorldId); var world = WorldSheet.GetRow(tellWorldId)?.Name?.RawString ?? "???"; @@ -1648,10 +1652,14 @@ public sealed class ChatLogWindow : Window var content = text.Content ?? ""; if (ScreenshotMode) { - if (chunk.Link is PlayerPayload playerPayload && content.Contains(playerPayload.PlayerName)) - content = content.Replace(playerPayload.PlayerName, HashPlayer(playerPayload.PlayerName, playerPayload.World.RowId)); - else if (Plugin.ClientState.LocalPlayer is { } player && content.Contains(player.Name.TextValue)) - content = content.Replace(player.Name.TextValue, HashPlayer(player.Name.TextValue, player.HomeWorld.Id)); + // TODO: the result of hashing the player name should be cached. + // Currently we recalculate the abbreviated player names, the + // hashes and the string replacements every frame when they + // never change. + if (chunk.Link is PlayerPayload playerPayload) + content = HidePlayerInString(content, playerPayload.PlayerName, playerPayload.World.RowId); + else if (Plugin.ClientState.LocalPlayer is { } player) + content = HidePlayerInString(content, player.Name.TextValue, player.HomeWorld.Id); } if (wrap) @@ -1683,6 +1691,14 @@ public sealed class ChatLogWindow : Window ImGui.Image(FontIcon.ImGuiHandle, size, uv0, uv1); ImGuiUtil.PostPayload(chunk, handler); + + } + + private string HidePlayerInString(string str, string playerName, uint worldId) + { + var expected = Plugin.Functions.Chat.AbbreviatePlayerName(playerName); + var hash = HashPlayer(playerName, worldId); + return str.Replace(playerName, expected).Replace(expected, hash); } private string HashPlayer(string playerName, uint worldId)