Update for changed GameData.

This commit is contained in:
Ottermandias 2023-12-17 11:51:24 +01:00
parent 3305250482
commit 7d612df951
42 changed files with 374 additions and 455 deletions

@ -1 +1 @@
Subproject commit 5f0eec50ea7f7a4727ceab056bc3756f0ed58a30 Subproject commit bde59c34f7108520002c21cdbf21e8ee5b586944

@ -1 +1 @@
Subproject commit ffdb966fec5a657893289e655c641ceb3af1d59f Subproject commit afc56d9f07a2a54ab791a4c85cf627b6f884aec2

View file

@ -95,7 +95,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
private DalamudServices _dalamud; private DalamudServices _dalamud;
private TempCollectionManager _tempCollections; private TempCollectionManager _tempCollections;
private TempModManager _tempMods; private TempModManager _tempMods;
private ActorService _actors; private ActorManager _actors;
private CollectionResolver _collectionResolver; private CollectionResolver _collectionResolver;
private CutsceneService _cutsceneService; private CutsceneService _cutsceneService;
private ModImportManager _modImportManager; private ModImportManager _modImportManager;
@ -108,7 +108,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public unsafe PenumbraApi(CommunicatorService communicator, ModManager modManager, ResourceLoader resourceLoader, public unsafe PenumbraApi(CommunicatorService communicator, ModManager modManager, ResourceLoader resourceLoader,
Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections, Configuration config, CollectionManager collectionManager, DalamudServices dalamud, TempCollectionManager tempCollections,
TempModManager tempMods, ActorService actors, CollectionResolver collectionResolver, CutsceneService cutsceneService, TempModManager tempMods, ActorManager actors, CollectionResolver collectionResolver, CutsceneService cutsceneService,
ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, ModFileSystem modFileSystem, ModImportManager modImportManager, CollectionEditor collectionEditor, RedrawService redrawService, ModFileSystem modFileSystem,
ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory) ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory)
{ {
@ -889,13 +889,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{ {
CheckInitialized(); CheckInitialized();
if (!_actors.Valid)
return PenumbraApiEc.SystemDisposed;
if (actorIndex < 0 || actorIndex >= _dalamud.Objects.Length) if (actorIndex < 0 || actorIndex >= _dalamud.Objects.Length)
return PenumbraApiEc.InvalidArgument; return PenumbraApiEc.InvalidArgument;
var identifier = _actors.AwaitedService.FromObject(_dalamud.Objects[actorIndex], false, false, true); var identifier = _actors.FromObject(_dalamud.Objects[actorIndex], false, false, true);
if (!identifier.IsValid) if (!identifier.IsValid)
return PenumbraApiEc.InvalidArgument; return PenumbraApiEc.InvalidArgument;
@ -1143,11 +1140,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx) private unsafe ActorIdentifier AssociatedIdentifier(int gameObjectIdx)
{ {
if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length || !_actors.Valid) if (gameObjectIdx < 0 || gameObjectIdx >= _dalamud.Objects.Length)
return ActorIdentifier.Invalid; return ActorIdentifier.Invalid;
var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_dalamud.Objects.GetObjectAddress(gameObjectIdx); var ptr = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)_dalamud.Objects.GetObjectAddress(gameObjectIdx);
return _actors.AwaitedService.FromObject(ptr, out _, false, true, true); return _actors.FromObject(ptr, out _, false, true, true);
} }
// Resolve a path given by string for a specific collection. // Resolve a path given by string for a specific collection.
@ -1241,12 +1238,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi
// TODO: replace all usages with ActorIdentifier stuff when incrementing API // TODO: replace all usages with ActorIdentifier stuff when incrementing API
private ActorIdentifier NameToIdentifier(string name, ushort worldId) private ActorIdentifier NameToIdentifier(string name, ushort worldId)
{ {
if (!_actors.Valid)
return ActorIdentifier.Invalid;
// Verified to be valid name beforehand. // Verified to be valid name beforehand.
var b = ByteString.FromStringUnsafe(name, false); var b = ByteString.FromStringUnsafe(name, false);
return _actors.AwaitedService.CreatePlayer(b, worldId); return _actors.CreatePlayer(b, worldId);
} }
private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int _1, int _2, bool inherited) private void OnModSettingChange(ModCollection collection, ModSettingChange type, Mod? mod, int _1, int _2, bool inherited)

View file

@ -22,10 +22,10 @@ public class CollectionCache : IDisposable
private readonly CollectionCacheManager _manager; private readonly CollectionCacheManager _manager;
private readonly ModCollection _collection; private readonly ModCollection _collection;
public readonly CollectionModData ModData = new(); public readonly CollectionModData ModData = new();
public readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = new(); private readonly SortedList<string, (SingleArray<IMod>, object?)> _changedItems = [];
public readonly ConcurrentDictionary<Utf8GamePath, ModPath> ResolvedFiles = new(); public readonly ConcurrentDictionary<Utf8GamePath, ModPath> ResolvedFiles = new();
public readonly MetaCache Meta; public readonly MetaCache Meta;
public readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new(); public readonly Dictionary<IMod, SingleArray<ModConflicts>> ConflictDict = [];
public int Calculating = -1; public int Calculating = -1;
@ -33,10 +33,10 @@ public class CollectionCache : IDisposable
=> _collection.AnonymizedName; => _collection.AnonymizedName;
public IEnumerable<SingleArray<ModConflicts>> AllConflicts public IEnumerable<SingleArray<ModConflicts>> AllConflicts
=> _conflicts.Values; => ConflictDict.Values;
public SingleArray<ModConflicts> Conflicts(IMod mod) public SingleArray<ModConflicts> Conflicts(IMod mod)
=> _conflicts.TryGetValue(mod, out var c) ? c : new SingleArray<ModConflicts>(); => ConflictDict.TryGetValue(mod, out SingleArray<ModConflicts> c) ? c : new SingleArray<ModConflicts>();
private int _changedItemsSaveCounter = -1; private int _changedItemsSaveCounter = -1;
@ -195,7 +195,7 @@ public class CollectionCache : IDisposable
$"Invalid mod state, removing {mod.Name} and associated manipulation {manipulation} returned current mod {mp.Name}."); $"Invalid mod state, removing {mod.Name} and associated manipulation {manipulation} returned current mod {mp.Name}.");
} }
_conflicts.Remove(mod); ConflictDict.Remove(mod);
foreach (var conflict in conflicts) foreach (var conflict in conflicts)
{ {
if (conflict.HasPriority) if (conflict.HasPriority)
@ -206,9 +206,9 @@ public class CollectionCache : IDisposable
{ {
var newConflicts = Conflicts(conflict.Mod2).Remove(c => c.Mod2 == mod); var newConflicts = Conflicts(conflict.Mod2).Remove(c => c.Mod2 == mod);
if (newConflicts.Count > 0) if (newConflicts.Count > 0)
_conflicts[conflict.Mod2] = newConflicts; ConflictDict[conflict.Mod2] = newConflicts;
else else
_conflicts.Remove(conflict.Mod2); ConflictDict.Remove(conflict.Mod2);
} }
} }
@ -336,9 +336,9 @@ public class CollectionCache : IDisposable
return false; return false;
}); });
if (changedConflicts.Count == 0) if (changedConflicts.Count == 0)
_conflicts.Remove(mod); ConflictDict.Remove(mod);
else else
_conflicts[mod] = changedConflicts; ConflictDict[mod] = changedConflicts;
} }
// Add a new conflict between the added mod and the existing mod. // Add a new conflict between the added mod and the existing mod.
@ -373,9 +373,9 @@ public class CollectionCache : IDisposable
{ {
// Add the same conflict list to both conflict directions. // Add the same conflict list to both conflict directions.
var conflictList = new List<object> { data }; var conflictList = new List<object> { data };
_conflicts[addedMod] = addedConflicts.Append(new ModConflicts(existingMod, conflictList, existingPriority < addedPriority, ConflictDict[addedMod] = addedConflicts.Append(new ModConflicts(existingMod, conflictList, existingPriority < addedPriority,
existingPriority != addedPriority)); existingPriority != addedPriority));
_conflicts[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList, ConflictDict[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList,
existingPriority >= addedPriority, existingPriority >= addedPriority,
existingPriority != addedPriority)); existingPriority != addedPriority));
} }
@ -426,7 +426,7 @@ public class CollectionCache : IDisposable
_changedItems.Clear(); _changedItems.Clear();
// Skip IMCs because they would result in far too many false-positive items, // Skip IMCs because they would result in far too many false-positive items,
// since they are per set instead of per item-slot/item/variant. // since they are per set instead of per item-slot/item/variant.
var identifier = _manager.MetaFileManager.Identifier.AwaitedService; var identifier = _manager.MetaFileManager.Identifier;
var items = new SortedList<string, object?>(512); var items = new SortedList<string, object?>(512);
void AddItems(IMod mod) void AddItems(IMod mod)

View file

@ -159,7 +159,7 @@ public class CollectionCacheManager : IDisposable
null); null);
cache.ResolvedFiles.Clear(); cache.ResolvedFiles.Clear();
cache.Meta.Reset(); cache.Meta.Reset();
cache._conflicts.Clear(); cache.ConflictDict.Clear();
// Add all forced redirects. // Add all forced redirects.
foreach (var tempMod in _tempMods.ModsForAllCollections foreach (var tempMod in _tempMods.ModsForAllCollections
@ -372,7 +372,7 @@ public class CollectionCacheManager : IDisposable
{ {
collection._cache!.ResolvedFiles.Clear(); collection._cache!.ResolvedFiles.Clear();
collection._cache!.Meta.Reset(); collection._cache!.Meta.Reset();
collection._cache!._conflicts.Clear(); collection._cache!.ConflictDict.Clear();
} }
} }

View file

@ -28,9 +28,9 @@ public class ActiveCollections : ISavable, IDisposable
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly SaveService _saveService; private readonly SaveService _saveService;
private readonly ActiveCollectionData _data; private readonly ActiveCollectionData _data;
private readonly ActorService _actors; private readonly ActorManager _actors;
public ActiveCollections(Configuration config, CollectionStorage storage, ActorService actors, CommunicatorService communicator, public ActiveCollections(Configuration config, CollectionStorage storage, ActorManager actors, CommunicatorService communicator,
SaveService saveService, ActiveCollectionData data) SaveService saveService, ActiveCollectionData data)
{ {
_storage = storage; _storage = storage;
@ -475,7 +475,7 @@ public class ActiveCollections : ISavable, IDisposable
{ {
case IdentifierType.Player when id.HomeWorld != ushort.MaxValue: case IdentifierType.Player when id.HomeWorld != ushort.MaxValue:
{ {
var global = ByType(CollectionType.Individual, _actors.AwaitedService.CreatePlayer(id.PlayerName, ushort.MaxValue)); var global = ByType(CollectionType.Individual, _actors.CreatePlayer(id.PlayerName, ushort.MaxValue));
return global?.Index == checkAssignment.Index return global?.Index == checkAssignment.Index
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it." ? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
: string.Empty; : string.Empty;
@ -484,12 +484,12 @@ public class ActiveCollections : ISavable, IDisposable
if (id.HomeWorld != ushort.MaxValue) if (id.HomeWorld != ushort.MaxValue)
{ {
var global = ByType(CollectionType.Individual, var global = ByType(CollectionType.Individual,
_actors.AwaitedService.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId)); _actors.CreateOwned(id.PlayerName, ushort.MaxValue, id.Kind, id.DataId));
if (global?.Index == checkAssignment.Index) if (global?.Index == checkAssignment.Index)
return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."; return "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it.";
} }
var unowned = ByType(CollectionType.Individual, _actors.AwaitedService.CreateNpc(id.Kind, id.DataId)); var unowned = ByType(CollectionType.Individual, _actors.CreateNpc(id.Kind, id.DataId));
return unowned?.Index == checkAssignment.Index return unowned?.Index == checkAssignment.Index
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it." ? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
: string.Empty; : string.Empty;

View file

@ -1,6 +1,7 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.String; using Penumbra.String;
namespace Penumbra.Collections.Manager; namespace Penumbra.Collections.Manager;
@ -36,7 +37,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
return true; return true;
if (identifier.Retainer is not ActorIdentifier.RetainerType.Mannequin && _config.UseOwnerNameForCharacterCollection) if (identifier.Retainer is not ActorIdentifier.RetainerType.Mannequin && _config.UseOwnerNameForCharacterCollection)
return CheckWorlds(_actorService.AwaitedService.GetCurrentPlayer(), out collection); return CheckWorlds(_actors.GetCurrentPlayer(), out collection);
break; break;
} }
@ -46,7 +47,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
return true; return true;
// Handle generic NPC // Handle generic NPC
var npcIdentifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty, var npcIdentifier = _actors.CreateIndividualUnchecked(IdentifierType.Npc, ByteString.Empty,
ushort.MaxValue, ushort.MaxValue,
identifier.Kind, identifier.DataId); identifier.Kind, identifier.DataId);
if (npcIdentifier.IsValid && _individuals.TryGetValue(npcIdentifier, out collection)) if (npcIdentifier.IsValid && _individuals.TryGetValue(npcIdentifier, out collection))
@ -56,7 +57,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (!_config.UseOwnerNameForCharacterCollection) if (!_config.UseOwnerNameForCharacterCollection)
return false; return false;
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier = _actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName,
identifier.HomeWorld.Id, identifier.HomeWorld.Id,
ObjectKind.None, uint.MaxValue); ObjectKind.None, uint.MaxValue);
return CheckWorlds(identifier, out collection); return CheckWorlds(identifier, out collection);
@ -89,37 +90,37 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (identifier.Type != IdentifierType.Special) if (identifier.Type != IdentifierType.Special)
return (identifier, SpecialResult.Invalid); return (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolvePartyBannerPlayer(identifier.Special, out var id)) if (_actors.ResolvePartyBannerPlayer(identifier.Special, out var id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PartyBanner) : (identifier, SpecialResult.Invalid); return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PartyBanner) : (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolvePvPBannerPlayer(identifier.Special, out id)) if (_actors.ResolvePvPBannerPlayer(identifier.Special, out id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PvPBanner) : (identifier, SpecialResult.Invalid); return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.PvPBanner) : (identifier, SpecialResult.Invalid);
if (_actorService.AwaitedService.ResolveMahjongPlayer(identifier.Special, out id)) if (_actors.ResolveMahjongPlayer(identifier.Special, out id))
return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.Mahjong) : (identifier, SpecialResult.Invalid); return _config.UseCharacterCollectionsInCards ? (id, SpecialResult.Mahjong) : (identifier, SpecialResult.Invalid);
switch (identifier.Special) switch (identifier.Special)
{ {
case ScreenActor.CharacterScreen when _config.UseCharacterCollectionInMainWindow: case ScreenActor.CharacterScreen when _config.UseCharacterCollectionInMainWindow:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.CharacterScreen); return (_actors.GetCurrentPlayer(), SpecialResult.CharacterScreen);
case ScreenActor.FittingRoom when _config.UseCharacterCollectionInTryOn: case ScreenActor.FittingRoom when _config.UseCharacterCollectionInTryOn:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.FittingRoom); return (_actors.GetCurrentPlayer(), SpecialResult.FittingRoom);
case ScreenActor.DyePreview when _config.UseCharacterCollectionInTryOn: case ScreenActor.DyePreview when _config.UseCharacterCollectionInTryOn:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.DyePreview); return (_actors.GetCurrentPlayer(), SpecialResult.DyePreview);
case ScreenActor.Portrait when _config.UseCharacterCollectionsInCards: case ScreenActor.Portrait when _config.UseCharacterCollectionsInCards:
return (_actorService.AwaitedService.GetCurrentPlayer(), SpecialResult.Portrait); return (_actors.GetCurrentPlayer(), SpecialResult.Portrait);
case ScreenActor.ExamineScreen: case ScreenActor.ExamineScreen:
{ {
identifier = _actorService.AwaitedService.GetInspectPlayer(); identifier = _actors.GetInspectPlayer();
if (identifier.IsValid) if (identifier.IsValid)
return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect); return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Inspect);
identifier = _actorService.AwaitedService.GetCardPlayer(); identifier = _actors.GetCardPlayer();
if (identifier.IsValid) if (identifier.IsValid)
return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card); return (_config.UseCharacterCollectionInInspect ? identifier : ActorIdentifier.Invalid, SpecialResult.Card);
return _config.UseCharacterCollectionInTryOn return _config.UseCharacterCollectionInTryOn
? (_actorService.AwaitedService.GetGlamourPlayer(), SpecialResult.Glamour) ? (_actors.GetGlamourPlayer(), SpecialResult.Glamour)
: (identifier, SpecialResult.Invalid); : (identifier, SpecialResult.Invalid);
} }
default: return (identifier, SpecialResult.Invalid); default: return (identifier, SpecialResult.Invalid);
@ -127,10 +128,10 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
} }
public bool TryGetCollection(GameObject? gameObject, out ModCollection? collection) public bool TryGetCollection(GameObject? gameObject, out ModCollection? collection)
=> TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, true, false, false), out collection); => TryGetCollection(_actors.FromObject(gameObject, true, false, false), out collection);
public unsafe bool TryGetCollection(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* gameObject, out ModCollection? collection) public unsafe bool TryGetCollection(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* gameObject, out ModCollection? collection)
=> TryGetCollection(_actorService.AwaitedService.FromObject(gameObject, out _, true, false, false), out collection); => TryGetCollection(_actors.FromObject(gameObject, out _, true, false, false), out collection);
private bool CheckWorlds(ActorIdentifier identifier, out ModCollection? collection) private bool CheckWorlds(ActorIdentifier identifier, out ModCollection? collection)
{ {
@ -143,7 +144,7 @@ public sealed partial class IndividualCollections : IReadOnlyList<(string Displa
if (_individuals.TryGetValue(identifier, out collection)) if (_individuals.TryGetValue(identifier, out collection))
return true; return true;
identifier = _actorService.AwaitedService.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue, identifier = _actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
identifier.Kind, identifier.Kind,
identifier.DataId); identifier.DataId);
if (identifier.IsValid && _individuals.TryGetValue(identifier, out collection)) if (identifier.IsValid && _individuals.TryGetValue(identifier, out collection))

View file

@ -3,6 +3,8 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes; using OtterGui.Classes;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Structs;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
@ -26,23 +28,20 @@ public partial class IndividualCollections
public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage) public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage)
{ {
if (_actorService.Valid) if (_actors.Awaiter.IsCompletedSuccessfully)
{ {
var ret = ReadJObjectInternal(obj, storage); var ret = ReadJObjectInternal(obj, storage);
return ret; return ret;
} }
void Func() Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actors.Awaiter.ContinueWith(_ =>
{ {
if (ReadJObjectInternal(obj, storage)) if (ReadJObjectInternal(obj, storage))
saver.ImmediateSave(parent); saver.ImmediateSave(parent);
IsLoaded = true; IsLoaded = true;
Loaded.Invoke(); Loaded.Invoke();
_actorService.FinishedCreation -= Func; });
}
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actorService.FinishedCreation += Func;
return false; return false;
} }
@ -60,7 +59,7 @@ public partial class IndividualCollections
{ {
try try
{ {
var identifier = _actorService.AwaitedService.FromJson(data as JObject); var identifier = _actors.FromJson(data as JObject);
var group = GetGroup(identifier); var group = GetGroup(identifier);
if (group.Length == 0 || group.Any(i => !i.IsValid)) if (group.Length == 0 || group.Any(i => !i.IsValid))
{ {
@ -101,10 +100,10 @@ public partial class IndividualCollections
internal void Migrate0To1(Dictionary<string, ModCollection> old) internal void Migrate0To1(Dictionary<string, ModCollection> old)
{ {
static bool FindDataId(string name, IReadOnlyDictionary<uint, string> data, out uint dataId) static bool FindDataId(string name, NameDictionary data, out NpcId dataId)
{ {
var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase), var kvp = data.FirstOrDefault(kvp => kvp.Value.Equals(name, StringComparison.OrdinalIgnoreCase),
new KeyValuePair<uint, string>(uint.MaxValue, string.Empty)); new KeyValuePair<NpcId, string>(uint.MaxValue, string.Empty));
dataId = kvp.Key; dataId = kvp.Key;
return kvp.Value.Length > 0; return kvp.Value.Length > 0;
} }
@ -114,22 +113,22 @@ public partial class IndividualCollections
var kind = ObjectKind.None; var kind = ObjectKind.None;
var lowerName = name.ToLowerInvariant(); var lowerName = name.ToLowerInvariant();
// Prefer matching NPC names, fewer false positives than preferring players. // Prefer matching NPC names, fewer false positives than preferring players.
if (FindDataId(lowerName, _actorService.AwaitedService.Data.Companions, out var dataId)) if (FindDataId(lowerName, _actors.Data.Companions, out var dataId))
kind = ObjectKind.Companion; kind = ObjectKind.Companion;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.Mounts, out dataId)) else if (FindDataId(lowerName, _actors.Data.Mounts, out dataId))
kind = ObjectKind.MountType; kind = ObjectKind.MountType;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.BNpcs, out dataId)) else if (FindDataId(lowerName, _actors.Data.BNpcs, out dataId))
kind = ObjectKind.BattleNpc; kind = ObjectKind.BattleNpc;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.ENpcs, out dataId)) else if (FindDataId(lowerName, _actors.Data.ENpcs, out dataId))
kind = ObjectKind.EventNpc; kind = ObjectKind.EventNpc;
var identifier = _actorService.AwaitedService.CreateNpc(kind, dataId); var identifier = _actors.CreateNpc(kind, dataId);
if (identifier.IsValid) if (identifier.IsValid)
{ {
// If the name corresponds to a valid npc, add it as a group. If this fails, notify users. // If the name corresponds to a valid npc, add it as a group. If this fails, notify users.
var group = GetGroup(identifier); var group = GetGroup(identifier);
var ids = string.Join(", ", group.Select(i => i.DataId.ToString())); var ids = string.Join(", ", group.Select(i => i.DataId.ToString()));
if (Add($"{_actorService.AwaitedService.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection)) if (Add($"{_actors.Data.ToName(kind, dataId)} ({kind.ToName()})", group, collection))
Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}]."); Penumbra.Log.Information($"Migrated {name} ({kind.ToName()}) to NPC Identifiers [{ids}].");
else else
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(
@ -137,15 +136,12 @@ public partial class IndividualCollections
NotificationType.Error); NotificationType.Error);
} }
// If it is not a valid NPC name, check if it can be a player name. // If it is not a valid NPC name, check if it can be a player name.
else if (ActorManager.VerifyPlayerName(name)) else if (ActorIdentifierFactory.VerifyPlayerName(name))
{ {
identifier = _actorService.AwaitedService.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue); identifier = _actors.CreatePlayer(ByteString.FromStringUnsafe(name, false), ushort.MaxValue);
var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}.")); var shortName = string.Join(" ", name.Split().Select(n => $"{n[0]}."));
// Try to migrate the player name without logging full names. // Try to migrate the player name without logging full names.
if (Add($"{name} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", new[] if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
{
identifier,
}, collection))
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier."); Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier.");
else else
Penumbra.Messager.NotificationMessage( Penumbra.Messager.NotificationMessage(

View file

@ -1,7 +1,9 @@
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Services; using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
namespace Penumbra.Collections.Manager; namespace Penumbra.Collections.Manager;
@ -11,9 +13,9 @@ public sealed partial class IndividualCollections
public record struct IndividualAssignment(string DisplayName, IReadOnlyList<ActorIdentifier> Identifiers, ModCollection Collection); public record struct IndividualAssignment(string DisplayName, IReadOnlyList<ActorIdentifier> Identifiers, ModCollection Collection);
private readonly Configuration _config; private readonly Configuration _config;
private readonly ActorService _actorService; private readonly ActorManager _actors;
private readonly Dictionary<ActorIdentifier, ModCollection> _individuals = new(); private readonly Dictionary<ActorIdentifier, ModCollection> _individuals = [];
private readonly List<IndividualAssignment> _assignments = new(); private readonly List<IndividualAssignment> _assignments = [];
public event Action Loaded; public event Action Loaded;
public bool IsLoaded { get; private set; } public bool IsLoaded { get; private set; }
@ -21,10 +23,10 @@ public sealed partial class IndividualCollections
public IReadOnlyList<IndividualAssignment> Assignments public IReadOnlyList<IndividualAssignment> Assignments
=> _assignments; => _assignments;
public IndividualCollections(ActorService actorService, Configuration config, bool temporary) public IndividualCollections(ActorManager actors, Configuration config, bool temporary)
{ {
_config = config; _config = config;
_actorService = actorService; _actors = actors;
IsLoaded = temporary; IsLoaded = temporary;
Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay."); Loaded += () => Penumbra.Log.Information($"{_assignments.Count} Individual Assignments loaded after delay.");
} }
@ -69,44 +71,34 @@ public sealed partial class IndividualCollections
return set ? AddResult.AlreadySet : AddResult.Valid; return set ? AddResult.AlreadySet : AddResult.Valid;
} }
public AddResult CanAdd(IdentifierType type, string name, ushort homeWorld, ObjectKind kind, IEnumerable<uint> dataIds, public AddResult CanAdd(IdentifierType type, string name, WorldId homeWorld, ObjectKind kind, IEnumerable<NpcId> dataIds,
out ActorIdentifier[] identifiers) out ActorIdentifier[] identifiers)
{ {
identifiers = Array.Empty<ActorIdentifier>(); identifiers = [];
var manager = _actorService.AwaitedService;
switch (type) switch (type)
{ {
case IdentifierType.Player: case IdentifierType.Player:
if (!ByteString.FromString(name, out var playerName)) if (!ByteString.FromString(name, out var playerName))
return AddResult.Invalid; return AddResult.Invalid;
identifiers = new[] identifiers = [_actors.CreatePlayer(playerName, homeWorld)];
{
manager.CreatePlayer(playerName, homeWorld),
};
break; break;
case IdentifierType.Retainer: case IdentifierType.Retainer:
if (!ByteString.FromString(name, out var retainerName)) if (!ByteString.FromString(name, out var retainerName))
return AddResult.Invalid; return AddResult.Invalid;
identifiers = new[] identifiers = [_actors.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both)];
{
manager.CreateRetainer(retainerName, ActorIdentifier.RetainerType.Both),
};
break; break;
case IdentifierType.Owned: case IdentifierType.Owned:
if (!ByteString.FromString(name, out var ownerName)) if (!ByteString.FromString(name, out var ownerName))
return AddResult.Invalid; return AddResult.Invalid;
identifiers = dataIds.Select(id => manager.CreateOwned(ownerName, homeWorld, kind, id)).ToArray(); identifiers = dataIds.Select(id => _actors.CreateOwned(ownerName, homeWorld, kind, id)).ToArray();
break; break;
case IdentifierType.Npc: case IdentifierType.Npc:
identifiers = dataIds identifiers = dataIds
.Select(id => manager.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray(); .Select(id => _actors.CreateIndividual(IdentifierType.Npc, ByteString.Empty, ushort.MaxValue, kind, id)).ToArray();
break;
default:
identifiers = Array.Empty<ActorIdentifier>();
break; break;
} }
@ -116,12 +108,22 @@ public sealed partial class IndividualCollections
public ActorIdentifier[] GetGroup(ActorIdentifier identifier) public ActorIdentifier[] GetGroup(ActorIdentifier identifier)
{ {
if (!identifier.IsValid) if (!identifier.IsValid)
return Array.Empty<ActorIdentifier>(); return [];
return identifier.Type switch
{
IdentifierType.Player => [identifier.CreatePermanent()],
IdentifierType.Special => [identifier],
IdentifierType.Retainer => [identifier.CreatePermanent()],
IdentifierType.Owned => CreateNpcs(_actors, identifier.CreatePermanent()),
IdentifierType.Npc => CreateNpcs(_actors, identifier),
_ => [],
};
static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier) static ActorIdentifier[] CreateNpcs(ActorManager manager, ActorIdentifier identifier)
{ {
var name = manager.Data.ToName(identifier.Kind, identifier.DataId); var name = manager.Data.ToName(identifier.Kind, identifier.DataId);
var table = identifier.Kind switch NameDictionary table = identifier.Kind switch
{ {
ObjectKind.BattleNpc => manager.Data.BNpcs, ObjectKind.BattleNpc => manager.Data.BNpcs,
ObjectKind.EventNpc => manager.Data.ENpcs, ObjectKind.EventNpc => manager.Data.ENpcs,
@ -134,25 +136,6 @@ public sealed partial class IndividualCollections
.Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id, .Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id,
identifier.Kind, kvp.Key)).ToArray(); identifier.Kind, kvp.Key)).ToArray();
} }
return identifier.Type switch
{
IdentifierType.Player => new[]
{
identifier.CreatePermanent(),
},
IdentifierType.Special => new[]
{
identifier,
},
IdentifierType.Retainer => new[]
{
identifier.CreatePermanent(),
},
IdentifierType.Owned => CreateNpcs(_actorService.AwaitedService, identifier.CreatePermanent()),
IdentifierType.Npc => CreateNpcs(_actorService.AwaitedService, identifier),
_ => Array.Empty<ActorIdentifier>(),
};
} }
internal bool Add(ActorIdentifier[] identifiers, ModCollection collection) internal bool Add(ActorIdentifier[] identifiers, ModCollection collection)
@ -241,12 +224,12 @@ public sealed partial class IndividualCollections
{ {
return identifier.Type switch return identifier.Type switch
{ {
IdentifierType.Player => $"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", IdentifierType.Player => $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})",
IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)", IdentifierType.Retainer => $"{identifier.PlayerName} (Retainer)",
IdentifierType.Owned => IdentifierType.Owned =>
$"{identifier.PlayerName} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})'s {_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)}", $"{identifier.PlayerName} ({_actors.Data.ToWorldName(identifier.HomeWorld)})'s {_actors.Data.ToName(identifier.Kind, identifier.DataId)}",
IdentifierType.Npc => IdentifierType.Npc =>
$"{_actorService.AwaitedService.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})", $"{_actors.Data.ToName(identifier.Kind, identifier.DataId)} ({identifier.Kind.ToName()})",
_ => string.Empty, _ => string.Empty,
}; };
} }

View file

@ -14,10 +14,10 @@ public class TempCollectionManager : IDisposable
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly CollectionStorage _storage; private readonly CollectionStorage _storage;
private readonly ActorService _actors; private readonly ActorManager _actors;
private readonly Dictionary<string, ModCollection> _customCollections = new(); private readonly Dictionary<string, ModCollection> _customCollections = new();
public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorService actors, CollectionStorage storage) public TempCollectionManager(Configuration config, CommunicatorService communicator, ActorManager actors, CollectionStorage storage)
{ {
_communicator = communicator; _communicator = communicator;
_actors = actors; _actors = actors;
@ -111,7 +111,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, false)) if (!ByteString.FromString(characterName, out var byteString, false))
return false; return false;
var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId); var identifier = _actors.CreatePlayer(byteString, worldId);
if (!identifier.IsValid) if (!identifier.IsValid)
return false; return false;
@ -123,7 +123,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, false)) if (!ByteString.FromString(characterName, out var byteString, false))
return false; return false;
var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId); var identifier = _actors.CreatePlayer(byteString, worldId);
return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Name); return Collections.TryGetValue(identifier, out var collection) && RemoveTemporaryCollection(collection.Name);
} }
} }

View file

@ -32,7 +32,7 @@ public class CommandHandler : IDisposable
public CommandHandler(IFramework framework, ICommandManager commandManager, IChatGui chat, RedrawService redrawService, public CommandHandler(IFramework framework, ICommandManager commandManager, IChatGui chat, RedrawService redrawService,
Configuration config, Configuration config,
ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorService actors, Penumbra penumbra, ConfigWindow configWindow, ModManager modManager, CollectionManager collectionManager, ActorManager actors, Penumbra penumbra,
CollectionEditor collectionEditor) CollectionEditor collectionEditor)
{ {
_commandManager = commandManager; _commandManager = commandManager;
@ -41,7 +41,7 @@ public class CommandHandler : IDisposable
_configWindow = configWindow; _configWindow = configWindow;
_modManager = modManager; _modManager = modManager;
_collectionManager = collectionManager; _collectionManager = collectionManager;
_actors = actors.AwaitedService; _actors = actors;
_chat = chat; _chat = chat;
_penumbra = penumbra; _penumbra = penumbra;
_collectionEditor = collectionEditor; _collectionEditor = collectionEditor;

View file

@ -1,5 +1,6 @@
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data;
namespace Penumbra.Import.Structs; namespace Penumbra.Import.Structs;
@ -47,7 +48,7 @@ public partial struct MetaFileInfo
_ => false, _ => false,
}; };
public MetaFileInfo(IGamePathParser parser, string fileName) public MetaFileInfo(GamePathParser parser, string fileName)
{ {
// Set the primary type from the gamePath start. // Set the primary type from the gamePath start.
PrimaryType = parser.PathToObjectType(fileName); PrimaryType = parser.PathToObjectType(fileName);

View file

@ -1,4 +1,5 @@
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.Import.Structs; using Penumbra.Import.Structs;
using Penumbra.Meta; using Penumbra.Meta;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -28,7 +29,7 @@ public partial class TexToolsMeta
private readonly MetaFileManager _metaFileManager; private readonly MetaFileManager _metaFileManager;
public TexToolsMeta(MetaFileManager metaFileManager, IGamePathParser parser, byte[] data, bool keepDefault) public TexToolsMeta(MetaFileManager metaFileManager, GamePathParser parser, byte[] data, bool keepDefault)
{ {
_metaFileManager = metaFileManager; _metaFileManager = metaFileManager;
_keepDefault = keepDefault; _keepDefault = keepDefault;

View file

@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.Util; using Penumbra.Util;
@ -21,7 +21,7 @@ public unsafe class CollectionResolver
private readonly IClientState _clientState; private readonly IClientState _clientState;
private readonly IGameGui _gameGui; private readonly IGameGui _gameGui;
private readonly ActorService _actors; private readonly ActorManager _actors;
private readonly CutsceneService _cutscenes; private readonly CutsceneService _cutscenes;
private readonly Configuration _config; private readonly Configuration _config;
@ -30,7 +30,7 @@ public unsafe class CollectionResolver
private readonly DrawObjectState _drawObjectState; private readonly DrawObjectState _drawObjectState;
public CollectionResolver(PerformanceTracker performance, IdentifiedCollectionCache cache, IClientState clientState, IGameGui gameGui, public CollectionResolver(PerformanceTracker performance, IdentifiedCollectionCache cache, IClientState clientState, IGameGui gameGui,
ActorService actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager, ActorManager actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager,
TempCollectionManager tempCollections, DrawObjectState drawObjectState, HumanModelList humanModels) TempCollectionManager tempCollections, DrawObjectState drawObjectState, HumanModelList humanModels)
{ {
_performance = performance; _performance = performance;
@ -58,7 +58,7 @@ public unsafe class CollectionResolver
return _collectionManager.Active.ByType(CollectionType.Yourself) return _collectionManager.Active.ByType(CollectionType.Yourself)
?? _collectionManager.Active.Default; ?? _collectionManager.Active.Default;
var player = _actors.AwaitedService.GetCurrentPlayer(); var player = _actors.GetCurrentPlayer();
var _ = false; var _ = false;
return CollectionByIdentifier(player) return CollectionByIdentifier(player)
?? CheckYourself(player, gameObject) ?? CheckYourself(player, gameObject)
@ -147,7 +147,7 @@ public unsafe class CollectionResolver
return false; return false;
} }
var player = _actors.AwaitedService.GetCurrentPlayer(); var player = _actors.GetCurrentPlayer();
var notYetReady = false; var notYetReady = false;
var collection = (player.IsValid ? CollectionByIdentifier(player) : null) var collection = (player.IsValid ? CollectionByIdentifier(player) : null)
?? _collectionManager.Active.ByType(CollectionType.Yourself) ?? _collectionManager.Active.ByType(CollectionType.Yourself)
@ -163,7 +163,7 @@ public unsafe class CollectionResolver
/// </summary> /// </summary>
private ResolveData DefaultState(GameObject* gameObject) private ResolveData DefaultState(GameObject* gameObject)
{ {
var identifier = _actors.AwaitedService.FromObject(gameObject, out var owner, true, false, false); var identifier = _actors.FromObject(gameObject, out var owner, true, false, false);
if (identifier.Type is IdentifierType.Special) if (identifier.Type is IdentifierType.Special)
{ {
(identifier, var type) = _collectionManager.Active.Individuals.ConvertSpecialIdentifier(identifier); (identifier, var type) = _collectionManager.Active.Individuals.ConvertSpecialIdentifier(identifier);
@ -193,7 +193,7 @@ public unsafe class CollectionResolver
{ {
if (actor->ObjectIndex == 0 if (actor->ObjectIndex == 0
|| _cutscenes.GetParentIndex(actor->ObjectIndex) == 0 || _cutscenes.GetParentIndex(actor->ObjectIndex) == 0
|| identifier.Equals(_actors.AwaitedService.GetCurrentPlayer())) || identifier.Equals(_actors.GetCurrentPlayer()))
return _collectionManager.Active.ByType(CollectionType.Yourself); return _collectionManager.Active.ByType(CollectionType.Yourself);
return null; return null;
@ -242,7 +242,7 @@ public unsafe class CollectionResolver
if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == null) if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == null)
return null; return null;
var id = _actors.AwaitedService.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id, var id = _actors.CreateIndividualUnchecked(IdentifierType.Player, identifier.PlayerName, identifier.HomeWorld.Id,
ObjectKind.None, ObjectKind.None,
uint.MaxValue); uint.MaxValue);
return CheckYourself(id, owner) return CheckYourself(id, owner)

View file

@ -1,6 +1,6 @@
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Penumbra.GameData.Actors; using Penumbra.GameData.Enums;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
namespace Penumbra.Interop.PathResolving; namespace Penumbra.Interop.PathResolving;
@ -45,6 +45,9 @@ public class CutsceneService : IDisposable
/// <summary> Return the currently set index of a parent or -1 if none is set or the index is invalid. </summary> /// <summary> Return the currently set index of a parent or -1 if none is set or the index is invalid. </summary>
public int GetParentIndex(int idx) public int GetParentIndex(int idx)
=> GetParentIndex((ushort)idx);
public short GetParentIndex(ushort idx)
{ {
if (idx is >= CutsceneStartIdx and < CutsceneEndIdx) if (idx is >= CutsceneStartIdx and < CutsceneEndIdx)
return _copiedCharacters[idx - CutsceneStartIdx]; return _copiedCharacters[idx - CutsceneStartIdx];

View file

@ -7,6 +7,7 @@ using OtterGui;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.String; using Penumbra.String;
@ -19,7 +20,7 @@ using ModelType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase.M
namespace Penumbra.Interop.ResourceTree; namespace Penumbra.Interop.ResourceTree;
internal record GlobalResolveContext(IObjectIdentifier Identifier, ModCollection Collection, TreeBuildCache TreeBuildCache, bool WithUiData) internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollection Collection, TreeBuildCache TreeBuildCache, bool WithUiData)
{ {
public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128); public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128);
@ -28,8 +29,13 @@ internal record GlobalResolveContext(IObjectIdentifier Identifier, ModCollection
=> new(this, characterBase, slotIndex, slot, equipment, weaponType); => new(this, characterBase, slotIndex, slot, equipment, weaponType);
} }
internal partial record ResolveContext(GlobalResolveContext Global, Pointer<CharacterBase> CharacterBase, uint SlotIndex, internal partial record ResolveContext(
EquipSlot Slot, CharacterArmor Equipment, WeaponType WeaponType) GlobalResolveContext Global,
Pointer<CharacterBase> CharacterBase,
uint SlotIndex,
EquipSlot Slot,
CharacterArmor Equipment,
WeaponType WeaponType)
{ {
private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true);
@ -152,6 +158,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
{ {
if (mdl == null || mdl->ModelResourceHandle == null) if (mdl == null || mdl->ModelResourceHandle == null)
return null; return null;
var mdlResource = mdl->ModelResourceHandle; var mdlResource = mdl->ModelResourceHandle;
var path = ResolveModelPath(); var path = ResolveModelPath();
@ -224,6 +231,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
shpkNode.Name = "Shader Package"; shpkNode.Name = "Shader Package";
node.Children.Add(shpkNode); node.Children.Add(shpkNode);
} }
var shpkFile = Global.WithUiData && shpkNode != null ? Global.TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null; var shpkFile = Global.WithUiData && shpkNode != null ? Global.TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null;
var shpk = Global.WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null; var shpk = Global.WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null;
@ -310,13 +318,13 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
return node; return node;
} }
internal ResourceNode.UiData GuessModelUIData(Utf8GamePath gamePath) internal ResourceNode.UiData GuessModelUiData(Utf8GamePath gamePath)
{ {
var path = gamePath.ToString().Split('/', StringSplitOptions.RemoveEmptyEntries); var path = gamePath.ToString().Split('/', StringSplitOptions.RemoveEmptyEntries);
// Weapons intentionally left out. // Weapons intentionally left out.
var isEquipment = SafeGet(path, 0) == "chara" && SafeGet(path, 1) is "accessory" or "equipment"; var isEquipment = SafeGet(path, 0) == "chara" && SafeGet(path, 1) is "accessory" or "equipment";
if (isEquipment) if (isEquipment)
foreach (var item in Global.Identifier.Identify(Equipment.Set, Equipment.Variant, Slot.ToSlot())) foreach (var item in Global.Identifier.Identify(Equipment.Set, 0, Equipment.Variant, Slot.ToSlot()))
{ {
var name = Slot switch var name = Slot switch
{ {
@ -324,11 +332,11 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
EquipSlot.LFinger => "L: ", EquipSlot.LFinger => "L: ",
_ => string.Empty, _ => string.Empty,
} }
+ item.Name.ToString(); + item.Name;
return new ResourceNode.UiData(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item)); return new ResourceNode.UiData(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item));
} }
var dataFromPath = GuessUIDataFromPath(gamePath); var dataFromPath = GuessUiDataFromPath(gamePath);
if (dataFromPath.Name != null) if (dataFromPath.Name != null)
return dataFromPath; return dataFromPath;
@ -337,7 +345,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
: new ResourceNode.UiData(null, ChangedItemDrawer.ChangedItemIcon.Unknown); : new ResourceNode.UiData(null, ChangedItemDrawer.ChangedItemIcon.Unknown);
} }
internal ResourceNode.UiData GuessUIDataFromPath(Utf8GamePath gamePath) internal ResourceNode.UiData GuessUiDataFromPath(Utf8GamePath gamePath)
{ {
foreach (var obj in Global.Identifier.Identify(gamePath.ToString())) foreach (var obj in Global.Identifier.Identify(gamePath.ToString()))
{ {

View file

@ -3,8 +3,9 @@ using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Penumbra.Services;
using Penumbra.String.Classes; using Penumbra.String.Classes;
namespace Penumbra.Interop.ResourceTree; namespace Penumbra.Interop.ResourceTree;
@ -14,13 +15,13 @@ public class ResourceTreeFactory
private readonly IDataManager _gameData; private readonly IDataManager _gameData;
private readonly IObjectTable _objects; private readonly IObjectTable _objects;
private readonly CollectionResolver _collectionResolver; private readonly CollectionResolver _collectionResolver;
private readonly IdentifierService _identifier; private readonly ObjectIdentification _identifier;
private readonly Configuration _config; private readonly Configuration _config;
private readonly ActorService _actors; private readonly ActorManager _actors;
private readonly PathState _pathState; private readonly PathState _pathState;
public ResourceTreeFactory(IDataManager gameData, IObjectTable objects, CollectionResolver resolver, IdentifierService identifier, public ResourceTreeFactory(IDataManager gameData, IObjectTable objects, CollectionResolver resolver, ObjectIdentification identifier,
Configuration config, ActorService actors, PathState pathState) Configuration config, ActorManager actors, PathState pathState)
{ {
_gameData = gameData; _gameData = gameData;
_objects = objects; _objects = objects;
@ -88,12 +89,13 @@ public class ResourceTreeFactory
var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId; var networked = character.ObjectId != Dalamud.Game.ClientState.Objects.Types.GameObject.InvalidGameObjectId;
var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related, var tree = new ResourceTree(name, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
networked, collectionResolveData.ModCollection.Name); networked, collectionResolveData.ModCollection.Name);
var globalContext = new GlobalResolveContext(_identifier.AwaitedService, collectionResolveData.ModCollection, var globalContext = new GlobalResolveContext(_identifier, collectionResolveData.ModCollection,
cache, (flags & Flags.WithUiData) != 0); cache, (flags & Flags.WithUiData) != 0);
using (var _ = _pathState.EnterInternalResolve()) using (var _ = _pathState.EnterInternalResolve())
{ {
tree.LoadResources(globalContext); tree.LoadResources(globalContext);
} }
tree.FlatNodes.UnionWith(globalContext.Nodes.Values); tree.FlatNodes.UnionWith(globalContext.Nodes.Values);
tree.ProcessPostfix((node, _) => tree.FlatNodes.Add(node)); tree.ProcessPostfix((node, _) => tree.FlatNodes.Add(node));
@ -161,9 +163,9 @@ public class ResourceTreeFactory
var gamePath = node.PossibleGamePaths[0]; var gamePath = node.PossibleGamePaths[0];
node.SetUiData(node.Type switch node.SetUiData(node.Type switch
{ {
ResourceType.Imc => node.ResolveContext!.GuessModelUIData(gamePath).PrependName("IMC: "), ResourceType.Imc => node.ResolveContext!.GuessModelUiData(gamePath).PrependName("IMC: "),
ResourceType.Mdl => node.ResolveContext!.GuessModelUIData(gamePath), ResourceType.Mdl => node.ResolveContext!.GuessModelUiData(gamePath),
_ => node.ResolveContext!.GuessUIDataFromPath(gamePath), _ => node.ResolveContext!.GuessUiDataFromPath(gamePath),
}); });
} }
@ -215,7 +217,7 @@ public class ResourceTreeFactory
private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character, private unsafe (string Name, bool PlayerRelated) GetCharacterName(Dalamud.Game.ClientState.Objects.Types.Character character,
TreeBuildCache cache) TreeBuildCache cache)
{ {
var identifier = _actors.AwaitedService.FromObject((GameObject*)character.Address, out var owner, true, false, false); var identifier = _actors.FromObject((GameObject*)character.Address, out var owner, true, false, false);
switch (identifier.Type) switch (identifier.Type)
{ {
case IdentifierType.Player: return (identifier.PlayerName.ToString(), true); case IdentifierType.Player: return (identifier.PlayerName.ToString(), true);

View file

@ -1,36 +1,26 @@
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
namespace Penumbra.Interop.ResourceTree; namespace Penumbra.Interop.ResourceTree;
internal readonly struct TreeBuildCache internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorManager actors)
{ {
private readonly IDataManager _dataManager; private readonly Dictionary<FullPath, ShpkFile?> _shaderPackages = [];
private readonly ActorService _actors;
private readonly Dictionary<FullPath, ShpkFile?> _shaderPackages = new();
private readonly IObjectTable _objects;
public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors)
{
_dataManager = dataManager;
_objects = objects;
_actors = actors;
}
public unsafe bool IsLocalPlayerRelated(Character character) public unsafe bool IsLocalPlayerRelated(Character character)
{ {
var player = _objects[0]; var player = objects[0];
if (player == null) if (player == null)
return false; return false;
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)character.Address; var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)character.Address;
var parent = _actors.AwaitedService.ToCutsceneParent(gameObject->ObjectIndex); var parent = actors.ToCutsceneParent(gameObject->ObjectIndex);
var actualIndex = parent >= 0 ? (ushort)parent : gameObject->ObjectIndex; var actualIndex = parent >= 0 ? (ushort)parent : gameObject->ObjectIndex;
return actualIndex switch return actualIndex switch
{ {
@ -41,38 +31,38 @@ internal readonly struct TreeBuildCache
} }
public IEnumerable<Character> GetCharacters() public IEnumerable<Character> GetCharacters()
=> _objects.OfType<Character>(); => objects.OfType<Character>();
public IEnumerable<Character> GetLocalPlayerRelatedCharacters() public IEnumerable<Character> GetLocalPlayerRelatedCharacters()
{ {
var player = _objects[0]; var player = objects[0];
if (player == null) if (player == null)
yield break; yield break;
yield return (Character)player; yield return (Character)player;
var minion = _objects[1]; var minion = objects[1];
if (minion != null) if (minion != null)
yield return (Character)minion; yield return (Character)minion;
var playerId = player.ObjectId; var playerId = player.ObjectId;
for (var i = 2; i < ObjectIndex.CutsceneStart.Index; i += 2) for (var i = 2; i < ObjectIndex.CutsceneStart.Index; i += 2)
{ {
if (_objects[i] is Character owned && owned.OwnerId == playerId) if (objects[i] is Character owned && owned.OwnerId == playerId)
yield return owned; yield return owned;
} }
for (var i = ObjectIndex.CutsceneStart.Index; i < ObjectIndex.CharacterScreen.Index; ++i) for (var i = ObjectIndex.CutsceneStart.Index; i < ObjectIndex.CharacterScreen.Index; ++i)
{ {
var character = _objects[i] as Character; var character = objects[i] as Character;
if (character == null) if (character == null)
continue; continue;
var parent = _actors.AwaitedService.ToCutsceneParent(i); var parent = actors.ToCutsceneParent(i);
if (parent < 0) if (parent < 0)
continue; continue;
if (parent is 0 or 1 || _objects[parent]?.OwnerId == playerId) if (parent is 0 or 1 || objects[parent]?.OwnerId == playerId)
yield return character; yield return character;
} }
} }
@ -85,11 +75,11 @@ internal readonly struct TreeBuildCache
private unsafe bool GetOwnedId(ByteString playerName, uint playerId, int idx, [NotNullWhen(true)] out Character? character) private unsafe bool GetOwnedId(ByteString playerName, uint playerId, int idx, [NotNullWhen(true)] out Character? character)
{ {
character = _objects[idx] as Character; character = objects[idx] as Character;
if (character == null) if (character == null)
return false; return false;
var actorId = _actors.AwaitedService.FromObject(character, out var owner, true, true, true); var actorId = actors.FromObject(character, out var owner, true, true, true);
if (!actorId.IsValid) if (!actorId.IsValid)
return false; return false;
if (owner != null && owner->OwnerID != playerId) if (owner != null && owner->OwnerID != playerId)
@ -102,7 +92,7 @@ internal readonly struct TreeBuildCache
/// <summary> Try to read a shpk file from the given path and cache it on success. </summary> /// <summary> Try to read a shpk file from the given path and cache it on success. </summary>
public ShpkFile? ReadShaderPackage(FullPath path) public ShpkFile? ReadShaderPackage(FullPath path)
=> ReadFile(_dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes)); => ReadFile(dataManager, path, _shaderPackages, bytes => new ShpkFile(bytes));
private static T? ReadFile<T>(IDataManager dataManager, FullPath path, Dictionary<FullPath, T?> cache, Func<byte[], T> parseFile) private static T? ReadFile<T>(IDataManager dataManager, FullPath path, Dictionary<FullPath, T?> cache, Func<byte[], T> parseFile)
where T : class where T : class

View file

@ -9,7 +9,7 @@ using FFXIVClientStructs.Interop;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Actors; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character; using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;

View file

@ -5,6 +5,7 @@ using OtterGui.Compression;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.Import; using Penumbra.Import;
using Penumbra.Interop.Services; using Penumbra.Interop.Services;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
@ -24,11 +25,11 @@ public unsafe class MetaFileManager
internal readonly IDataManager GameData; internal readonly IDataManager GameData;
internal readonly ActiveCollectionData ActiveCollections; internal readonly ActiveCollectionData ActiveCollections;
internal readonly ValidityChecker ValidityChecker; internal readonly ValidityChecker ValidityChecker;
internal readonly IdentifierService Identifier; internal readonly ObjectIdentification Identifier;
internal readonly FileCompactor Compactor; internal readonly FileCompactor Compactor;
public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, IDataManager gameData, public MetaFileManager(CharacterUtility characterUtility, ResidentResourceManager residentResources, IDataManager gameData,
ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, IdentifierService identifier, ActiveCollectionData activeCollections, Configuration config, ValidityChecker validityChecker, ObjectIdentification identifier,
FileCompactor compactor, IGameInteropProvider interop) FileCompactor compactor, IGameInteropProvider interop)
{ {
CharacterUtility = characterUtility; CharacterUtility = characterUtility;

View file

@ -1,5 +1,4 @@
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
@ -41,7 +40,7 @@ public static class EquipmentSwap
: Array.Empty<EquipSlot>(); : Array.Empty<EquipSlot>();
} }
public static EquipItem[] CreateTypeSwap(MetaFileManager manager, IObjectIdentifier identifier, List<Swap> swaps, public static EquipItem[] CreateTypeSwap(MetaFileManager manager, ObjectIdentification identifier, List<Swap> swaps,
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips,
EquipSlot slotFrom, EquipItem itemFrom, EquipSlot slotTo, EquipItem itemTo) EquipSlot slotFrom, EquipItem itemFrom, EquipSlot slotTo, EquipItem itemTo)
{ {
@ -99,7 +98,7 @@ public static class EquipmentSwap
return affectedItems; return affectedItems;
} }
public static EquipItem[] CreateItemSwap(MetaFileManager manager, IObjectIdentifier identifier, List<Swap> swaps, public static EquipItem[] CreateItemSwap(MetaFileManager manager, ObjectIdentification identifier, List<Swap> swaps,
Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipItem itemFrom, Func<Utf8GamePath, FullPath> redirections, Func<MetaManipulation, MetaManipulation> manips, EquipItem itemFrom,
EquipItem itemTo, bool rFinger = true, bool lFinger = true) EquipItem itemTo, bool rFinger = true, bool lFinger = true)
{ {
@ -247,7 +246,7 @@ public static class EquipmentSwap
variant = i.Variant; variant = i.Variant;
} }
private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, IObjectIdentifier identifier, EquipSlot slotFrom, private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom,
SetId idFrom, SetId idTo, Variant variantFrom) SetId idFrom, SetId idTo, Variant variantFrom)
{ {
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default); var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
@ -256,11 +255,8 @@ public static class EquipmentSwap
Variant[] variants; Variant[] variants;
if (idFrom == idTo) if (idFrom == idTo)
{ {
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray(); items = identifier.Identify(idFrom, 0, variantFrom, slotFrom).ToArray();
variants = new[] variants = [variantFrom];
{
variantFrom,
};
} }
else else
{ {

View file

@ -1,5 +1,5 @@
using Lumina.Excel.GeneratedSheets;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -7,17 +7,16 @@ using Penumbra.String.Classes;
using Penumbra.Meta; using Penumbra.Meta;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses; using Penumbra.Mods.Subclasses;
using Penumbra.Services;
namespace Penumbra.Mods.ItemSwap; namespace Penumbra.Mods.ItemSwap;
public class ItemSwapContainer public class ItemSwapContainer
{ {
private readonly MetaFileManager _manager; private readonly MetaFileManager _manager;
private readonly IdentifierService _identifier; private readonly ObjectIdentification _identifier;
private Dictionary<Utf8GamePath, FullPath> _modRedirections = new(); private Dictionary<Utf8GamePath, FullPath> _modRedirections = [];
private HashSet<MetaManipulation> _modManipulations = new(); private HashSet<MetaManipulation> _modManipulations = [];
public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections
=> _modRedirections; => _modRedirections;
@ -25,7 +24,7 @@ public class ItemSwapContainer
public IReadOnlySet<MetaManipulation> ModManipulations public IReadOnlySet<MetaManipulation> ModManipulations
=> _modManipulations; => _modManipulations;
public readonly List<Swap> Swaps = new(); public readonly List<Swap> Swaps = [];
public bool Loaded { get; private set; } public bool Loaded { get; private set; }
@ -107,7 +106,7 @@ public class ItemSwapContainer
} }
} }
public ItemSwapContainer(MetaFileManager manager, IdentifierService identifier) public ItemSwapContainer(MetaFileManager manager, ObjectIdentification identifier)
{ {
_manager = manager; _manager = manager;
_identifier = identifier; _identifier = identifier;
@ -130,7 +129,7 @@ public class ItemSwapContainer
{ {
Swaps.Clear(); Swaps.Clear();
Loaded = false; Loaded = false;
var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection), var ret = EquipmentSwap.CreateItemSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection),
from, to, useRightRing, useLeftRing); from, to, useRightRing, useLeftRing);
Loaded = true; Loaded = true;
return ret; return ret;
@ -140,7 +139,7 @@ public class ItemSwapContainer
{ {
Swaps.Clear(); Swaps.Clear();
Loaded = false; Loaded = false;
var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier.AwaitedService, Swaps, PathResolver(collection), MetaResolver(collection), var ret = EquipmentSwap.CreateTypeSwap(_manager, _identifier, Swaps, PathResolver(collection), MetaResolver(collection),
slotFrom, from, slotTo, to); slotFrom, from, slotTo, to);
Loaded = true; Loaded = true;
return ret; return ret;

View file

@ -1,5 +1,4 @@
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.GameData;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
@ -10,10 +9,11 @@ namespace Penumbra.Mods.Manager;
public class ModCacheManager : IDisposable public class ModCacheManager : IDisposable
{ {
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly IdentifierService _identifier; private readonly ObjectIdentification _identifier;
private readonly ModStorage _modManager; private readonly ModStorage _modManager;
private bool _updatingItems = false;
public ModCacheManager(CommunicatorService communicator, IdentifierService identifier, ModStorage modStorage) public ModCacheManager(CommunicatorService communicator, ObjectIdentification identifier, ModStorage modStorage)
{ {
_communicator = communicator; _communicator = communicator;
_identifier = identifier; _identifier = identifier;
@ -23,8 +23,7 @@ public class ModCacheManager : IDisposable
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModCacheManager); _communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModCacheManager);
_communicator.ModDataChanged.Subscribe(OnModDataChange, ModDataChanged.Priority.ModCacheManager); _communicator.ModDataChanged.Subscribe(OnModDataChange, ModDataChanged.Priority.ModCacheManager);
_communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.ModCacheManager); _communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.ModCacheManager);
if (!identifier.Valid) identifier.Awaiter.ContinueWith(_ => OnIdentifierCreation());
identifier.FinishedCreation += OnIdentifierCreation;
OnModDiscoveryFinished(); OnModDiscoveryFinished();
} }
@ -37,7 +36,7 @@ public class ModCacheManager : IDisposable
} }
/// <summary> Compute the items changed by a given meta manipulation and put them into the changedItems dictionary. </summary> /// <summary> Compute the items changed by a given meta manipulation and put them into the changedItems dictionary. </summary>
public static void ComputeChangedItems(IObjectIdentifier identifier, IDictionary<string, object?> changedItems, MetaManipulation manip) public static void ComputeChangedItems(ObjectIdentification identifier, IDictionary<string, object?> changedItems, MetaManipulation manip)
{ {
switch (manip.ManipulationType) switch (manip.ManipulationType)
{ {
@ -155,10 +154,7 @@ public class ModCacheManager : IDisposable
=> Parallel.ForEach(_modManager, Refresh); => Parallel.ForEach(_modManager, Refresh);
private void OnIdentifierCreation() private void OnIdentifierCreation()
{ => Parallel.ForEach(_modManager, UpdateChangedItems);
Parallel.ForEach(_modManager, UpdateChangedItems);
_identifier.FinishedCreation -= OnIdentifierCreation;
}
private static void UpdateFileCount(Mod mod) private static void UpdateFileCount(Mod mod)
=> mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count); => mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count);
@ -177,18 +173,23 @@ public class ModCacheManager : IDisposable
private void UpdateChangedItems(Mod mod) private void UpdateChangedItems(Mod mod)
{ {
if (_updatingItems)
return;
_updatingItems = true;
var changedItems = (SortedList<string, object?>)mod.ChangedItems; var changedItems = (SortedList<string, object?>)mod.ChangedItems;
changedItems.Clear(); changedItems.Clear();
if (!_identifier.Valid) if (!_identifier.Awaiter.IsCompletedSuccessfully)
return; return;
foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys))) foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys)))
_identifier.AwaitedService.Identify(changedItems, gamePath.ToString()); _identifier.Identify(changedItems, gamePath.ToString());
foreach (var manip in mod.AllSubMods.SelectMany(m => m.Manipulations)) foreach (var manip in mod.AllSubMods.SelectMany(m => m.Manipulations))
ComputeChangedItems(_identifier.AwaitedService, changedItems, manip); ComputeChangedItems(_identifier, changedItems, manip);
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant())); mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
_updatingItems = false;
} }
private static void UpdateCounts(Mod mod) private static void UpdateCounts(Mod mod)

View file

@ -5,7 +5,7 @@ using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.GameData; using Penumbra.GameData.Data;
using Penumbra.Import; using Penumbra.Import;
using Penumbra.Import.Structs; using Penumbra.Import.Structs;
using Penumbra.Meta; using Penumbra.Meta;
@ -17,7 +17,7 @@ using Penumbra.String.Classes;
namespace Penumbra.Mods; namespace Penumbra.Mods;
public partial class ModCreator(SaveService _saveService, Configuration config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager, public partial class ModCreator(SaveService _saveService, Configuration config, ModDataEditor _dataEditor, MetaFileManager _metaFileManager,
IGamePathParser _gamePathParser) GamePathParser _gamePathParser)
{ {
public readonly Configuration Config = config; public readonly Configuration Config = config;

View file

@ -1,4 +1,5 @@
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets; using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -6,7 +7,6 @@ using OtterGui;
using OtterGui.Log; using OtterGui.Log;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Util;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Cache; using Penumbra.Collections.Cache;
using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.ResourceLoading;
@ -19,6 +19,7 @@ using Penumbra.UI.Tabs;
using ChangedItemClick = Penumbra.Communication.ChangedItemClick; using ChangedItemClick = Penumbra.Communication.ChangedItemClick;
using ChangedItemHover = Penumbra.Communication.ChangedItemHover; using ChangedItemHover = Penumbra.Communication.ChangedItemHover;
using OtterGui.Tasks; using OtterGui.Tasks;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.UI; using Penumbra.UI;
@ -53,9 +54,7 @@ public class Penumbra : IDalamudPlugin
{ {
try try
{ {
var startTimer = new StartTracker(); _services = ServiceManager.CreateProvider(this, pluginInterface, Log);
using var timer = startTimer.Measure(StartTimeType.Total);
_services = ServiceManager.CreateProvider(this, pluginInterface, Log, startTimer);
Messager = _services.GetRequiredService<MessageService>(); Messager = _services.GetRequiredService<MessageService>();
_validityChecker = _services.GetRequiredService<ValidityChecker>(); _validityChecker = _services.GetRequiredService<ValidityChecker>();
var startup = _services.GetRequiredService<DalamudServices>().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s) var startup = _services.GetRequiredService<DalamudServices>().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s)
@ -78,10 +77,7 @@ public class Penumbra : IDalamudPlugin
_services.GetRequiredService<ModCacheManager>(); // Initialize because not required anywhere else. _services.GetRequiredService<ModCacheManager>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ModelResourceHandleUtility>(); // Initialize because not required anywhere else. _services.GetRequiredService<ModelResourceHandleUtility>(); // Initialize because not required anywhere else.
_collectionManager.Caches.CreateNecessaryCaches(); _collectionManager.Caches.CreateNecessaryCaches();
using (var t = _services.GetRequiredService<StartTracker>().Measure(StartTimeType.PathResolver))
{
_services.GetRequiredService<PathResolver>(); _services.GetRequiredService<PathResolver>();
}
_services.GetRequiredService<SkinFixer>(); _services.GetRequiredService<SkinFixer>();
@ -108,7 +104,6 @@ public class Penumbra : IDalamudPlugin
private void SetupApi() private void SetupApi()
{ {
using var timer = _services.GetRequiredService<StartTracker>().Measure(StartTimeType.Api);
var api = _services.GetRequiredService<IPenumbraApi>(); var api = _services.GetRequiredService<IPenumbraApi>();
_services.GetRequiredService<PenumbraIpcProviders>(); _services.GetRequiredService<PenumbraIpcProviders>();
_communicatorService.ChangedItemHover.Subscribe(it => _communicatorService.ChangedItemHover.Subscribe(it =>
@ -128,7 +123,6 @@ public class Penumbra : IDalamudPlugin
{ {
AsyncTask.Run(() => AsyncTask.Run(() =>
{ {
using var tInterface = _services.GetRequiredService<StartTracker>().Measure(StartTimeType.Interface);
var system = _services.GetRequiredService<PenumbraWindowSystem>(); var system = _services.GetRequiredService<PenumbraWindowSystem>();
system.Window.Setup(this, _services.GetRequiredService<ConfigTabBar>()); system.Window.Setup(this, _services.GetRequiredService<ConfigTabBar>());
_services.GetRequiredService<CommandHandler>(); _services.GetRequiredService<CommandHandler>();

View file

@ -1,15 +1,13 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using Penumbra.Util;
namespace Penumbra.Services; namespace Penumbra.Services;
public class BackupService public class BackupService
{ {
public BackupService(Logger logger, StartTracker timer, FilenameService fileNames) public BackupService(Logger logger, FilenameService fileNames)
{ {
using var t = timer.Measure(StartTimeType.Backup);
var files = PenumbraFiles(fileNames); var files = PenumbraFiles(fileNames);
Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files); Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files);
} }

View file

@ -7,7 +7,10 @@ using Penumbra.Api;
using Penumbra.Collections.Cache; using Penumbra.Collections.Cache;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.Import.Textures; using Penumbra.Import.Textures;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.ResourceLoading;
@ -24,17 +27,17 @@ using Penumbra.UI.Classes;
using Penumbra.UI.ModsTab; using Penumbra.UI.ModsTab;
using Penumbra.UI.ResourceWatcher; using Penumbra.UI.ResourceWatcher;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
using Penumbra.UI.Tabs.Debug;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
namespace Penumbra.Services; namespace Penumbra.Services;
public static class ServiceManager public static class ServiceManager
{ {
public static ServiceProvider CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log, StartTracker startTimer) public static ServiceProvider CreateProvider(Penumbra penumbra, DalamudPluginInterface pi, Logger log)
{ {
var services = new ServiceCollection() var services = new ServiceCollection()
.AddSingleton(log) .AddSingleton(log)
.AddSingleton(startTimer)
.AddSingleton(penumbra) .AddSingleton(penumbra)
.AddDalamud(pi) .AddDalamud(pi)
.AddMeta() .AddMeta()
@ -47,7 +50,9 @@ public static class ServiceManager
.AddResolvers() .AddResolvers()
.AddInterface() .AddInterface()
.AddModEditor() .AddModEditor()
.AddApi(); .AddApi()
.AddDataContainers()
.AddAsyncServices();
return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true }); return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
} }
@ -59,6 +64,22 @@ public static class ServiceManager
return services; return services;
} }
private static IServiceCollection AddDataContainers(this IServiceCollection services)
{
foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes()
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IDataContainer))))
services.AddSingleton(type);
return services;
}
private static IServiceCollection AddAsyncServices(this IServiceCollection services)
{
foreach (var type in typeof(IDataContainer).Assembly.GetExportedTypes()
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncService))))
services.AddSingleton(type);
return services;
}
private static IServiceCollection AddMeta(this IServiceCollection services) private static IServiceCollection AddMeta(this IServiceCollection services)
=> services.AddSingleton<ValidityChecker>() => services.AddSingleton<ValidityChecker>()
.AddSingleton<PerformanceTracker>() .AddSingleton<PerformanceTracker>()
@ -71,17 +92,19 @@ public static class ServiceManager
private static IServiceCollection AddGameData(this IServiceCollection services) private static IServiceCollection AddGameData(this IServiceCollection services)
=> services.AddSingleton<IGamePathParser, GamePathParser>() => services.AddSingleton<GamePathParser>()
.AddSingleton<IdentifierService>()
.AddSingleton<StainService>() .AddSingleton<StainService>()
.AddSingleton<ItemService>()
.AddSingleton<ActorService>()
.AddSingleton<HumanModelList>(); .AddSingleton<HumanModelList>();
private static IServiceCollection AddInterop(this IServiceCollection services) private static IServiceCollection AddInterop(this IServiceCollection services)
=> services.AddSingleton<GameEventManager>() => services.AddSingleton<GameEventManager>()
.AddSingleton<FrameworkManager>() .AddSingleton<FrameworkManager>()
.AddSingleton<CutsceneService>() .AddSingleton<CutsceneService>()
.AddSingleton(p =>
{
var cutsceneService = p.GetRequiredService<CutsceneService>();
return new CutsceneResolver(cutsceneService.GetParentIndex);
})
.AddSingleton<CharacterUtility>() .AddSingleton<CharacterUtility>()
.AddSingleton<ResourceManagerService>() .AddSingleton<ResourceManagerService>()
.AddSingleton<ResourceService>() .AddSingleton<ResourceService>()
@ -173,7 +196,8 @@ public static class ServiceManager
.AddSingleton<ResourceWatcher>() .AddSingleton<ResourceWatcher>()
.AddSingleton<ItemSwapTab>() .AddSingleton<ItemSwapTab>()
.AddSingleton<ModMergeTab>() .AddSingleton<ModMergeTab>()
.AddSingleton<ChangedItemDrawer>(); .AddSingleton<ChangedItemDrawer>()
.AddSingleton(p => new Diagnostics(p));
private static IServiceCollection AddModEditor(this IServiceCollection services) private static IServiceCollection AddModEditor(this IServiceCollection services)
=> services.AddSingleton<ModFileCollection>() => services.AddSingleton<ModFileCollection>()

View file

@ -1,5 +1,4 @@
using OtterGui.Tasks; using OtterGui.Tasks;
using Penumbra.Util;
namespace Penumbra.Services; namespace Penumbra.Services;
@ -12,10 +11,9 @@ public abstract class SyncServiceWrapper<T> : IDisposable
public bool Valid public bool Valid
=> !_isDisposed; => !_isDisposed;
protected SyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func<T> factory) protected SyncServiceWrapper(string name, Func<T> factory)
{ {
Name = name; Name = name;
using var timer = tracker.Measure(type);
Service = factory(); Service = factory();
Penumbra.Log.Verbose($"[{Name}] Created."); Penumbra.Log.Verbose($"[{Name}] Created.");
} }
@ -54,32 +52,6 @@ public abstract class AsyncServiceWrapper<T> : IDisposable
private bool _isDisposed; private bool _isDisposed;
protected AsyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func<T> factory)
{
Name = name;
_task = TrackedTask.Run(() =>
{
using var timer = tracker.Measure(type);
var service = factory();
if (_isDisposed)
{
if (service is IDisposable d)
d.Dispose();
}
else
{
Service = service;
Penumbra.Log.Verbose($"[{Name}] Created.");
_task = null;
}
});
_task.ContinueWith((t, x) =>
{
if (!_isDisposed)
FinishedCreation?.Invoke();
}, null);
}
protected AsyncServiceWrapper(string name, Func<T> factory) protected AsyncServiceWrapper(string name, Func<T> factory)
{ {
Name = name; Name = name;

View file

@ -4,7 +4,7 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using ImGuiNET; using ImGuiNET;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.UI.AdvancedWindow; using Penumbra.UI.AdvancedWindow;
using Penumbra.Util; using Penumbra.Util;
@ -71,17 +71,16 @@ public class StainService : IDisposable
} }
} }
public readonly StainData StainData; public readonly DictStains StainData;
public readonly FilterComboColors StainCombo; public readonly FilterComboColors StainCombo;
public readonly StmFile StmFile; public readonly StmFile StmFile;
public readonly StainTemplateCombo TemplateCombo; public readonly StainTemplateCombo TemplateCombo;
public StainService(StartTracker timer, DalamudPluginInterface pluginInterface, IDataManager dataManager, IPluginLog dalamudLog) public StainService(DalamudPluginInterface pluginInterface, IDataManager dataManager, IPluginLog dalamudLog)
{ {
using var t = timer.Measure(StartTimeType.Stains); StainData = new DictStains(pluginInterface, dalamudLog, dataManager);
StainData = new StainData(pluginInterface, dataManager, dataManager.Language, dalamudLog);
StainCombo = new FilterComboColors(140, StainCombo = new FilterComboColors(140,
() => StainData.Data.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))).ToList(), () => StainData.Value.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))).ToList(),
Penumbra.Log); Penumbra.Log);
StmFile = new StmFile(dataManager); StmFile = new StmFile(dataManager);
TemplateCombo = new StainTemplateCombo(StainCombo, StmFile); TemplateCombo = new StainTemplateCombo(StainCombo, StmFile);

View file

@ -1,34 +0,0 @@
using Dalamud.Game;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Penumbra.GameData;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.Interop.PathResolving;
using Penumbra.Util;
namespace Penumbra.Services;
public sealed class IdentifierService : AsyncServiceWrapper<IObjectIdentifier>
{
public IdentifierService(StartTracker tracker, DalamudPluginInterface pi, IDataManager data, ItemService items, IPluginLog log)
: base(nameof(IdentifierService), tracker, StartTimeType.Identifier,
() => GameData.GameData.GetIdentifier(pi, data, items.AwaitedService, log))
{ }
}
public sealed class ItemService : AsyncServiceWrapper<ItemData>
{
public ItemService(StartTracker tracker, DalamudPluginInterface pi, IDataManager gameData, IPluginLog log)
: base(nameof(ItemService), tracker, StartTimeType.Items, () => new ItemData(pi, gameData, gameData.Language, log))
{ }
}
public sealed class ActorService : AsyncServiceWrapper<ActorManager>
{
public ActorService(StartTracker tracker, DalamudPluginInterface pi, IObjectTable objects, IClientState clientState,
IFramework framework, IDataManager gameData, IGameGui gui, CutsceneService cutscene, IPluginLog log, IGameInteropProvider interop)
: base(nameof(ActorService), tracker, StartTimeType.Actors,
() => new ActorManager(pi, objects, clientState, framework, interop, gameData, gui, idx => (short)cutscene.GetParentIndex(idx), log))
{ }
}

View file

@ -1,8 +1,5 @@
using Dalamud.Interface;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Dalamud.Utility;
using ImGuiNET; using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Raii; using OtterGui.Raii;
@ -11,6 +8,7 @@ using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Meta; using Penumbra.Meta;
@ -27,16 +25,14 @@ public class ItemSwapTab : IDisposable, ITab
{ {
private readonly Configuration _config; private readonly Configuration _config;
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly ItemService _itemService;
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly MetaFileManager _metaFileManager; private readonly MetaFileManager _metaFileManager;
public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager, public ItemSwapTab(CommunicatorService communicator, ItemData itemService, CollectionManager collectionManager,
ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager, Configuration config) ModManager modManager, ObjectIdentification identifier, MetaFileManager metaFileManager, Configuration config)
{ {
_communicator = communicator; _communicator = communicator;
_itemService = itemService;
_collectionManager = collectionManager; _collectionManager = collectionManager;
_modManager = modManager; _modManager = modManager;
_metaFileManager = metaFileManager; _metaFileManager = metaFileManager;
@ -46,15 +42,15 @@ public class ItemSwapTab : IDisposable, ITab
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)> _selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
{ {
// @formatter:off // @formatter:off
[SwapType.Hat] = (new ItemSelector(_itemService, FullEquipType.Head), new ItemSelector(_itemService, FullEquipType.Head), "Take this Hat", "and put it on this one" ), [SwapType.Hat] = (new ItemSelector(itemService, FullEquipType.Head), new ItemSelector(itemService, FullEquipType.Head), "Take this Hat", "and put it on this one" ),
[SwapType.Top] = (new ItemSelector(_itemService, FullEquipType.Body), new ItemSelector(_itemService, FullEquipType.Body), "Take this Top", "and put it on this one" ), [SwapType.Top] = (new ItemSelector(itemService, FullEquipType.Body), new ItemSelector(itemService, FullEquipType.Body), "Take this Top", "and put it on this one" ),
[SwapType.Gloves] = (new ItemSelector(_itemService, FullEquipType.Hands), new ItemSelector(_itemService, FullEquipType.Hands), "Take these Gloves", "and put them on these" ), [SwapType.Gloves] = (new ItemSelector(itemService, FullEquipType.Hands), new ItemSelector(itemService, FullEquipType.Hands), "Take these Gloves", "and put them on these" ),
[SwapType.Pants] = (new ItemSelector(_itemService, FullEquipType.Legs), new ItemSelector(_itemService, FullEquipType.Legs), "Take these Pants", "and put them on these" ), [SwapType.Pants] = (new ItemSelector(itemService, FullEquipType.Legs), new ItemSelector(itemService, FullEquipType.Legs), "Take these Pants", "and put them on these" ),
[SwapType.Shoes] = (new ItemSelector(_itemService, FullEquipType.Feet), new ItemSelector(_itemService, FullEquipType.Feet), "Take these Shoes", "and put them on these" ), [SwapType.Shoes] = (new ItemSelector(itemService, FullEquipType.Feet), new ItemSelector(itemService, FullEquipType.Feet), "Take these Shoes", "and put them on these" ),
[SwapType.Earrings] = (new ItemSelector(_itemService, FullEquipType.Ears), new ItemSelector(_itemService, FullEquipType.Ears), "Take these Earrings", "and put them on these" ), [SwapType.Earrings] = (new ItemSelector(itemService, FullEquipType.Ears), new ItemSelector(itemService, FullEquipType.Ears), "Take these Earrings", "and put them on these" ),
[SwapType.Necklace] = (new ItemSelector(_itemService, FullEquipType.Neck), new ItemSelector(_itemService, FullEquipType.Neck), "Take this Necklace", "and put it on this one" ), [SwapType.Necklace] = (new ItemSelector(itemService, FullEquipType.Neck), new ItemSelector(itemService, FullEquipType.Neck), "Take this Necklace", "and put it on this one" ),
[SwapType.Bracelet] = (new ItemSelector(_itemService, FullEquipType.Wrists), new ItemSelector(_itemService, FullEquipType.Wrists), "Take these Bracelets", "and put them on these" ), [SwapType.Bracelet] = (new ItemSelector(itemService, FullEquipType.Wrists), new ItemSelector(itemService, FullEquipType.Wrists), "Take these Bracelets", "and put them on these" ),
[SwapType.Ring] = (new ItemSelector(_itemService, FullEquipType.Finger), new ItemSelector(_itemService, FullEquipType.Finger), "Take this Ring", "and put it on this one" ), [SwapType.Ring] = (new ItemSelector(itemService, FullEquipType.Finger), new ItemSelector(itemService, FullEquipType.Finger), "Take this Ring", "and put it on this one" ),
// @formatter:on // @formatter:on
}; };
@ -131,8 +127,8 @@ public class ItemSwapTab : IDisposable, ITab
private class ItemSelector : FilterComboCache<EquipItem> private class ItemSelector : FilterComboCache<EquipItem>
{ {
public ItemSelector(ItemService data, FullEquipType type) public ItemSelector(ItemData data, FullEquipType type)
: base(() => data.AwaitedService[type], Penumbra.Log) : base(() => data.ByType[type], Penumbra.Log)
{ } { }
protected override string ToString(EquipItem obj) protected override string ToString(EquipItem obj)

View file

@ -8,6 +8,7 @@ using OtterGui.Classes;
using Penumbra.GameData; using Penumbra.GameData;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.GameData.Interop;
using Penumbra.String; using Penumbra.String;
using static Penumbra.GameData.Files.ShpkFile; using static Penumbra.GameData.Files.ShpkFile;

View file

@ -3,6 +3,7 @@ using Lumina.Misc;
using OtterGui; using OtterGui;
using Penumbra.GameData.Data; using Penumbra.GameData.Data;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.GameData.Interop;
namespace Penumbra.UI.AdvancedWindow; namespace Penumbra.UI.AdvancedWindow;

View file

@ -22,7 +22,7 @@ public sealed class CollectionPanel : IDisposable
private readonly CollectionStorage _collections; private readonly CollectionStorage _collections;
private readonly ActiveCollections _active; private readonly ActiveCollections _active;
private readonly CollectionSelector _selector; private readonly CollectionSelector _selector;
private readonly ActorService _actors; private readonly ActorManager _actors;
private readonly ITargetManager _targets; private readonly ITargetManager _targets;
private readonly IndividualAssignmentUi _individualAssignmentUi; private readonly IndividualAssignmentUi _individualAssignmentUi;
private readonly InheritanceUi _inheritanceUi; private readonly InheritanceUi _inheritanceUi;
@ -37,7 +37,7 @@ public sealed class CollectionPanel : IDisposable
private int _draggedIndividualAssignment = -1; private int _draggedIndividualAssignment = -1;
public CollectionPanel(DalamudPluginInterface pi, CommunicatorService communicator, CollectionManager manager, public CollectionPanel(DalamudPluginInterface pi, CommunicatorService communicator, CollectionManager manager,
CollectionSelector selector, ActorService actors, ITargetManager targets, ModStorage mods) CollectionSelector selector, ActorManager actors, ITargetManager targets, ModStorage mods)
{ {
_collections = manager.Storage; _collections = manager.Storage;
_active = manager.Active; _active = manager.Active;
@ -382,11 +382,11 @@ public sealed class CollectionPanel : IDisposable
} }
private void DrawCurrentCharacter(Vector2 width) private void DrawCurrentCharacter(Vector2 width)
=> DrawIndividualButton("Current Character", width, string.Empty, 'c', _actors.AwaitedService.GetCurrentPlayer()); => DrawIndividualButton("Current Character", width, string.Empty, 'c', _actors.GetCurrentPlayer());
private void DrawCurrentTarget(Vector2 width) private void DrawCurrentTarget(Vector2 width)
=> DrawIndividualButton("Current Target", width, string.Empty, 't', => DrawIndividualButton("Current Target", width, string.Empty, 't',
_actors.AwaitedService.FromObject(_targets.Target, false, true, true)); _actors.FromObject(_targets.Target, false, true, true));
private void DrawNewPlayer(Vector2 width) private void DrawNewPlayer(Vector2 width)
=> DrawIndividualButton("New Player", width, _individualAssignmentUi.PlayerTooltip, 'p', => DrawIndividualButton("New Player", width, _individualAssignmentUi.PlayerTooltip, 'p',

View file

@ -5,6 +5,9 @@ using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Communication; using Penumbra.Communication;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui;
using Penumbra.GameData.Structs;
using Penumbra.Services; using Penumbra.Services;
namespace Penumbra.UI.CollectionTab; namespace Penumbra.UI.CollectionTab;
@ -12,7 +15,7 @@ namespace Penumbra.UI.CollectionTab;
public class IndividualAssignmentUi : IDisposable public class IndividualAssignmentUi : IDisposable
{ {
private readonly CommunicatorService _communicator; private readonly CommunicatorService _communicator;
private readonly ActorService _actorService; private readonly ActorManager _actors;
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
private WorldCombo _worldCombo = null!; private WorldCombo _worldCombo = null!;
@ -24,16 +27,13 @@ public class IndividualAssignmentUi : IDisposable
private bool _ready; private bool _ready;
public IndividualAssignmentUi(CommunicatorService communicator, ActorService actors, CollectionManager collectionManager) public IndividualAssignmentUi(CommunicatorService communicator, ActorManager actors, CollectionManager collectionManager)
{ {
_communicator = communicator; _communicator = communicator;
_actorService = actors; _actors = actors;
_collectionManager = collectionManager; _collectionManager = collectionManager;
_communicator.CollectionChange.Subscribe(UpdateIdentifiers, CollectionChange.Priority.IndividualAssignmentUi); _communicator.CollectionChange.Subscribe(UpdateIdentifiers, CollectionChange.Priority.IndividualAssignmentUi);
if (_actorService.Valid) _actors.Awaiter.ContinueWith(_ => SetupCombos());
SetupCombos();
else
_actorService.FinishedCreation += SetupCombos;
} }
public string PlayerTooltip { get; private set; } = NewPlayerTooltipEmpty; public string PlayerTooltip { get; private set; } = NewPlayerTooltipEmpty;
@ -91,10 +91,10 @@ public class IndividualAssignmentUi : IDisposable
// Input Selections. // Input Selections.
private string _newCharacterName = string.Empty; private string _newCharacterName = string.Empty;
private ObjectKind _newKind = ObjectKind.BattleNpc; private ObjectKind _newKind = ObjectKind.BattleNpc;
private ActorIdentifier[] _playerIdentifiers = Array.Empty<ActorIdentifier>(); private ActorIdentifier[] _playerIdentifiers = [];
private ActorIdentifier[] _retainerIdentifiers = Array.Empty<ActorIdentifier>(); private ActorIdentifier[] _retainerIdentifiers = [];
private ActorIdentifier[] _npcIdentifiers = Array.Empty<ActorIdentifier>(); private ActorIdentifier[] _npcIdentifiers = [];
private ActorIdentifier[] _ownedIdentifiers = Array.Empty<ActorIdentifier>(); private ActorIdentifier[] _ownedIdentifiers = [];
private const string NewPlayerTooltipEmpty = "Please enter a valid player name and choose an available world or 'Any World'."; private const string NewPlayerTooltipEmpty = "Please enter a valid player name and choose an available world or 'Any World'.";
private const string NewRetainerTooltipEmpty = "Please enter a valid retainer name."; private const string NewRetainerTooltipEmpty = "Please enter a valid retainer name.";
@ -126,14 +126,13 @@ public class IndividualAssignmentUi : IDisposable
/// <summary> Create combos when ready. </summary> /// <summary> Create combos when ready. </summary>
private void SetupCombos() private void SetupCombos()
{ {
_worldCombo = new WorldCombo(_actorService.AwaitedService.Data.Worlds, Penumbra.Log); _worldCombo = new WorldCombo(_actors.Data.Worlds, Penumbra.Log, WorldId.AnyWorld);
_mountCombo = new NpcCombo("##mountCombo", _actorService.AwaitedService.Data.Mounts, Penumbra.Log); _mountCombo = new NpcCombo("##mountCombo", _actors.Data.Mounts, Penumbra.Log);
_companionCombo = new NpcCombo("##companionCombo", _actorService.AwaitedService.Data.Companions, Penumbra.Log); _companionCombo = new NpcCombo("##companionCombo", _actors.Data.Companions, Penumbra.Log);
_ornamentCombo = new NpcCombo("##ornamentCombo", _actorService.AwaitedService.Data.Ornaments, Penumbra.Log); _ornamentCombo = new NpcCombo("##ornamentCombo", _actors.Data.Ornaments, Penumbra.Log);
_bnpcCombo = new NpcCombo("##bnpcCombo", _actorService.AwaitedService.Data.BNpcs, Penumbra.Log); _bnpcCombo = new NpcCombo("##bnpcCombo", _actors.Data.BNpcs, Penumbra.Log);
_enpcCombo = new NpcCombo("##enpcCombo", _actorService.AwaitedService.Data.ENpcs, Penumbra.Log); _enpcCombo = new NpcCombo("##enpcCombo", _actors.Data.ENpcs, Penumbra.Log);
_ready = true; _ready = true;
_actorService.FinishedCreation -= SetupCombos;
} }
private void UpdateIdentifiers(CollectionType type, ModCollection? _1, ModCollection? _2, string _3) private void UpdateIdentifiers(CollectionType type, ModCollection? _1, ModCollection? _2, string _3)
@ -146,16 +145,16 @@ public class IndividualAssignmentUi : IDisposable
{ {
var combo = GetNpcCombo(_newKind); var combo = GetNpcCombo(_newKind);
PlayerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Player, _newCharacterName, PlayerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Player, _newCharacterName,
_worldCombo.CurrentSelection.Key, ObjectKind.None, _worldCombo.CurrentSelection.Key, ObjectKind.None, [], out _playerIdentifiers) switch
Array.Empty<uint>(), out _playerIdentifiers) switch
{ {
_ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty, _ when _newCharacterName.Length == 0 => NewPlayerTooltipEmpty,
IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid, IndividualCollections.AddResult.Invalid => NewPlayerTooltipInvalid,
IndividualCollections.AddResult.AlreadySet => AlreadyAssigned, IndividualCollections.AddResult.AlreadySet => AlreadyAssigned,
_ => string.Empty, _ => string.Empty,
}; };
RetainerTooltip = _collectionManager.Active.Individuals.CanAdd(IdentifierType.Retainer, _newCharacterName, 0, ObjectKind.None, RetainerTooltip =
Array.Empty<uint>(), out _retainerIdentifiers) switch _collectionManager.Active.Individuals.CanAdd(IdentifierType.Retainer, _newCharacterName, 0, ObjectKind.None, [],
out _retainerIdentifiers) switch
{ {
_ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty, _ when _newCharacterName.Length == 0 => NewRetainerTooltipEmpty,
IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid, IndividualCollections.AddResult.Invalid => NewRetainerTooltipInvalid,
@ -184,8 +183,8 @@ public class IndividualAssignmentUi : IDisposable
{ {
NpcTooltip = NewNpcTooltipEmpty; NpcTooltip = NewNpcTooltipEmpty;
OwnedTooltip = NewNpcTooltipEmpty; OwnedTooltip = NewNpcTooltipEmpty;
_npcIdentifiers = Array.Empty<ActorIdentifier>(); _npcIdentifiers = [];
_ownedIdentifiers = Array.Empty<ActorIdentifier>(); _ownedIdentifiers = [];
} }
} }
} }

View file

@ -6,16 +6,16 @@ using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.Interop.ResourceLoading; using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Services;
using Penumbra.String; using Penumbra.String;
using Penumbra.String.Classes; using Penumbra.String.Classes;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
namespace Penumbra.UI.ResourceWatcher; namespace Penumbra.UI.ResourceWatcher;
public class ResourceWatcher : IDisposable, ITab public sealed class ResourceWatcher : IDisposable, ITab
{ {
public const int DefaultMaxEntries = 1024; public const int DefaultMaxEntries = 1024;
public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction; public const RecordType AllRecords = RecordType.Request | RecordType.ResourceLoad | RecordType.FileLoad | RecordType.Destruction;
@ -24,15 +24,15 @@ public class ResourceWatcher : IDisposable, ITab
private readonly EphemeralConfig _ephemeral; private readonly EphemeralConfig _ephemeral;
private readonly ResourceService _resources; private readonly ResourceService _resources;
private readonly ResourceLoader _loader; private readonly ResourceLoader _loader;
private readonly ActorService _actors; private readonly ActorManager _actors;
private readonly List<Record> _records = new(); private readonly List<Record> _records = [];
private readonly ConcurrentQueue<Record> _newRecords = new(); private readonly ConcurrentQueue<Record> _newRecords = [];
private readonly ResourceWatcherTable _table; private readonly ResourceWatcherTable _table;
private string _logFilter = string.Empty; private string _logFilter = string.Empty;
private Regex? _logRegex; private Regex? _logRegex;
private int _newMaxEntries; private int _newMaxEntries;
public unsafe ResourceWatcher(ActorService actors, Configuration config, ResourceService resources, ResourceLoader loader) public unsafe ResourceWatcher(ActorManager actors, Configuration config, ResourceService resources, ResourceLoader loader)
{ {
_actors = actors; _actors = actors;
_config = config; _config = config;
@ -266,12 +266,12 @@ public class ResourceWatcher : IDisposable, ITab
public unsafe string Name(ResolveData resolve, string none = "") public unsafe string Name(ResolveData resolve, string none = "")
{ {
if (resolve.AssociatedGameObject == IntPtr.Zero || !_actors.Valid) if (resolve.AssociatedGameObject == IntPtr.Zero || !_actors.Awaiter.IsCompletedSuccessfully)
return none; return none;
try try
{ {
var id = _actors.AwaitedService.FromObject((GameObject*)resolve.AssociatedGameObject, out _, false, true, true); var id = _actors.FromObject((GameObject*)resolve.AssociatedGameObject, out _, false, true, true);
if (id.IsValid) if (id.IsValid)
{ {
if (id.Type is not (IdentifierType.Player or IdentifierType.Owned)) if (id.Type is not (IdentifierType.Player or IdentifierType.Owned))

View file

@ -7,6 +7,7 @@ using OtterGui;
using OtterGui.Raii; using OtterGui.Raii;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
@ -14,7 +15,7 @@ using Penumbra.UI.CollectionTab;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;
public class CollectionsTab : IDisposable, ITab public sealed class CollectionsTab : IDisposable, ITab
{ {
private readonly EphemeralConfig _config; private readonly EphemeralConfig _config;
private readonly CollectionSelector _selector; private readonly CollectionSelector _selector;
@ -40,7 +41,7 @@ public class CollectionsTab : IDisposable, ITab
} }
public CollectionsTab(DalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator, public CollectionsTab(DalamudPluginInterface pi, Configuration configuration, CommunicatorService communicator,
CollectionManager collectionManager, ModStorage modStorage, ActorService actors, ITargetManager targets, TutorialService tutorial) CollectionManager collectionManager, ModStorage modStorage, ActorManager actors, ITargetManager targets, TutorialService tutorial)
{ {
_config = configuration.Ephemeral; _config = configuration.Ephemeral;
_tutorial = tutorial; _tutorial = tutorial;

View file

@ -3,6 +3,7 @@ using OtterGui.Widgets;
using Penumbra.Api.Enums; using Penumbra.Api.Enums;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.UI.Tabs.Debug;
using Watcher = Penumbra.UI.ResourceWatcher.ResourceWatcher; using Watcher = Penumbra.UI.ResourceWatcher.ResourceWatcher;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs;

View file

@ -1,24 +1,30 @@
using Dalamud.Interface; using Dalamud.Interface;
using Dalamud.Interface.Utility; using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Utility; using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.Group;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource; using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET; using ImGuiNET;
using Microsoft.Extensions.DependencyInjection;
using OtterGui; using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Widgets; using OtterGui.Widgets;
using Penumbra.Api; using Penumbra.Api;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Files; using Penumbra.GameData.Files;
using Penumbra.Import.Structs; using Penumbra.Import.Structs;
using Penumbra.Import.Textures; using Penumbra.Import.Textures;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs; using Penumbra.Interop.Structs;
using Penumbra.Mods; using Penumbra.Mods;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
@ -31,22 +37,39 @@ using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBa
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind; using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager; using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
using Penumbra.Interop.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using ImGuiClip = OtterGui.ImGuiClip; using ImGuiClip = OtterGui.ImGuiClip;
namespace Penumbra.UI.Tabs; namespace Penumbra.UI.Tabs.Debug;
public class Diagnostics(IServiceProvider provider)
{
public void DrawDiagnostics()
{
if (!ImGui.CollapsingHeader("Diagnostics"))
return;
using var table = ImRaii.Table("##data", 4, ImGuiTableFlags.RowBg);
foreach (var type in typeof(IAsyncDataContainer).Assembly.GetTypes()
.Where(t => t is { IsAbstract: false, IsInterface: false } && t.IsAssignableTo(typeof(IAsyncDataContainer))))
{
var container = (IAsyncDataContainer) provider.GetRequiredService(type);
ImGuiUtil.DrawTableColumn(container.Name);
ImGuiUtil.DrawTableColumn(container.Time.ToString());
ImGuiUtil.DrawTableColumn(Functions.HumanReadableSize(container.Memory));
ImGuiUtil.DrawTableColumn(container.TotalCount.ToString());
}
}
}
public class DebugTab : Window, ITab public class DebugTab : Window, ITab
{ {
private readonly StartTracker _timer;
private readonly PerformanceTracker _performance; private readonly PerformanceTracker _performance;
private readonly Configuration _config; private readonly Configuration _config;
private readonly CollectionManager _collectionManager; private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager; private readonly ModManager _modManager;
private readonly ValidityChecker _validityChecker; private readonly ValidityChecker _validityChecker;
private readonly HttpApi _httpApi; private readonly HttpApi _httpApi;
private readonly ActorService _actorService; private readonly ActorManager _actors;
private readonly DalamudServices _dalamud; private readonly DalamudServices _dalamud;
private readonly StainService _stains; private readonly StainService _stains;
private readonly CharacterUtility _characterUtility; private readonly CharacterUtility _characterUtility;
@ -64,16 +87,17 @@ public class DebugTab : Window, ITab
private readonly FrameworkManager _framework; private readonly FrameworkManager _framework;
private readonly TextureManager _textureManager; private readonly TextureManager _textureManager;
private readonly SkinFixer _skinFixer; private readonly SkinFixer _skinFixer;
private readonly IdentifierService _identifier;
private readonly RedrawService _redraws; private readonly RedrawService _redraws;
private readonly DictEmotes _emotes;
private readonly Diagnostics _diagnostics;
public DebugTab(StartTracker timer, PerformanceTracker performance, Configuration config, CollectionManager collectionManager, public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager,
ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorService actorService, ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorManager actors,
DalamudServices dalamud, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources, DalamudServices dalamud, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources,
ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver, ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver,
DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache, DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache,
CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework, CutsceneService cutsceneService, ModImportManager modImporter, ImportPopup importPopup, FrameworkManager framework,
TextureManager textureManager, SkinFixer skinFixer, IdentifierService identifier, RedrawService redraws) TextureManager textureManager, SkinFixer skinFixer, RedrawService redraws, DictEmotes emotes, Diagnostics diagnostics)
: base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse) : base("Penumbra Debug Window", ImGuiWindowFlags.NoCollapse)
{ {
IsOpen = true; IsOpen = true;
@ -82,14 +106,13 @@ public class DebugTab : Window, ITab
MinimumSize = new Vector2(200, 200), MinimumSize = new Vector2(200, 200),
MaximumSize = new Vector2(2000, 2000), MaximumSize = new Vector2(2000, 2000),
}; };
_timer = timer;
_performance = performance; _performance = performance;
_config = config; _config = config;
_collectionManager = collectionManager; _collectionManager = collectionManager;
_validityChecker = validityChecker; _validityChecker = validityChecker;
_modManager = modManager; _modManager = modManager;
_httpApi = httpApi; _httpApi = httpApi;
_actorService = actorService; _actors = actors;
_dalamud = dalamud; _dalamud = dalamud;
_stains = stains; _stains = stains;
_characterUtility = characterUtility; _characterUtility = characterUtility;
@ -107,8 +130,9 @@ public class DebugTab : Window, ITab
_framework = framework; _framework = framework;
_textureManager = textureManager; _textureManager = textureManager;
_skinFixer = skinFixer; _skinFixer = skinFixer;
_identifier = identifier;
_redraws = redraws; _redraws = redraws;
_emotes = emotes;
_diagnostics = diagnostics;
} }
public ReadOnlySpan<byte> Label public ReadOnlySpan<byte> Label
@ -130,6 +154,7 @@ public class DebugTab : Window, ITab
return; return;
DrawDebugTabGeneral(); DrawDebugTabGeneral();
_diagnostics.DrawDiagnostics();
DrawPerformanceTab(); DrawPerformanceTab();
ImGui.NewLine(); ImGui.NewLine();
DrawPathResolverDebug(); DrawPathResolverDebug();
@ -357,7 +382,6 @@ public class DebugTab : Window, ITab
ImGuiUtil.DrawTableColumn(name); ImGuiUtil.DrawTableColumn(name);
ImGui.TableNextColumn(); ImGui.TableNextColumn();
} }
} }
} }
} }
@ -372,11 +396,8 @@ public class DebugTab : Window, ITab
using (var start = TreeNode("Startup Performance", ImGuiTreeNodeFlags.DefaultOpen)) using (var start = TreeNode("Startup Performance", ImGuiTreeNodeFlags.DefaultOpen))
{ {
if (start) if (start)
{
_timer.Draw("##startTimer", TimingExtensions.ToName);
ImGui.NewLine(); ImGui.NewLine();
} }
}
_performance.Draw("##performance", "Enable Runtime Performance Tracking", TimingExtensions.ToName); _performance.Draw("##performance", "Enable Runtime Performance Tracking", TimingExtensions.ToName);
} }
@ -391,22 +412,10 @@ public class DebugTab : Window, ITab
if (!table) if (!table)
return; return;
void DrawSpecial(string name, ActorIdentifier id) DrawSpecial("Current Player", _actors.GetCurrentPlayer());
{ DrawSpecial("Current Inspect", _actors.GetInspectPlayer());
if (!id.IsValid) DrawSpecial("Current Card", _actors.GetCardPlayer());
return; DrawSpecial("Current Glamour", _actors.GetGlamourPlayer());
ImGuiUtil.DrawTableColumn(name);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(id));
ImGuiUtil.DrawTableColumn(string.Empty);
}
DrawSpecial("Current Player", _actorService.AwaitedService.GetCurrentPlayer());
DrawSpecial("Current Inspect", _actorService.AwaitedService.GetInspectPlayer());
DrawSpecial("Current Card", _actorService.AwaitedService.GetCardPlayer());
DrawSpecial("Current Glamour", _actorService.AwaitedService.GetGlamourPlayer());
foreach (var obj in _dalamud.Objects) foreach (var obj in _dalamud.Objects)
{ {
@ -415,11 +424,25 @@ public class DebugTab : Window, ITab
ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero
? string.Empty ? string.Empty
: $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}"); : $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}");
var identifier = _actorService.AwaitedService.FromObject(obj, false, true, false); var identifier = _actors.FromObject(obj, false, true, false);
ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(identifier)); ImGuiUtil.DrawTableColumn(_actors.ToString(identifier));
var id = obj.ObjectKind == ObjectKind.BattleNpc ? $"{identifier.DataId} | {obj.DataId}" : identifier.DataId.ToString(); var id = obj.ObjectKind == ObjectKind.BattleNpc ? $"{identifier.DataId} | {obj.DataId}" : identifier.DataId.ToString();
ImGuiUtil.DrawTableColumn(id); ImGuiUtil.DrawTableColumn(id);
} }
return;
void DrawSpecial(string name, ActorIdentifier id)
{
if (!id.IsValid)
return;
ImGuiUtil.DrawTableColumn(name);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(_actors.ToString(id));
ImGuiUtil.DrawTableColumn(string.Empty);
}
} }
/// <summary> /// <summary>
@ -616,7 +639,7 @@ public class DebugTab : Window, ITab
return; return;
var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing()); var skips = ImGuiClip.GetNecessarySkips(ImGui.GetTextLineHeightWithSpacing());
var dummy = ImGuiClip.FilteredClippedDraw(_identifier.AwaitedService.Emotes, skips, var dummy = ImGuiClip.FilteredClippedDraw(_emotes, skips,
p => p.Key.Contains(_emoteSearchFile, StringComparison.OrdinalIgnoreCase) p => p.Key.Contains(_emoteSearchFile, StringComparison.OrdinalIgnoreCase)
&& (_emoteSearchName.Length == 0 && (_emoteSearchName.Length == 0
|| p.Value.Any(s => s.Name.ToDalamudString().TextValue.Contains(_emoteSearchName, StringComparison.OrdinalIgnoreCase))), || p.Value.Any(s => s.Name.ToDalamudString().TextValue.Contains(_emoteSearchName, StringComparison.OrdinalIgnoreCase))),

View file

@ -2,7 +2,7 @@ using Dalamud.Interface;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using Dalamud.Plugin; using Dalamud.Plugin;
using Penumbra.UI.AdvancedWindow; using Penumbra.UI.AdvancedWindow;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs.Debug;
namespace Penumbra.UI; namespace Penumbra.UI;

View file

@ -1,23 +1,7 @@
global using StartTracker = OtterGui.Classes.StartTimeTracker<Penumbra.Util.StartTimeType>;
global using PerformanceTracker = OtterGui.Classes.PerformanceTracker<Penumbra.Util.PerformanceType>; global using PerformanceTracker = OtterGui.Classes.PerformanceTracker<Penumbra.Util.PerformanceType>;
namespace Penumbra.Util; namespace Penumbra.Util;
public enum StartTimeType
{
Total,
Identifier,
Stains,
Items,
Actors,
Backup,
Mods,
Collections,
PathResolver,
Interface,
Api,
}
public enum PerformanceType public enum PerformanceType
{ {
UiMainWindow, UiMainWindow,
@ -48,23 +32,6 @@ public enum PerformanceType
public static class TimingExtensions public static class TimingExtensions
{ {
public static string ToName(this StartTimeType type)
=> type switch
{
StartTimeType.Total => "Total Construction",
StartTimeType.Identifier => "Identification Data",
StartTimeType.Stains => "Stain Data",
StartTimeType.Items => "Item Data",
StartTimeType.Actors => "Actor Data",
StartTimeType.Backup => "Checking Backups",
StartTimeType.Mods => "Loading Mods",
StartTimeType.Collections => "Loading Collections",
StartTimeType.Api => "Setting Up API",
StartTimeType.Interface => "Setting Up Interface",
StartTimeType.PathResolver => "Setting Up Path Resolver",
_ => $"Unknown {(int)type}",
};
public static string ToName(this PerformanceType type) public static string ToName(this PerformanceType type)
=> type switch => type switch
{ {

View file

@ -81,7 +81,8 @@
"penumbra.gamedata": { "penumbra.gamedata": {
"type": "Project", "type": "Project",
"dependencies": { "dependencies": {
"Penumbra.Api": "[1.0.8, )", "OtterGui": "[1.0.0, )",
"Penumbra.Api": "[1.0.13, )",
"Penumbra.String": "[1.0.4, )" "Penumbra.String": "[1.0.4, )"
} }
}, },