From ef1ef4de5b9c89b8fbcdf96cbc808fe16db855e2 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 10 May 2022 23:48:01 -0400 Subject: [PATCH] feat: increase efficiency --- NominaOcculta/AppearanceRepository.cs | 74 +++++++++++++---------- NominaOcculta/Obscurer.cs | 86 +++++++++++++++------------ NominaOcculta/Util.cs | 28 +++------ 3 files changed, 99 insertions(+), 89 deletions(-) diff --git a/NominaOcculta/AppearanceRepository.cs b/NominaOcculta/AppearanceRepository.cs index be85101..eee310b 100755 --- a/NominaOcculta/AppearanceRepository.cs +++ b/NominaOcculta/AppearanceRepository.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using Dalamud; using Dalamud.Logging; +using FFXIVClientStructs.FFXIV.Client.Game.Object; using Lumina.Excel.GeneratedSheets; namespace NominaOcculta; @@ -13,37 +14,42 @@ internal class AppearanceRepository { private Plugin Plugin { get; } private List Npcs { get; } private List PersonalNpcs { get; } = new(); + private Dictionary Cache { get; } = new(); private int Salt { get; set; } = new Random().Next(); private static readonly string[] Exclude = { - "Thancred", - "Y'shtola", - "Alphinaud", "Alisaie", - "Urianger", - "Tataru", - "Minfilia", - "Lyse", - "Yda", - "Papalymo", - "Krile", - "Ryne", - "Estinien", - "Nanamo Ul Namo", - "G'raha Tia", - "Raubahn", - "Cid", - "Biggs", - "Wedge", - "Haurchefant", - "Merlwyb", - "Kan-E-Senna", - "Yugiri", + "Alphinaud", "Aymeric", - "Lahabrea", - "Igeyorhm", - "Hildibrand", + "Biggs", + "Cid", + "Estinien", + "G'raha Tia", "Godbert", + "Haurchefant", + "Hermes", + "Hildibrand", + "Hythlodaeus", + "Igeyorhm", + "Kan-E-Senna", + "Krile", + "Lahabrea", + "Lyse", + "Merlwyb", + "Minfilia", + "Nanamo Ul Namo", + "Papalymo", + "Raubahn", + "Ryne", + "Tataru", + "Thancred", + "Themis", + "Urianger", + "Venat", + "Wedge", + "Y'shtola", + "Yda", + "Yugiri", }; internal IReadOnlyDictionary> JobMainHands { get; } @@ -68,11 +74,11 @@ internal class AppearanceRepository { var allMainHands = this.Plugin.DataManager.GetExcelSheet()! .Where(row => row.EquipSlotCategory.Value!.MainHand != 0) // let's not give people ultimate weapons and shit - .Where(row => !row.IsUnique && !row.IsUntradable) + .Where(row => !row.IsUnique) .ToList(); var allOffHands = this.Plugin.DataManager.GetExcelSheet()! .Where(row => row.EquipSlotCategory.Value!.OffHand != 0) - .Where(row => !row.IsUnique && !row.IsUntradable) + .Where(row => !row.IsUnique) .ToList(); foreach (var job in this.Plugin.DataManager.GetExcelSheet(ClientLanguage.English)!) { if (job.RowId == 0) { @@ -122,6 +128,7 @@ internal class AppearanceRepository { internal void Reset() { this.Salt = new Random().Next(); + this.Cache.Clear(); } internal void RefilterPersonal() { @@ -142,15 +149,22 @@ internal class AppearanceRepository { } private int GetNpcIndex(uint objectId) { - return new Random((int) (objectId + this.Salt)).Next(0, this.Npcs.Count); + if (this.Cache.TryGetValue(objectId, out var index)) { + return index; + } + + var idx = new Random((int) (objectId + this.Salt)).Next(0, this.Npcs.Count); + this.Cache[objectId] = idx; + return idx; } private int GetNpcIndexPersonal(uint objectId) { return new Random((int) (objectId + this.Salt)).Next(0, this.PersonalNpcs.Count); } - internal ENpcBase GetNpc(uint objectId) { - if (objectId == this.Plugin.ClientState.LocalPlayer?.ObjectId && this.PersonalNpcs.Count > 0) { + internal unsafe ENpcBase GetNpc(uint objectId) { + var player = *(GameObject**) this.Plugin.ObjectTable.Address; + if (player != null && objectId == player->ObjectID && this.PersonalNpcs.Count > 0) { return this.PersonalNpcs[this.GetNpcIndexPersonal(objectId)]; } diff --git a/NominaOcculta/Obscurer.cs b/NominaOcculta/Obscurer.cs index df626f3..eeceacf 100755 --- a/NominaOcculta/Obscurer.cs +++ b/NominaOcculta/Obscurer.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using Dalamud.Game; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text; using Dalamud.Game.Text.SeStringHandling; +using FFXIVClientStructs.FFXIV.Client.Game.Group; using XivCommon.Functions.NamePlates; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -17,7 +20,7 @@ internal class Obscurer : IDisposable { private Plugin Plugin { get; } private Stopwatch UpdateTimer { get; } = new(); - private IList Friends { get; set; } + private IReadOnlySet Friends { get; set; } internal unsafe Obscurer(Plugin plugin) { this.Plugin = plugin; @@ -26,7 +29,7 @@ internal class Obscurer : IDisposable { this.Friends = this.Plugin.Common.Functions.FriendList.List .Select(friend => friend.Name.TextValue) - .ToList(); + .ToHashSet(); this.Plugin.Framework.Update += this.OnFrameworkUpdate; this.Plugin.Functions.AtkTextNodeSetText += this.OnAtkTextNodeSetText; @@ -63,15 +66,22 @@ internal class Obscurer : IDisposable { this.Friends = this.Plugin.Common.Functions.FriendList.List .Select(friend => friend.Name.TextValue) - .ToList(); + .ToHashSet(); this.UpdateTimer.Restart(); } + private static readonly Regex Coords = new(@"^X: \d+. Y: \d+.(?: Z: \d+.)?$", RegexOptions.Compiled); + private void OnAtkTextNodeSetText(IntPtr node, IntPtr textPtr, ref SeString? overwrite) { // A catch-all for UI text. This is slow, so specialised methods should be preferred. var text = Util.ReadRawSeString(textPtr); + var tval = text.TextValue; + if (string.IsNullOrWhiteSpace(tval) || tval.All(c => !char.IsLetter(c)) || Coords.IsMatch(tval)) { + return; + } + var changed = this.ChangeNames(text); if (changed) { overwrite = text; @@ -86,31 +96,31 @@ internal class Obscurer : IDisposable { if (gameObj->ObjectKind != (byte) FFXIVClientStructs.FFXIV.Client.Game.Object.ObjectKind.Pc) { return false; } - + var gameObject = this.Plugin.ObjectTable.CreateObjectReference((IntPtr) gameObj)!; return gameObject is Character chara && this.ShouldObscureAppearance(chara); } - private bool ShouldObscureAppearance(Character chara) { + private unsafe bool ShouldObscureAppearance(Character chara) { if (!this.Plugin.Config.Enabled) { return false; } - var name = chara.Name.TextValue; - var isFriend = this.Friends.Contains(name); + var gameChara = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*) chara.Address; + var name = Marshal.PtrToStringUTF8((IntPtr) gameChara->GameObject.Name)!; - if (this.Plugin.Config.ObscureAppearancesExcludeFriends && isFriend) { + if (this.Plugin.Config.ObscureAppearancesExcludeFriends && this.Friends.Contains(name)) { return false; } - if (this.Plugin.Config.ObscureAppearancesSelf && chara.ObjectId == this.Plugin.ClientState.LocalPlayer?.ObjectId) { - return true; + var player = *(GameObject**) this.Plugin.ObjectTable.Address; + if (player != null && player->ObjectID == chara.ObjectId) { + return this.Plugin.Config.ObscureAppearancesSelf; } - - var party = this.Plugin.PartyList.Select(member => member.ObjectId).ToArray(); - var inParty = party.Contains(chara.ObjectId); - if (this.Plugin.Config.ObscureAppearancesParty && inParty) { - return true; + + var party = this.Plugin.PartyList.Select(member => member.ObjectId); + if (party.Contains(chara.ObjectId)) { + return this.Plugin.Config.ObscureAppearancesParty; } return this.Plugin.Config.ObscureAppearancesOthers; @@ -120,7 +130,7 @@ internal class Obscurer : IDisposable { if (!this.ShouldObscureAppearance(gameObj)) { return; } - + var npc = this.Plugin.AppearanceRepository.GetNpc(gameObj->ObjectID); var customise = (byte*) customiseDataPtr; @@ -171,11 +181,11 @@ internal class Obscurer : IDisposable { if (equipData == null) { return; } - + if (!this.ShouldObscureAppearance(gameObj)) { return; } - + var chara = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*) gameObj; var (mainHand, offHand) = this.Plugin.AppearanceRepository.GetHands(chara->ClassJob, gameObj->ObjectID); @@ -232,7 +242,7 @@ internal class Obscurer : IDisposable { return; } - var info = GetInfo(chara); + var info = this.GetInfo(chara); void Change(string name) { this.ChangeName(args.Name, name, info); @@ -278,10 +288,6 @@ internal class Obscurer : IDisposable { return; } - if (!text.ContainsPlayerName(name)) { - return; - } - text.ReplacePlayerName(name, replacement); } @@ -297,26 +303,26 @@ internal class Obscurer : IDisposable { var player = this.Plugin.ClientState.LocalPlayer; - if (this.Plugin.Config.SelfFull) { - var playerName = player?.Name.TextValue; - if (playerName != null && text.ContainsPlayerName(playerName) && this.Plugin.NameRepository.GetReplacement(playerName, GetInfo(player!)) is { } replacement) { + if (player != null && this.Plugin.Config.SelfFull) { + var playerName = player.RawName()!; + if (this.Plugin.NameRepository.GetReplacement(playerName, this.GetInfo(player!)) is { } replacement) { text.ReplacePlayerName(playerName, replacement); changed = true; } } - if (this.Plugin.Config.SelfFirst || this.Plugin.Config.SelfLast) { - var playerName = player?.Name.TextValue; - if (playerName != null && this.Plugin.NameRepository.GetReplacement(playerName, GetInfo(player!)) is { } replacement) { + if (player != null && (this.Plugin.Config.SelfFirst || this.Plugin.Config.SelfLast)) { + var playerName = player.RawName()!; + if (this.Plugin.NameRepository.GetReplacement(playerName, this.GetInfo(player!)) is { } replacement) { var parts = playerName.Split(' ', 2); var replacementParts = replacement.Split(' ', 2); - if (this.Plugin.Config.SelfFirst && text.ContainsPlayerName(parts[0])) { + if (this.Plugin.Config.SelfFirst) { text.ReplacePlayerName(parts[0], replacementParts[0]); changed = true; } - if (this.Plugin.Config.SelfLast && text.ContainsPlayerName(parts[1])) { + if (this.Plugin.Config.SelfLast) { text.ReplacePlayerName(parts[1], replacementParts[1]); changed = true; } @@ -325,14 +331,18 @@ internal class Obscurer : IDisposable { if (this.Plugin.Config.Party) { foreach (var member in this.Plugin.PartyList) { - var name = member.Name.TextValue; + string name; + unsafe { + var raw = (PartyMember*) member.Address; + name = Marshal.PtrToStringUTF8((IntPtr) raw->Name)!; + } var info = ((byte) 0xFF, (byte) 0xFF, member.Sex); if (member.GameObject is Character chara) { - info = GetInfo(chara); + info = this.GetInfo(chara); } - if (member.ObjectId == player?.ObjectId || !text.ContainsPlayerName(name) || this.Plugin.NameRepository.GetReplacement(name, info) is not { } replacement) { + if (member.ObjectId == player?.ObjectId || this.Plugin.NameRepository.GetReplacement(name, info) is not { } replacement) { continue; } @@ -353,17 +363,17 @@ internal class Obscurer : IDisposable { continue; } - var name = chara.Name.TextValue; + var name = chara.RawName()!; if (this.Plugin.Config.ExcludeFriends && this.Friends.Contains(name)) { continue; } - var info = GetInfo(chara); + var info = this.GetInfo(chara); if (info.race == 0) { continue; } - if (this.Plugin.NameRepository.GetReplacement(name, GetInfo(chara)) is not { } replacement) { + if (this.Plugin.NameRepository.GetReplacement(name, this.GetInfo(chara)) is not { } replacement) { continue; } @@ -382,7 +392,7 @@ internal class Obscurer : IDisposable { (byte) npc.Race.Row, (byte) ((npc.Tribe.Row - 1) % 2), npc.Gender - ); + ); } return ( diff --git a/NominaOcculta/Util.cs b/NominaOcculta/Util.cs index efa6a0f..2db9e86 100755 --- a/NominaOcculta/Util.cs +++ b/NominaOcculta/Util.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Text.SeStringHandling; using Dalamud.Game.Text.SeStringHandling.Payloads; @@ -8,27 +10,6 @@ namespace NominaOcculta; internal static class Util { internal const string SheetName = "CharaMakeName"; - internal static bool ContainsPlayerName(this SeString text, string name) { - foreach (var payload in text.Payloads) { - switch (payload) { - case PlayerPayload pp: - if (pp.PlayerName.Contains(name)) { - return true; - } - - break; - case ITextProvider prov: - if (prov.Text.Contains(name)) { - return true; - } - - break; - } - } - - return false; - } - internal static void ReplacePlayerName(this SeString text, string name, string replacement) { if (string.IsNullOrEmpty(name)) { return; @@ -52,6 +33,11 @@ internal static class Util { } } + internal static unsafe string? RawName(this GameObject obj) { + var gameObj = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*) obj.Address; + return Marshal.PtrToStringUTF8((IntPtr) gameObj->Name); + } + internal static byte[] Terminate(this byte[] bs) { var terminated = new byte[bs.Length + 1]; Array.Copy(bs, terminated, bs.Length);