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 TempCollectionManager _tempCollections;
private TempModManager _tempMods;
private ActorService _actors;
private ActorManager _actors;
private CollectionResolver _collectionResolver;
private CutsceneService _cutsceneService;
private ModImportManager _modImportManager;
@ -108,7 +108,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public unsafe PenumbraApi(CommunicatorService communicator, ModManager modManager, ResourceLoader resourceLoader,
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,
ConfigWindow configWindow, TextureManager textureManager, ResourceTreeFactory resourceTreeFactory)
{
@ -889,13 +889,10 @@ public class PenumbraApi : IDisposable, IPenumbraApi
{
CheckInitialized();
if (!_actors.Valid)
return PenumbraApiEc.SystemDisposed;
if (actorIndex < 0 || actorIndex >= _dalamud.Objects.Length)
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)
return PenumbraApiEc.InvalidArgument;
@ -1143,11 +1140,11 @@ public class PenumbraApi : IDisposable, IPenumbraApi
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
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;
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.
@ -1241,12 +1238,9 @@ public class PenumbraApi : IDisposable, IPenumbraApi
// TODO: replace all usages with ActorIdentifier stuff when incrementing API
private ActorIdentifier NameToIdentifier(string name, ushort worldId)
{
if (!_actors.Valid)
return ActorIdentifier.Invalid;
// Verified to be valid name beforehand.
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)

View file

@ -22,10 +22,10 @@ public class CollectionCache : IDisposable
private readonly CollectionCacheManager _manager;
private readonly ModCollection _collection;
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 MetaCache Meta;
public readonly Dictionary<IMod, SingleArray<ModConflicts>> _conflicts = new();
public readonly Dictionary<IMod, SingleArray<ModConflicts>> ConflictDict = [];
public int Calculating = -1;
@ -33,10 +33,10 @@ public class CollectionCache : IDisposable
=> _collection.AnonymizedName;
public IEnumerable<SingleArray<ModConflicts>> AllConflicts
=> _conflicts.Values;
=> ConflictDict.Values;
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;
@ -195,7 +195,7 @@ public class CollectionCache : IDisposable
$"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)
{
if (conflict.HasPriority)
@ -206,9 +206,9 @@ public class CollectionCache : IDisposable
{
var newConflicts = Conflicts(conflict.Mod2).Remove(c => c.Mod2 == mod);
if (newConflicts.Count > 0)
_conflicts[conflict.Mod2] = newConflicts;
ConflictDict[conflict.Mod2] = newConflicts;
else
_conflicts.Remove(conflict.Mod2);
ConflictDict.Remove(conflict.Mod2);
}
}
@ -336,9 +336,9 @@ public class CollectionCache : IDisposable
return false;
});
if (changedConflicts.Count == 0)
_conflicts.Remove(mod);
ConflictDict.Remove(mod);
else
_conflicts[mod] = changedConflicts;
ConflictDict[mod] = changedConflicts;
}
// 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.
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));
_conflicts[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList,
ConflictDict[existingMod] = existingConflicts.Append(new ModConflicts(addedMod, conflictList,
existingPriority >= addedPriority,
existingPriority != addedPriority));
}
@ -426,7 +426,7 @@ public class CollectionCache : IDisposable
_changedItems.Clear();
// 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.
var identifier = _manager.MetaFileManager.Identifier.AwaitedService;
var identifier = _manager.MetaFileManager.Identifier;
var items = new SortedList<string, object?>(512);
void AddItems(IMod mod)

View file

@ -159,7 +159,7 @@ public class CollectionCacheManager : IDisposable
null);
cache.ResolvedFiles.Clear();
cache.Meta.Reset();
cache._conflicts.Clear();
cache.ConflictDict.Clear();
// Add all forced redirects.
foreach (var tempMod in _tempMods.ModsForAllCollections
@ -372,7 +372,7 @@ public class CollectionCacheManager : IDisposable
{
collection._cache!.ResolvedFiles.Clear();
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 SaveService _saveService;
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)
{
_storage = storage;
@ -475,7 +475,7 @@ public class ActiveCollections : ISavable, IDisposable
{
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
? "Assignment is redundant due to an identical Any-World assignment existing.\nYou can remove it."
: string.Empty;
@ -484,12 +484,12 @@ public class ActiveCollections : ISavable, IDisposable
if (id.HomeWorld != ushort.MaxValue)
{
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)
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
? "Assignment is redundant due to an identical unowned NPC assignment existing.\nYou can remove it."
: string.Empty;

View file

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

View file

@ -3,6 +3,8 @@ using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Structs;
using Penumbra.Services;
using Penumbra.String;
@ -26,23 +28,20 @@ public partial class IndividualCollections
public bool ReadJObject(SaveService saver, ActiveCollections parent, JArray? obj, CollectionStorage storage)
{
if (_actorService.Valid)
if (_actors.Awaiter.IsCompletedSuccessfully)
{
var ret = ReadJObjectInternal(obj, storage);
return ret;
}
void Func()
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actors.Awaiter.ContinueWith(_ =>
{
if (ReadJObjectInternal(obj, storage))
saver.ImmediateSave(parent);
IsLoaded = true;
Loaded.Invoke();
_actorService.FinishedCreation -= Func;
}
Penumbra.Log.Debug("[Collections] Delayed reading individual assignments until actor service is ready...");
_actorService.FinishedCreation += Func;
});
return false;
}
@ -60,7 +59,7 @@ public partial class IndividualCollections
{
try
{
var identifier = _actorService.AwaitedService.FromJson(data as JObject);
var identifier = _actors.FromJson(data as JObject);
var group = GetGroup(identifier);
if (group.Length == 0 || group.Any(i => !i.IsValid))
{
@ -101,10 +100,10 @@ public partial class IndividualCollections
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),
new KeyValuePair<uint, string>(uint.MaxValue, string.Empty));
new KeyValuePair<NpcId, string>(uint.MaxValue, string.Empty));
dataId = kvp.Key;
return kvp.Value.Length > 0;
}
@ -114,22 +113,22 @@ public partial class IndividualCollections
var kind = ObjectKind.None;
var lowerName = name.ToLowerInvariant();
// 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;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.Mounts, out dataId))
else if (FindDataId(lowerName, _actors.Data.Mounts, out dataId))
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;
else if (FindDataId(lowerName, _actorService.AwaitedService.Data.ENpcs, out dataId))
else if (FindDataId(lowerName, _actors.Data.ENpcs, out dataId))
kind = ObjectKind.EventNpc;
var identifier = _actorService.AwaitedService.CreateNpc(kind, dataId);
var identifier = _actors.CreateNpc(kind, dataId);
if (identifier.IsValid)
{
// If the name corresponds to a valid npc, add it as a group. If this fails, notify users.
var group = GetGroup(identifier);
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}].");
else
Penumbra.Messager.NotificationMessage(
@ -137,15 +136,12 @@ public partial class IndividualCollections
NotificationType.Error);
}
// 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]}."));
// Try to migrate the player name without logging full names.
if (Add($"{name} ({_actorService.AwaitedService.Data.ToWorldName(identifier.HomeWorld)})", new[]
{
identifier,
}, collection))
if (Add($"{name} ({_actors.Data.ToWorldName(identifier.HomeWorld)})", [identifier], collection))
Penumbra.Log.Information($"Migrated {shortName} ({collection.AnonymizedName}) to Player Identifier.");
else
Penumbra.Messager.NotificationMessage(

View file

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

View file

@ -14,10 +14,10 @@ public class TempCollectionManager : IDisposable
private readonly CommunicatorService _communicator;
private readonly CollectionStorage _storage;
private readonly ActorService _actors;
private readonly ActorManager _actors;
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;
_actors = actors;
@ -111,7 +111,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, false))
return false;
var identifier = _actors.AwaitedService.CreatePlayer(byteString, worldId);
var identifier = _actors.CreatePlayer(byteString, worldId);
if (!identifier.IsValid)
return false;
@ -123,7 +123,7 @@ public class TempCollectionManager : IDisposable
if (!ByteString.FromString(characterName, out var byteString, 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);
}
}

View file

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

View file

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

View file

@ -1,4 +1,5 @@
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.Import.Structs;
using Penumbra.Meta;
using Penumbra.Meta.Manipulations;
@ -28,7 +29,7 @@ public partial class TexToolsMeta
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;
_keepDefault = keepDefault;

View file

@ -3,7 +3,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.Services;
using Penumbra.Util;
@ -21,7 +21,7 @@ public unsafe class CollectionResolver
private readonly IClientState _clientState;
private readonly IGameGui _gameGui;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly CutsceneService _cutscenes;
private readonly Configuration _config;
@ -30,7 +30,7 @@ public unsafe class CollectionResolver
private readonly DrawObjectState _drawObjectState;
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)
{
_performance = performance;
@ -58,7 +58,7 @@ public unsafe class CollectionResolver
return _collectionManager.Active.ByType(CollectionType.Yourself)
?? _collectionManager.Active.Default;
var player = _actors.AwaitedService.GetCurrentPlayer();
var player = _actors.GetCurrentPlayer();
var _ = false;
return CollectionByIdentifier(player)
?? CheckYourself(player, gameObject)
@ -147,7 +147,7 @@ public unsafe class CollectionResolver
return false;
}
var player = _actors.AwaitedService.GetCurrentPlayer();
var player = _actors.GetCurrentPlayer();
var notYetReady = false;
var collection = (player.IsValid ? CollectionByIdentifier(player) : null)
?? _collectionManager.Active.ByType(CollectionType.Yourself)
@ -163,7 +163,7 @@ public unsafe class CollectionResolver
/// </summary>
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)
{
(identifier, var type) = _collectionManager.Active.Individuals.ConvertSpecialIdentifier(identifier);
@ -193,7 +193,7 @@ public unsafe class CollectionResolver
{
if (actor->ObjectIndex == 0
|| _cutscenes.GetParentIndex(actor->ObjectIndex) == 0
|| identifier.Equals(_actors.AwaitedService.GetCurrentPlayer()))
|| identifier.Equals(_actors.GetCurrentPlayer()))
return _collectionManager.Active.ByType(CollectionType.Yourself);
return null;
@ -242,7 +242,7 @@ public unsafe class CollectionResolver
if (identifier.Type != IdentifierType.Owned || !_config.UseOwnerNameForCharacterCollection || owner == 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,
uint.MaxValue);
return CheckYourself(id, owner)

View file

@ -1,6 +1,6 @@
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Services;
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>
public int GetParentIndex(int idx)
=> GetParentIndex((ushort)idx);
public short GetParentIndex(ushort idx)
{
if (idx is >= CutsceneStartIdx and < CutsceneEndIdx)
return _copiedCharacters[idx - CutsceneStartIdx];

View file

@ -7,6 +7,7 @@ using OtterGui;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
@ -19,7 +20,7 @@ using ModelType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBase.M
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);
@ -28,8 +29,13 @@ internal record GlobalResolveContext(IObjectIdentifier Identifier, ModCollection
=> new(this, characterBase, slotIndex, slot, equipment, weaponType);
}
internal partial record ResolveContext(GlobalResolveContext Global, Pointer<CharacterBase> CharacterBase, uint SlotIndex,
EquipSlot Slot, CharacterArmor Equipment, WeaponType WeaponType)
internal partial record ResolveContext(
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);
@ -152,6 +158,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
{
if (mdl == null || mdl->ModelResourceHandle == null)
return null;
var mdlResource = mdl->ModelResourceHandle;
var path = ResolveModelPath();
@ -224,6 +231,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
shpkNode.Name = "Shader Package";
node.Children.Add(shpkNode);
}
var shpkFile = Global.WithUiData && shpkNode != null ? Global.TreeBuildCache.ReadShaderPackage(shpkNode.FullPath) : null;
var shpk = Global.WithUiData && shpkNode != null ? (ShaderPackage*)shpkNode.ObjectAddress : null;
@ -255,7 +263,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
}
}
texNode = texNode.Clone();
texNode = texNode.Clone();
texNode.Name = name ?? $"Texture #{i}";
}
@ -310,13 +318,13 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
return node;
}
internal ResourceNode.UiData GuessModelUIData(Utf8GamePath gamePath)
internal ResourceNode.UiData GuessModelUiData(Utf8GamePath gamePath)
{
var path = gamePath.ToString().Split('/', StringSplitOptions.RemoveEmptyEntries);
// Weapons intentionally left out.
var isEquipment = SafeGet(path, 0) == "chara" && SafeGet(path, 1) is "accessory" or "equipment";
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
{
@ -324,11 +332,11 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
EquipSlot.LFinger => "L: ",
_ => string.Empty,
}
+ item.Name.ToString();
+ item.Name;
return new ResourceNode.UiData(name, ChangedItemDrawer.GetCategoryIcon(item.Name, item));
}
var dataFromPath = GuessUIDataFromPath(gamePath);
var dataFromPath = GuessUiDataFromPath(gamePath);
if (dataFromPath.Name != null)
return dataFromPath;
@ -337,7 +345,7 @@ internal partial record ResolveContext(GlobalResolveContext Global, Pointer<Char
: 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()))
{

View file

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

View file

@ -1,36 +1,26 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
using Penumbra.GameData.Structs;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.String.Classes;
namespace Penumbra.Interop.ResourceTree;
internal readonly struct TreeBuildCache
internal readonly struct TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorManager actors)
{
private readonly IDataManager _dataManager;
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;
}
private readonly Dictionary<FullPath, ShpkFile?> _shaderPackages = [];
public unsafe bool IsLocalPlayerRelated(Character character)
{
var player = _objects[0];
var player = objects[0];
if (player == null)
return false;
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;
return actualIndex switch
{
@ -41,38 +31,38 @@ internal readonly struct TreeBuildCache
}
public IEnumerable<Character> GetCharacters()
=> _objects.OfType<Character>();
=> objects.OfType<Character>();
public IEnumerable<Character> GetLocalPlayerRelatedCharacters()
{
var player = _objects[0];
var player = objects[0];
if (player == null)
yield break;
yield return (Character)player;
var minion = _objects[1];
var minion = objects[1];
if (minion != null)
yield return (Character)minion;
var playerId = player.ObjectId;
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;
}
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)
continue;
var parent = _actors.AwaitedService.ToCutsceneParent(i);
var parent = actors.ToCutsceneParent(i);
if (parent < 0)
continue;
if (parent is 0 or 1 || _objects[parent]?.OwnerId == playerId)
if (parent is 0 or 1 || objects[parent]?.OwnerId == playerId)
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)
{
character = _objects[idx] as Character;
character = objects[idx] as Character;
if (character == null)
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)
return false;
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>
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)
where T : class

View file

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

View file

@ -5,6 +5,7 @@ using OtterGui.Compression;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.Import;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
@ -24,11 +25,11 @@ public unsafe class MetaFileManager
internal readonly IDataManager GameData;
internal readonly ActiveCollectionData ActiveCollections;
internal readonly ValidityChecker ValidityChecker;
internal readonly IdentifierService Identifier;
internal readonly ObjectIdentification Identifier;
internal readonly FileCompactor Compactor;
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)
{
CharacterUtility = characterUtility;

View file

@ -1,5 +1,4 @@
using Penumbra.Api.Enums;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
@ -41,7 +40,7 @@ public static class EquipmentSwap
: 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,
EquipSlot slotFrom, EquipItem itemFrom, EquipSlot slotTo, EquipItem itemTo)
{
@ -99,7 +98,7 @@ public static class EquipmentSwap
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,
EquipItem itemTo, bool rFinger = true, bool lFinger = true)
{
@ -247,7 +246,7 @@ public static class EquipmentSwap
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)
{
var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default);
@ -256,11 +255,8 @@ public static class EquipmentSwap
Variant[] variants;
if (idFrom == idTo)
{
items = identifier.Identify(idFrom, variantFrom, slotFrom).ToArray();
variants = new[]
{
variantFrom,
};
items = identifier.Identify(idFrom, 0, variantFrom, slotFrom).ToArray();
variants = [variantFrom];
}
else
{

View file

@ -1,5 +1,5 @@
using Lumina.Excel.GeneratedSheets;
using Penumbra.Collections;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta.Manipulations;
@ -7,17 +7,16 @@ using Penumbra.String.Classes;
using Penumbra.Meta;
using Penumbra.Mods.Manager;
using Penumbra.Mods.Subclasses;
using Penumbra.Services;
namespace Penumbra.Mods.ItemSwap;
public class ItemSwapContainer
{
private readonly MetaFileManager _manager;
private readonly IdentifierService _identifier;
private readonly MetaFileManager _manager;
private readonly ObjectIdentification _identifier;
private Dictionary<Utf8GamePath, FullPath> _modRedirections = new();
private HashSet<MetaManipulation> _modManipulations = new();
private Dictionary<Utf8GamePath, FullPath> _modRedirections = [];
private HashSet<MetaManipulation> _modManipulations = [];
public IReadOnlyDictionary<Utf8GamePath, FullPath> ModRedirections
=> _modRedirections;
@ -25,7 +24,7 @@ public class ItemSwapContainer
public IReadOnlySet<MetaManipulation> ModManipulations
=> _modManipulations;
public readonly List<Swap> Swaps = new();
public readonly List<Swap> Swaps = [];
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;
_identifier = identifier;
@ -130,7 +129,7 @@ public class ItemSwapContainer
{
Swaps.Clear();
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);
Loaded = true;
return ret;
@ -140,7 +139,7 @@ public class ItemSwapContainer
{
Swaps.Clear();
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);
Loaded = true;
return ret;

View file

@ -1,5 +1,4 @@
using Penumbra.Communication;
using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.Meta.Manipulations;
@ -9,11 +8,12 @@ namespace Penumbra.Mods.Manager;
public class ModCacheManager : IDisposable
{
private readonly CommunicatorService _communicator;
private readonly IdentifierService _identifier;
private readonly ModStorage _modManager;
private readonly CommunicatorService _communicator;
private readonly ObjectIdentification _identifier;
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;
_identifier = identifier;
@ -23,8 +23,7 @@ public class ModCacheManager : IDisposable
_communicator.ModPathChanged.Subscribe(OnModPathChange, ModPathChanged.Priority.ModCacheManager);
_communicator.ModDataChanged.Subscribe(OnModDataChange, ModDataChanged.Priority.ModCacheManager);
_communicator.ModDiscoveryFinished.Subscribe(OnModDiscoveryFinished, ModDiscoveryFinished.Priority.ModCacheManager);
if (!identifier.Valid)
identifier.FinishedCreation += OnIdentifierCreation;
identifier.Awaiter.ContinueWith(_ => OnIdentifierCreation());
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>
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)
{
@ -155,10 +154,7 @@ public class ModCacheManager : IDisposable
=> Parallel.ForEach(_modManager, Refresh);
private void OnIdentifierCreation()
{
Parallel.ForEach(_modManager, UpdateChangedItems);
_identifier.FinishedCreation -= OnIdentifierCreation;
}
=> Parallel.ForEach(_modManager, UpdateChangedItems);
private static void UpdateFileCount(Mod mod)
=> mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count);
@ -177,18 +173,23 @@ public class ModCacheManager : IDisposable
private void UpdateChangedItems(Mod mod)
{
if (_updatingItems)
return;
_updatingItems = true;
var changedItems = (SortedList<string, object?>)mod.ChangedItems;
changedItems.Clear();
if (!_identifier.Valid)
if (!_identifier.Awaiter.IsCompletedSuccessfully)
return;
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))
ComputeChangedItems(_identifier.AwaitedService, changedItems, manip);
ComputeChangedItems(_identifier, changedItems, manip);
mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant()));
_updatingItems = false;
}
private static void UpdateCounts(Mod mod)

View file

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

View file

@ -1,4 +1,5 @@
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Microsoft.Extensions.DependencyInjection;
@ -6,7 +7,6 @@ using OtterGui;
using OtterGui.Log;
using Penumbra.Api;
using Penumbra.Api.Enums;
using Penumbra.Util;
using Penumbra.Collections;
using Penumbra.Collections.Cache;
using Penumbra.Interop.ResourceLoading;
@ -19,6 +19,7 @@ using Penumbra.UI.Tabs;
using ChangedItemClick = Penumbra.Communication.ChangedItemClick;
using ChangedItemHover = Penumbra.Communication.ChangedItemHover;
using OtterGui.Tasks;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.Interop.Structs;
using Penumbra.UI;
@ -31,7 +32,7 @@ public class Penumbra : IDalamudPlugin
public string Name
=> "Penumbra";
public static readonly Logger Log = new();
public static readonly Logger Log = new();
public static MessageService Messager { get; private set; } = null!;
private readonly ValidityChecker _validityChecker;
@ -53,10 +54,8 @@ public class Penumbra : IDalamudPlugin
{
try
{
var startTimer = new StartTracker();
using var timer = startTimer.Measure(StartTimeType.Total);
_services = ServiceManager.CreateProvider(this, pluginInterface, Log, startTimer);
Messager = _services.GetRequiredService<MessageService>();
_services = ServiceManager.CreateProvider(this, pluginInterface, Log);
Messager = _services.GetRequiredService<MessageService>();
_validityChecker = _services.GetRequiredService<ValidityChecker>();
var startup = _services.GetRequiredService<DalamudServices>().GetDalamudConfig(DalamudServices.WaitingForPluginsOption, out bool s)
? s.ToString()
@ -74,14 +73,11 @@ public class Penumbra : IDalamudPlugin
_tempCollections = _services.GetRequiredService<TempCollectionManager>();
_redrawService = _services.GetRequiredService<RedrawService>();
_communicatorService = _services.GetRequiredService<CommunicatorService>();
_services.GetRequiredService<ResourceService>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ModCacheManager>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ResourceService>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ModCacheManager>(); // Initialize because not required anywhere else.
_services.GetRequiredService<ModelResourceHandleUtility>(); // Initialize because not required anywhere else.
_collectionManager.Caches.CreateNecessaryCaches();
using (var t = _services.GetRequiredService<StartTracker>().Measure(StartTimeType.PathResolver))
{
_services.GetRequiredService<PathResolver>();
}
_services.GetRequiredService<PathResolver>();
_services.GetRequiredService<SkinFixer>();
@ -108,8 +104,7 @@ public class Penumbra : IDalamudPlugin
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>();
_communicatorService.ChangedItemHover.Subscribe(it =>
{
@ -128,8 +123,7 @@ public class Penumbra : IDalamudPlugin
{
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>());
_services.GetRequiredService<CommandHandler>();
if (!_disposed)

View file

@ -1,15 +1,13 @@
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using OtterGui.Log;
using Penumbra.Util;
namespace Penumbra.Services;
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);
Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), files);
}

View file

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

View file

@ -1,5 +1,4 @@
using OtterGui.Tasks;
using Penumbra.Util;
namespace Penumbra.Services;
@ -12,10 +11,9 @@ public abstract class SyncServiceWrapper<T> : IDisposable
public bool Valid
=> !_isDisposed;
protected SyncServiceWrapper(string name, StartTracker tracker, StartTimeType type, Func<T> factory)
protected SyncServiceWrapper(string name, Func<T> factory)
{
Name = name;
using var timer = tracker.Measure(type);
Service = factory();
Penumbra.Log.Verbose($"[{Name}] Created.");
}
@ -54,32 +52,6 @@ public abstract class AsyncServiceWrapper<T> : IDisposable
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)
{
Name = name;

View file

@ -4,7 +4,7 @@ using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ImGuiNET;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Files;
using Penumbra.UI.AdvancedWindow;
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 StmFile StmFile;
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 StainData(pluginInterface, dataManager, dataManager.Language, dalamudLog);
StainData = new DictStains(pluginInterface, dalamudLog, dataManager);
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);
StmFile = new StmFile(dataManager);
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.Utility;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
@ -11,6 +8,7 @@ using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.Collections.Manager;
using Penumbra.Communication;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.Meta;
@ -27,16 +25,14 @@ public class ItemSwapTab : IDisposable, ITab
{
private readonly Configuration _config;
private readonly CommunicatorService _communicator;
private readonly ItemService _itemService;
private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager;
private readonly MetaFileManager _metaFileManager;
public ItemSwapTab(CommunicatorService communicator, ItemService itemService, CollectionManager collectionManager,
ModManager modManager, IdentifierService identifier, MetaFileManager metaFileManager, Configuration config)
public ItemSwapTab(CommunicatorService communicator, ItemData itemService, CollectionManager collectionManager,
ModManager modManager, ObjectIdentification identifier, MetaFileManager metaFileManager, Configuration config)
{
_communicator = communicator;
_itemService = itemService;
_collectionManager = collectionManager;
_modManager = modManager;
_metaFileManager = metaFileManager;
@ -46,15 +42,15 @@ public class ItemSwapTab : IDisposable, ITab
_selectors = new Dictionary<SwapType, (ItemSelector Source, ItemSelector Target, string TextFrom, string TextTo)>
{
// @formatter:off
[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.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.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.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.Ring] = (new ItemSelector(_itemService, FullEquipType.Finger), new ItemSelector(_itemService, FullEquipType.Finger), "Take this Ring", "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.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.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.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.Ring] = (new ItemSelector(itemService, FullEquipType.Finger), new ItemSelector(itemService, FullEquipType.Finger), "Take this Ring", "and put it on this one" ),
// @formatter:on
};
@ -131,8 +127,8 @@ public class ItemSwapTab : IDisposable, ITab
private class ItemSelector : FilterComboCache<EquipItem>
{
public ItemSelector(ItemService data, FullEquipType type)
: base(() => data.AwaitedService[type], Penumbra.Log)
public ItemSelector(ItemData data, FullEquipType type)
: base(() => data.ByType[type], Penumbra.Log)
{ }
protected override string ToString(EquipItem obj)

View file

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

View file

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

View file

@ -22,7 +22,7 @@ public sealed class CollectionPanel : IDisposable
private readonly CollectionStorage _collections;
private readonly ActiveCollections _active;
private readonly CollectionSelector _selector;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly ITargetManager _targets;
private readonly IndividualAssignmentUi _individualAssignmentUi;
private readonly InheritanceUi _inheritanceUi;
@ -37,7 +37,7 @@ public sealed class CollectionPanel : IDisposable
private int _draggedIndividualAssignment = -1;
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;
_active = manager.Active;
@ -382,11 +382,11 @@ public sealed class CollectionPanel : IDisposable
}
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)
=> 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)
=> DrawIndividualButton("New Player", width, _individualAssignmentUi.PlayerTooltip, 'p',

View file

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

View file

@ -6,16 +6,16 @@ using OtterGui.Widgets;
using Penumbra.Api.Enums;
using Penumbra.Collections;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Structs;
using Penumbra.Services;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.UI.Classes;
namespace Penumbra.UI.ResourceWatcher;
public class ResourceWatcher : IDisposable, ITab
public sealed class ResourceWatcher : IDisposable, ITab
{
public const int DefaultMaxEntries = 1024;
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 ResourceService _resources;
private readonly ResourceLoader _loader;
private readonly ActorService _actors;
private readonly List<Record> _records = new();
private readonly ConcurrentQueue<Record> _newRecords = new();
private readonly ActorManager _actors;
private readonly List<Record> _records = [];
private readonly ConcurrentQueue<Record> _newRecords = [];
private readonly ResourceWatcherTable _table;
private string _logFilter = string.Empty;
private Regex? _logRegex;
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;
_config = config;
@ -266,12 +266,12 @@ public class ResourceWatcher : IDisposable, ITab
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;
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.Type is not (IdentifierType.Player or IdentifierType.Owned))

View file

@ -7,6 +7,7 @@ using OtterGui;
using OtterGui.Raii;
using OtterGui.Widgets;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.Mods.Manager;
using Penumbra.Services;
using Penumbra.UI.Classes;
@ -14,7 +15,7 @@ using Penumbra.UI.CollectionTab;
namespace Penumbra.UI.Tabs;
public class CollectionsTab : IDisposable, ITab
public sealed class CollectionsTab : IDisposable, ITab
{
private readonly EphemeralConfig _config;
private readonly CollectionSelector _selector;
@ -40,7 +41,7 @@ public class CollectionsTab : IDisposable, ITab
}
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;
_tutorial = tutorial;

View file

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

View file

@ -1,24 +1,30 @@
using Dalamud.Interface;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Group;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using ImGuiNET;
using Microsoft.Extensions.DependencyInjection;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Widgets;
using Penumbra.Api;
using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.DataContainers.Bases;
using Penumbra.GameData.Files;
using Penumbra.Import.Structs;
using Penumbra.Import.Textures;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.PathResolving;
using Penumbra.Interop.ResourceLoading;
using Penumbra.Interop.Services;
using Penumbra.Interop.Structs;
using Penumbra.Mods;
using Penumbra.Mods.Manager;
@ -31,22 +37,39 @@ using CharacterBase = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.CharacterBa
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
using ObjectKind = Dalamud.Game.ClientState.Objects.Enums.ObjectKind;
using ResidentResourceManager = Penumbra.Interop.Services.ResidentResourceManager;
using Penumbra.Interop.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
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
{
private readonly StartTracker _timer;
private readonly PerformanceTracker _performance;
private readonly Configuration _config;
private readonly CollectionManager _collectionManager;
private readonly ModManager _modManager;
private readonly ValidityChecker _validityChecker;
private readonly HttpApi _httpApi;
private readonly ActorService _actorService;
private readonly ActorManager _actors;
private readonly DalamudServices _dalamud;
private readonly StainService _stains;
private readonly CharacterUtility _characterUtility;
@ -64,16 +87,17 @@ public class DebugTab : Window, ITab
private readonly FrameworkManager _framework;
private readonly TextureManager _textureManager;
private readonly SkinFixer _skinFixer;
private readonly IdentifierService _identifier;
private readonly RedrawService _redraws;
private readonly DictEmotes _emotes;
private readonly Diagnostics _diagnostics;
public DebugTab(StartTracker timer, PerformanceTracker performance, Configuration config, CollectionManager collectionManager,
ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorService actorService,
public DebugTab(PerformanceTracker performance, Configuration config, CollectionManager collectionManager,
ValidityChecker validityChecker, ModManager modManager, HttpApi httpApi, ActorManager actors,
DalamudServices dalamud, StainService stains, CharacterUtility characterUtility, ResidentResourceManager residentResources,
ResourceManagerService resourceManager, PenumbraIpcProviders ipc, CollectionResolver collectionResolver,
DrawObjectState drawObjectState, PathState pathState, SubfileHelper subfileHelper, IdentifiedCollectionCache identifiedCollectionCache,
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)
{
IsOpen = true;
@ -82,14 +106,13 @@ public class DebugTab : Window, ITab
MinimumSize = new Vector2(200, 200),
MaximumSize = new Vector2(2000, 2000),
};
_timer = timer;
_performance = performance;
_config = config;
_collectionManager = collectionManager;
_validityChecker = validityChecker;
_modManager = modManager;
_httpApi = httpApi;
_actorService = actorService;
_actors = actors;
_dalamud = dalamud;
_stains = stains;
_characterUtility = characterUtility;
@ -107,8 +130,9 @@ public class DebugTab : Window, ITab
_framework = framework;
_textureManager = textureManager;
_skinFixer = skinFixer;
_identifier = identifier;
_redraws = redraws;
_emotes = emotes;
_diagnostics = diagnostics;
}
public ReadOnlySpan<byte> Label
@ -130,6 +154,7 @@ public class DebugTab : Window, ITab
return;
DrawDebugTabGeneral();
_diagnostics.DrawDiagnostics();
DrawPerformanceTab();
ImGui.NewLine();
DrawPathResolverDebug();
@ -357,7 +382,6 @@ public class DebugTab : Window, ITab
ImGuiUtil.DrawTableColumn(name);
ImGui.TableNextColumn();
}
}
}
}
@ -372,10 +396,7 @@ public class DebugTab : Window, ITab
using (var start = TreeNode("Startup Performance", ImGuiTreeNodeFlags.DefaultOpen))
{
if (start)
{
_timer.Draw("##startTimer", TimingExtensions.ToName);
ImGui.NewLine();
}
}
_performance.Draw("##performance", "Enable Runtime Performance Tracking", TimingExtensions.ToName);
@ -391,22 +412,10 @@ public class DebugTab : Window, ITab
if (!table)
return;
void DrawSpecial(string name, ActorIdentifier id)
{
if (!id.IsValid)
return;
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());
DrawSpecial("Current Player", _actors.GetCurrentPlayer());
DrawSpecial("Current Inspect", _actors.GetInspectPlayer());
DrawSpecial("Current Card", _actors.GetCardPlayer());
DrawSpecial("Current Glamour", _actors.GetGlamourPlayer());
foreach (var obj in _dalamud.Objects)
{
@ -415,11 +424,25 @@ public class DebugTab : Window, ITab
ImGuiUtil.DrawTableColumn(obj.Address == nint.Zero
? string.Empty
: $"0x{(nint)((Character*)obj.Address)->GameObject.GetDrawObject():X}");
var identifier = _actorService.AwaitedService.FromObject(obj, false, true, false);
ImGuiUtil.DrawTableColumn(_actorService.AwaitedService.ToString(identifier));
var identifier = _actors.FromObject(obj, false, true, false);
ImGuiUtil.DrawTableColumn(_actors.ToString(identifier));
var id = obj.ObjectKind == ObjectKind.BattleNpc ? $"{identifier.DataId} | {obj.DataId}" : identifier.DataId.ToString();
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>
@ -616,7 +639,7 @@ public class DebugTab : Window, ITab
return;
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)
&& (_emoteSearchName.Length == 0
|| 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.Plugin;
using Penumbra.UI.AdvancedWindow;
using Penumbra.UI.Tabs;
using Penumbra.UI.Tabs.Debug;
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>;
namespace Penumbra.Util;
public enum StartTimeType
{
Total,
Identifier,
Stains,
Items,
Actors,
Backup,
Mods,
Collections,
PathResolver,
Interface,
Api,
}
public enum PerformanceType
{
UiMainWindow,
@ -48,23 +32,6 @@ public enum PerformanceType
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)
=> type switch
{

View file

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