Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 55 additions & 22 deletions Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Luna;
using Penumbra.Api.Enums;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
Expand All @@ -23,7 +25,7 @@ public class ResourceTreeFactory(
ActorManager actors,
PathState pathState,
IFramework framework,
ModManager modManager) : Luna.IService
ModManager modManager) : IService
{
private static readonly string ParentDirectoryPrefix = $"..{Path.DirectorySeparatorChar}";

Expand All @@ -48,13 +50,17 @@ public IEnumerable<ICharacter> GetLocalPlayerRelatedCharacters()
var (cache, characters) = framework.RunOnFrameworkThread(() =>
{
var cache = CreateTreeBuildCache();
var characters = ((flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.GetLocalPlayerRelatedCharacters() : cache.GetCharacters()).ToArray();
var characters = ((flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.GetLocalPlayerRelatedCharacters() : cache.GetCharacters())
.SelectWhere(character
=> !CharacterInfo.TryFromCharacter(character, out var info)
? (false, default)
: (true, (character, info))).ToArray();
return (cache, characters);
}).Result;

foreach (var character in characters)
foreach (var (character, info) in characters)
{
var tree = FromCharacter(character, cache, flags);
var tree = FromCharacter(in info, cache, flags);
if (tree != null)
yield return (character, tree);
}
Expand All @@ -66,34 +72,32 @@ public IEnumerable<ICharacter> GetLocalPlayerRelatedCharacters()
var cache = CreateTreeBuildCache(flags);
foreach (var character in characters)
{
var tree = FromCharacter(character, cache, flags);
if (!CharacterInfo.TryFromCharacter(character, out var info))
continue;

var tree = FromCharacter(in info, cache, flags);
if (tree != null)
yield return (character, tree);
}
}

public ResourceTree? FromCharacter(ICharacter character, Flags flags)
=> FromCharacter(character, CreateTreeBuildCache(flags), flags);
=> CharacterInfo.TryFromCharacter(character, out var info)
? FromCharacter(in info, CreateTreeBuildCache(flags), flags)
: null;

private unsafe ResourceTree? FromCharacter(ICharacter character, TreeBuildCache cache, Flags flags)
private unsafe ResourceTree? FromCharacter(in CharacterInfo character, TreeBuildCache cache, Flags flags)
{
if (!character.IsValid())
return null;

var gameObjStruct = (GameObject*)character.Address;
var drawObjStruct = gameObjStruct->GetDrawObject();
if (drawObjStruct == null)
return null;

var collectionResolveData = resolver.IdentifyCollection(gameObjStruct, true);
var collectionResolveData = resolver.IdentifyCollection(character.GameObject, true);
if (!collectionResolveData.Valid)
return null;

var localPlayerRelated = cache.IsLocalPlayerRelated(character);
var (name, anonymizedName, related) = GetCharacterName((GameObject*)character.Address);
var localPlayerRelated = cache.IsLocalPlayerRelated(character.GameObject);
var (name, anonymizedName, related) = GetCharacterName(character.GameObject);
var networked = character.EntityId != 0xE0000000;
var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)gameObjStruct, (nint)drawObjStruct, localPlayerRelated, related,
networked, collectionResolveData.ModCollection.Identity.Name, collectionResolveData.ModCollection.Identity.AnonymizedName);
var tree = new ResourceTree(name, anonymizedName, character.ObjectIndex, (nint)character.GameObject, (nint)character.DrawObject,
localPlayerRelated, related, networked, collectionResolveData.ModCollection.Identity.Name,
collectionResolveData.ModCollection.Identity.AnonymizedName);
var globalContext = new GlobalResolveContext(metaFileManager, objectIdentifier, collectionResolveData.ModCollection,
cache, (flags & Flags.WithUiData) != 0);
using (var _ = pathState.EnterInternalResolve())
Expand All @@ -111,6 +115,7 @@ public IEnumerable<ICharacter> GetLocalPlayerRelatedCharacters()
ResolveUiData(tree);
ResolveModData(tree);
}

FilterFullPaths(tree, (flags & Flags.RedactExternalPaths) is not 0 ? config.Main.ModDirectory : null);
Cleanup(tree);

Expand Down Expand Up @@ -146,7 +151,7 @@ private void ResolveModData(ResourceTree tree)
{
if (node.FullPath.IsRooted && modManager.TryIdentifyPath(node.FullPath.FullName, out var mod, out var relativePath))
{
node.ModName = mod.Name;
node.ModName = mod.Name;
node.Mod.SetTarget(mod);
node.ModRelativePath = relativePath;
}
Expand Down Expand Up @@ -195,7 +200,7 @@ private static void Cleanup(ResourceTree tree)

private unsafe (string Name, string AnonymizedName, bool PlayerRelated) GetCharacterName(GameObject* character)
{
var identifier = actors.FromObject(character, out var owner, true, false, false);
var identifier = actors.FromObject(character, out var owner, true, false, false);
var identifierStr = identifier.ToString();
return (identifierStr, identifier.Incognito(identifierStr), IsPlayerRelated(identifier, owner));
}
Expand All @@ -217,6 +222,34 @@ private unsafe bool IsPlayerRelated(ActorIdentifier identifier, Actor owner)
_ => false,
};

private unsafe struct CharacterInfo(GameObject* gameObject, DrawObject* drawObject, uint entityId, ushort objectIndex)
{
public readonly GameObject* GameObject = gameObject;
public readonly DrawObject* DrawObject = drawObject;
public readonly uint EntityId = entityId;
public readonly ushort ObjectIndex = objectIndex;

public static bool TryFromCharacter(ICharacter character, out CharacterInfo info)
{
if (!character.IsValid())
{
info = default;
return false;
}

var gameObject = (GameObject*)character.Address;
var drawObject = gameObject->GetDrawObject();
if (drawObject is null)
{
info = default;
return false;
}

info = new CharacterInfo(gameObject, drawObject, character.EntityId, character.ObjectIndex);
return true;
}
}

[Flags]
public enum Flags
{
Expand Down
13 changes: 6 additions & 7 deletions Penumbra/Interop/ResourceTree/TreeBuildCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ internal readonly struct TreeBuildCache(ObjectManager? objects, IDataManager dat
{
private readonly Dictionary<FullPath, IReadOnlyDictionary<uint, Name>?> _shaderPackageNames = [];

private readonly IGameObject? _player = objects?.GetDalamudObject(0);
private readonly IGameObject? _player = objects?.GetDalamudObject(0);
private readonly uint _playerId = objects?.GetDalamudObject(0)?.EntityId ?? 0u;

public unsafe bool IsLocalPlayerRelated(ICharacter character)
public unsafe bool IsLocalPlayerRelated(FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* gameObject)
{
if (_player is null)
return false;

var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)character.Address;
var parent = actors.ToCutsceneParent(gameObject->ObjectIndex);
var actualIndex = parent >= 0 ? (ushort)parent : gameObject->ObjectIndex;
return actualIndex switch
{
< 2 => true,
< (int)ScreenActor.CutsceneStart => gameObject->OwnerId == _player.EntityId,
< (int)ScreenActor.CutsceneStart => gameObject->OwnerId == _playerId,
_ => false,
};
}
Expand All @@ -48,10 +48,9 @@ public IEnumerable<ICharacter> GetLocalPlayerRelatedCharacters()
if (minion is not null)
yield return (ICharacter)minion;

var playerId = _player.EntityId;
for (var i = 2; i < ObjectIndex.CutsceneStart.Index; i += 2)
{
if (objects.GetDalamudObject(i) is ICharacter owned && owned.OwnerId == playerId)
if (objects.GetDalamudObject(i) is ICharacter owned && owned.OwnerId == _playerId)
yield return owned;
}

Expand All @@ -65,7 +64,7 @@ public IEnumerable<ICharacter> GetLocalPlayerRelatedCharacters()
if (parent < 0)
continue;

if (parent is 0 or 1 || objects.GetDalamudObject(parent)?.OwnerId == playerId)
if (parent is 0 or 1 || objects.GetDalamudObject(parent)?.OwnerId == _playerId)
yield return character;
}
}
Expand Down
2 changes: 1 addition & 1 deletion Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ private void DrawModifiedGameFilesWarning()
if (!gameData.HasModifiedGameDataFiles)
return;

using var style = ImGuiColor.Text.Push(ImGuiColors.DalamudOrange);
using var style = ImGuiColor.Text.Push(ImGuiColors.WarningForeground);

Im.TextWrapped(
"Dalamud is reporting your FFXIV installation has modified game files. Any mods installed through TexTools will produce this message."u8);
Expand Down
Loading