feat: increase efficiency
This commit is contained in:
parent
09a0077623
commit
ef1ef4de5b
|
@ -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<ENpcBase> Npcs { get; }
|
||||
private List<ENpcBase> PersonalNpcs { get; } = new();
|
||||
private Dictionary<uint, int> 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<uint, IReadOnlyList<Item>> JobMainHands { get; }
|
||||
|
@ -68,11 +74,11 @@ internal class AppearanceRepository {
|
|||
var allMainHands = this.Plugin.DataManager.GetExcelSheet<Item>()!
|
||||
.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<Item>()!
|
||||
.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<ClassJob>(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)];
|
||||
}
|
||||
|
||||
|
|
|
@ -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<string> Friends { get; set; }
|
||||
private IReadOnlySet<string> 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 (
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue