mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Optimize ResourceTree somewhat.
This commit is contained in:
parent
69388689ac
commit
11bf0d2998
5 changed files with 108 additions and 63 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 7c483764678c6edb5efd55f056aeaecae144d5fe
|
||||
Subproject commit ef403be979bfac5ef805030ce76066151d36f112
|
||||
|
|
@ -18,6 +18,7 @@ using Penumbra.Collections.Manager;
|
|||
using Dalamud.Plugin.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using System.Diagnostics;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.Api;
|
||||
|
||||
|
|
@ -1450,7 +1451,7 @@ public class IpcTester : IDisposable
|
|||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastGameObjectResourcePaths = gameObjects
|
||||
.Select(GameObjectToString)
|
||||
.Select(i => GameObjectToString(i))
|
||||
.Zip(resourcePaths)
|
||||
.ToArray();
|
||||
|
||||
|
|
@ -1482,7 +1483,7 @@ public class IpcTester : IDisposable
|
|||
|
||||
_lastCallDuration = _stopwatch.Elapsed;
|
||||
_lastGameObjectResourcesOfType = gameObjects
|
||||
.Select(GameObjectToString)
|
||||
.Select(i => GameObjectToString(i))
|
||||
.Zip(resourcesOfType)
|
||||
.ToArray();
|
||||
|
||||
|
|
@ -1630,9 +1631,9 @@ public class IpcTester : IDisposable
|
|||
.SelectWhere(index => (ushort.TryParse(index.Trim(), out var i), i))
|
||||
.ToArray();
|
||||
|
||||
private unsafe string GameObjectToString(ushort gameObjectIndex)
|
||||
private unsafe string GameObjectToString(ObjectIndex gameObjectIndex)
|
||||
{
|
||||
var gameObject = _objects[gameObjectIndex];
|
||||
var gameObject = _objects[gameObjectIndex.Index];
|
||||
|
||||
return gameObject != null
|
||||
? $"[{gameObjectIndex}] {gameObject.Name} ({gameObject.ObjectKind})"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Dalamud.Plugin.Services;
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.ResourceTree;
|
||||
using Penumbra.String;
|
||||
|
||||
|
|
@ -15,10 +16,10 @@ public enum DrawObjectType
|
|||
Vfx,
|
||||
};
|
||||
|
||||
public readonly record struct MaterialInfo(ushort ObjectIndex, DrawObjectType Type, int ModelSlot, int MaterialSlot)
|
||||
public readonly record struct MaterialInfo(ObjectIndex ObjectIndex, DrawObjectType Type, int ModelSlot, int MaterialSlot)
|
||||
{
|
||||
public nint GetCharacter(IObjectTable objects)
|
||||
=> objects.GetObjectAddress(ObjectIndex);
|
||||
=> objects.GetObjectAddress(ObjectIndex.Index);
|
||||
|
||||
public nint GetDrawObject(nint address)
|
||||
=> GetDrawObject(Type, address);
|
||||
|
|
@ -71,7 +72,7 @@ public readonly record struct MaterialInfo(ushort ObjectIndex, DrawObjectType Ty
|
|||
if (gameObject == null)
|
||||
continue;
|
||||
|
||||
var index = gameObject->GameObject.ObjectIndex;
|
||||
var index = (ObjectIndex) gameObject->GameObject.ObjectIndex;
|
||||
|
||||
foreach (var type in Enum.GetValues<DrawObjectType>())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,21 +30,20 @@ public class ResourceTreeFactory
|
|||
_actors = actors;
|
||||
}
|
||||
|
||||
private TreeBuildCache CreateTreeBuildCache(bool withCharacters)
|
||||
=> new(_objects, _gameData, _actors, withCharacters);
|
||||
private TreeBuildCache CreateTreeBuildCache()
|
||||
=> new(_objects, _gameData, _actors);
|
||||
|
||||
public IEnumerable<Dalamud.Game.ClientState.Objects.Types.Character> GetLocalPlayerRelatedCharacters()
|
||||
{
|
||||
var cache = CreateTreeBuildCache(true);
|
||||
|
||||
return cache.Characters.Where(cache.IsLocalPlayerRelated);
|
||||
var cache = CreateTreeBuildCache();
|
||||
return cache.GetLocalPlayerRelatedCharacters();
|
||||
}
|
||||
|
||||
public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromObjectTable(
|
||||
Flags flags)
|
||||
{
|
||||
var cache = CreateTreeBuildCache(true);
|
||||
var characters = (flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.Characters.Where(cache.IsLocalPlayerRelated) : cache.Characters;
|
||||
var cache = CreateTreeBuildCache();
|
||||
var characters = (flags & Flags.LocalPlayerRelatedOnly) != 0 ? cache.GetLocalPlayerRelatedCharacters() : cache.GetCharacters();
|
||||
|
||||
foreach (var character in characters)
|
||||
{
|
||||
|
|
@ -57,7 +56,7 @@ public class ResourceTreeFactory
|
|||
public IEnumerable<(Dalamud.Game.ClientState.Objects.Types.Character Character, ResourceTree ResourceTree)> FromCharacters(
|
||||
IEnumerable<Dalamud.Game.ClientState.Objects.Types.Character> characters, Flags flags)
|
||||
{
|
||||
var cache = CreateTreeBuildCache((flags & Flags.WithOwnership) != 0);
|
||||
var cache = CreateTreeBuildCache();
|
||||
foreach (var character in characters)
|
||||
{
|
||||
var tree = FromCharacter(character, cache, flags);
|
||||
|
|
@ -67,7 +66,7 @@ public class ResourceTreeFactory
|
|||
}
|
||||
|
||||
public ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, Flags flags)
|
||||
=> FromCharacter(character, CreateTreeBuildCache((flags & Flags.WithOwnership) != 0), flags);
|
||||
=> FromCharacter(character, CreateTreeBuildCache(), flags);
|
||||
|
||||
private unsafe ResourceTree? FromCharacter(Dalamud.Game.ClientState.Objects.Types.Character character, TreeBuildCache cache, Flags flags)
|
||||
{
|
||||
|
|
@ -136,7 +135,7 @@ public class ResourceTreeFactory
|
|||
if (filteredList.Count > 0)
|
||||
resolvedList = filteredList;
|
||||
}
|
||||
|
||||
|
||||
if (resolvedList.Count != 1)
|
||||
{
|
||||
Penumbra.Log.Debug(
|
||||
|
|
@ -216,27 +215,22 @@ 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);
|
||||
string name;
|
||||
bool playerRelated;
|
||||
var identifier = _actors.AwaitedService.FromObject((GameObject*)character.Address, out var owner, true, false, false);
|
||||
switch (identifier.Type)
|
||||
{
|
||||
case IdentifierType.Player:
|
||||
name = identifier.PlayerName.ToString();
|
||||
playerRelated = true;
|
||||
break;
|
||||
case IdentifierType.Owned when cache.CharactersById.TryGetValue(owner->ObjectID, out var ownerChara):
|
||||
var ownerName = GetCharacterName(ownerChara, cache);
|
||||
name = $"[{ownerName.Name}] {character.Name} ({identifier.Kind.ToName()})";
|
||||
playerRelated = ownerName.PlayerRelated;
|
||||
break;
|
||||
default:
|
||||
name = $"{character.Name} ({identifier.Kind.ToName()})";
|
||||
playerRelated = false;
|
||||
case IdentifierType.Player: return (identifier.PlayerName.ToString(), true);
|
||||
case IdentifierType.Owned:
|
||||
var ownerChara = _objects.CreateObjectReference((nint)owner) as Dalamud.Game.ClientState.Objects.Types.Character;
|
||||
if (ownerChara != null)
|
||||
{
|
||||
var ownerName = GetCharacterName(ownerChara, cache);
|
||||
return ($"[{ownerName.Name}] {character.Name} ({identifier.Kind.ToName()})", ownerName.PlayerRelated);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (name, playerRelated);
|
||||
return ($"{character.Name} ({identifier.Kind.ToName()})", false);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
|
|
|||
|
|
@ -1,55 +1,104 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Dalamud.Game.ClientState.Objects.Types;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Files;
|
||||
using Penumbra.Interop.Services;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Services;
|
||||
using Penumbra.String;
|
||||
using Penumbra.String.Classes;
|
||||
|
||||
namespace Penumbra.Interop.ResourceTree;
|
||||
|
||||
internal class TreeBuildCache
|
||||
internal readonly struct TreeBuildCache
|
||||
{
|
||||
private readonly IDataManager _dataManager;
|
||||
private readonly ActorService _actors;
|
||||
private readonly Dictionary<FullPath, ShpkFile?> _shaderPackages = new();
|
||||
private readonly uint _localPlayerId;
|
||||
public readonly List<Character> Characters;
|
||||
public readonly Dictionary<uint, Character> CharactersById;
|
||||
private readonly IObjectTable _objects;
|
||||
|
||||
public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors, bool withCharacters)
|
||||
public TreeBuildCache(IObjectTable objects, IDataManager dataManager, ActorService actors)
|
||||
{
|
||||
_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());
|
||||
}
|
||||
else
|
||||
{
|
||||
Characters = new();
|
||||
CharactersById = new();
|
||||
}
|
||||
_dataManager = dataManager;
|
||||
_objects = objects;
|
||||
_actors = actors;
|
||||
}
|
||||
|
||||
public unsafe bool IsLocalPlayerRelated(Character character)
|
||||
{
|
||||
if (_localPlayerId == GameObject.InvalidGameObjectId)
|
||||
var player = _objects[0];
|
||||
if (player == null)
|
||||
return false;
|
||||
|
||||
// Index 0 is the local player, index 1 is the mount/minion/accessory.
|
||||
if (character.ObjectIndex < 2 || character.ObjectIndex == RedrawService.GPosePlayerIdx)
|
||||
return true;
|
||||
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)character.Address;
|
||||
var parent = _actors.AwaitedService.ToCutsceneParent(gameObject->ObjectIndex);
|
||||
var actualIndex = parent >= 0 ? (ushort)parent : gameObject->ObjectIndex;
|
||||
return actualIndex switch
|
||||
{
|
||||
< 2 => true,
|
||||
< (int)ScreenActor.CutsceneStart => gameObject->OwnerID == player.ObjectId,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!_actors.AwaitedService.FromObject(character, out var owner, true, false, false).IsValid)
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
=> _objects.OfType<Character>();
|
||||
|
||||
public IEnumerable<Character> GetLocalPlayerRelatedCharacters()
|
||||
{
|
||||
var player = _objects[0];
|
||||
if (player == null)
|
||||
yield break;
|
||||
|
||||
yield return (Character)player;
|
||||
|
||||
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)
|
||||
yield return owned;
|
||||
}
|
||||
|
||||
for (var i = ObjectIndex.CutsceneStart.Index; i < ObjectIndex.CharacterScreen.Index; ++i)
|
||||
{
|
||||
var character = _objects[i] as Character;
|
||||
if (character == null)
|
||||
continue;
|
||||
|
||||
var parent = _actors.AwaitedService.ToCutsceneParent(i);
|
||||
if (parent < 0)
|
||||
continue;
|
||||
|
||||
if (parent is 0 or 1 || _objects[parent]?.OwnerId == playerId)
|
||||
yield return character;
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe ByteString GetPlayerName(GameObject player)
|
||||
{
|
||||
var gameObject = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)player.Address;
|
||||
return new ByteString(gameObject->Name);
|
||||
}
|
||||
|
||||
private unsafe bool GetOwnedId(ByteString playerName, uint playerId, int idx, [NotNullWhen(true)] out Character? character)
|
||||
{
|
||||
character = _objects[idx] as Character;
|
||||
if (character == null)
|
||||
return false;
|
||||
|
||||
// Check for SMN/SCH pet, chocobo and other owned NPCs.
|
||||
return owner != null && owner->ObjectID == _localPlayerId;
|
||||
var actorId = _actors.AwaitedService.FromObject(character, out var owner, true, true, true);
|
||||
if (!actorId.IsValid)
|
||||
return false;
|
||||
if (owner != null && owner->OwnerID != playerId)
|
||||
return false;
|
||||
if (actorId.Type is not IdentifierType.Player || !actorId.PlayerName.Equals(playerName))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Try to read a shpk file from the given path and cache it on success. </summary>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue