ResourceTree: Avoid enumerating the whole object table in some cases

This commit is contained in:
Exter-N 2023-09-18 12:35:41 +02:00 committed by Ottermandias
parent 22966e648d
commit a241b933ca
4 changed files with 48 additions and 29 deletions

View file

@ -1019,7 +1019,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public IReadOnlyDictionary<string, string[]>?[] GetGameObjectResourcePaths(ushort[] gameObjects)
{
var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType<Character>();
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, false, false);
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, 0);
var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees);
return Array.ConvertAll(gameObjects, obj => pathDictionaries.TryGetValue(obj, out var pathDict) ? pathDict : null);
@ -1027,7 +1027,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public IReadOnlyDictionary<ushort, IReadOnlyDictionary<string, string[]>> GetPlayerResourcePaths()
{
var resourceTrees = _resourceTreeFactory.FromObjectTable(true, false, false);
var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly);
var pathDictionaries = ResourceTreeApiHelper.GetResourcePathDictionaries(resourceTrees);
return pathDictionaries.AsReadOnly();
@ -1036,7 +1036,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>?[] GetGameObjectResourcesOfType(ushort[] gameObjects, ResourceType type, bool withUIData)
{
var characters = gameObjects.Select(index => _dalamud.Objects[index]).OfType<Character>();
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData, false);
var resourceTrees = _resourceTreeFactory.FromCharacters(characters, withUIData ? ResourceTreeFactory.Flags.WithUIData : 0);
var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);
return Array.ConvertAll(gameObjects, obj => resDictionaries.TryGetValue(obj, out var resDict) ? resDict : null);
@ -1044,7 +1044,7 @@ public class PenumbraApi : IDisposable, IPenumbraApi
public IReadOnlyDictionary<ushort, IReadOnlyDictionary<nint, (string, string, ChangedItemIcon)>> GetPlayerResourcesOfType(ResourceType type, bool withUIData)
{
var resourceTrees = _resourceTreeFactory.FromObjectTable(true, withUIData, false);
var resourceTrees = _resourceTreeFactory.FromObjectTable(ResourceTreeFactory.Flags.LocalPlayerRelatedOnly | (withUIData ? ResourceTreeFactory.Flags.WithUIData : 0));
var resDictionaries = ResourceTreeApiHelper.GetResourcesOfType(resourceTrees, type);
return resDictionaries.AsReadOnly();

View file

@ -27,49 +27,46 @@ public class ResourceTreeFactory
_actors = actors;
}
private TreeBuildCache CreateTreeBuildCache()
=> new(_objects, _gameData, _actors);
private TreeBuildCache CreateTreeBuildCache(bool withCharacters)
=> new(_objects, _gameData, _actors, withCharacters);
public IEnumerable<Dalamud.Game.ClientState.Objects.Types.Character> GetLocalPlayerRelatedCharacters()
{
var cache = CreateTreeBuildCache();
var cache = CreateTreeBuildCache(true);
return cache.Characters.Where(cache.IsLocalPlayerRelated);
}
public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromObjectTable(
bool localPlayerRelatedOnly = false, bool withUIData = true, bool redactExternalPaths = true)
Flags flags)
{
var cache = CreateTreeBuildCache();
var characters = localPlayerRelatedOnly ? cache.Characters.Where(cache.IsLocalPlayerRelated) : cache.Characters;
var cache = CreateTreeBuildCache(true);
var characters = (flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.Characters.Where(cache.IsLocalPlayerRelated) : cache.Characters;
foreach (var character in characters)
{
var tree = FromCharacter(character, cache, withUIData, redactExternalPaths);
var tree = FromCharacter(character, cache, flags);
if (tree != null)
yield return (character, tree);
}
}
public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromCharacters(
IEnumerable<Dalamud.Game.ClientState.Objects.Types.Character> characters,
bool withUIData = true, bool redactExternalPaths = true)
IEnumerable<Dalamud.Game.ClientState.Objects.Types.Character> characters, Flags flags)
{
var cache = CreateTreeBuildCache();
var cache = CreateTreeBuildCache((flags & Flags.WithOwnership) != 0);
foreach (var character in characters)
{
var tree = FromCharacter(character, cache, withUIData, redactExternalPaths);
var tree = FromCharacter(character, cache, flags);
if (tree != null)
yield return (character, tree);
}
}
public ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, bool withUIData = true,
bool redactExternalPaths = true)
=> FromCharacter(character, CreateTreeBuildCache(), withUIData, redactExternalPaths);
public ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, Flags flags)
=> FromCharacter(character, CreateTreeBuildCache((flags & Flags.WithOwnership) != 0), flags);
private unsafe ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache,
bool withUIData = true, bool redactExternalPaths = true)
private unsafe ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache, Flags flags)
{
if (!character.IsValid())
return null;
@ -88,7 +85,7 @@ 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(_config, _identifier.AwaitedService, cache, collectionResolveData.ModCollection,
((Character*)gameObjStruct)->CharacterData.ModelCharaId, withUIData, redactExternalPaths);
((Character*)gameObjStruct)->CharacterData.ModelCharaId, (flags & Flags.WithUIData) != 0, (flags & Flags.RedactExternalPaths) != 0);
tree.LoadResources(globalContext);
tree.FlatNodes.UnionWith(globalContext.Nodes.Values);
return tree;
@ -119,4 +116,13 @@ public class ResourceTreeFactory
return (name, playerRelated);
}
[Flags]
public enum Flags
{
RedactExternalPaths = 1,
WithUIData = 2,
LocalPlayerRelatedOnly = 4,
WithOwnership = 8,
}
}

View file

@ -16,16 +16,24 @@ internal class TreeBuildCache
public readonly List<Character> Characters;
public readonly Dictionary<uint, Character> CharactersById;
public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors)
public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors, bool withCharacters)
{
_dataManager = dataManager;
_actors = actors;
_localPlayerId = objects[0]?.ObjectId ?? GameObject.InvalidGameObjectId;
if (withCharacters)
{
Characters = objects.OfType<Character>().Where(ch => ch.IsValid()).ToList();
CharactersById = Characters
.Where(c => c.ObjectId != GameObject.InvalidGameObjectId)
.GroupBy(c => c.ObjectId)
.ToDictionary(c => c.Key, c => c.First());
_localPlayerId = Characters.Count > 0 && Characters[0].ObjectIndex == 0 ? Characters[0].ObjectId : GameObject.InvalidGameObjectId;
}
else
{
Characters = new();
CharactersById = new();
}
}
public unsafe bool IsLocalPlayerRelated(Character character)

View file

@ -9,6 +9,11 @@ namespace Penumbra.UI.AdvancedWindow;
public class ResourceTreeViewer
{
private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags =
ResourceTreeFactory.Flags.RedactExternalPaths |
ResourceTreeFactory.Flags.WithUIData |
ResourceTreeFactory.Flags.WithOwnership;
private readonly Configuration _config;
private readonly ResourceTreeFactory _treeFactory;
private readonly ChangedItemDrawer _changedItemDrawer;
@ -101,7 +106,7 @@ public class ResourceTreeViewer
{
try
{
return _treeFactory.FromObjectTable()
return _treeFactory.FromObjectTable(ResourceTreeFactoryFlags)
.Select(entry => entry.ResourceTree)
.ToArray();
}