feat: increase efficiency

This commit is contained in:
Anna 2022-05-10 23:48:01 -04:00
parent 09a0077623
commit ef1ef4de5b
3 changed files with 99 additions and 89 deletions

View File

@ -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)];
}

View File

@ -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 (

View File

@ -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);