Net 8, updated submodules

This commit is contained in:
RisaDev
2024-03-21 23:05:36 +03:00
parent 7f18030018
commit d919024f29
18 changed files with 62 additions and 389 deletions

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

View File

@@ -1,116 +0,0 @@
using Penumbra.GameData.Actors;
using System;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
namespace CustomizePlus.GameData.Data;
public readonly unsafe struct Actor : IEquatable<Actor>
{
private Actor(nint address)
=> Address = address;
public static readonly Actor Null = new(nint.Zero);
public readonly nint Address;
public GameObject* AsObject
=> (GameObject*)Address;
public Character* AsCharacter
=> (Character*)Address;
public bool Valid
=> Address != nint.Zero;
public bool IsCharacter
=> Valid && AsObject->IsCharacter();
public static implicit operator Actor(nint? pointer)
=> new(pointer ?? nint.Zero);
public static implicit operator Actor(GameObject* pointer)
=> new((nint)pointer);
public static implicit operator Actor(Character* pointer)
=> new((nint)pointer);
public static implicit operator nint(Actor actor)
=> actor.Address;
public bool IsGPoseOrCutscene
=> Index.Index is >= (int)ScreenActor.CutsceneStart and < (int)ScreenActor.CutsceneEnd;
public ActorIdentifier GetIdentifier(ActorManager actors)
=> actors.FromObject(AsObject, out _, true, true, false);
public ByteString Utf8Name
=> Valid ? new ByteString(AsObject->Name) : ByteString.Empty;
public bool Identifier(ActorManager actors, out ActorIdentifier ident)
{
if (Valid)
{
ident = GetIdentifier(actors);
return ident.IsValid;
}
ident = ActorIdentifier.Invalid;
return false;
}
public ObjectIndex Index
=> Valid ? AsObject->ObjectIndex : ObjectIndex.AnyIndex;
public Model Model
=> Valid ? AsObject->DrawObject : null;
public byte Job
=> IsCharacter ? AsCharacter->CharacterData.ClassJob : (byte)0;
public static implicit operator bool(Actor actor)
=> actor.Address != nint.Zero;
public static bool operator true(Actor actor)
=> actor.Address != nint.Zero;
public static bool operator false(Actor actor)
=> actor.Address == nint.Zero;
public static bool operator !(Actor actor)
=> actor.Address == nint.Zero;
public bool Equals(Actor other)
=> Address == other.Address;
public override bool Equals(object? obj)
=> obj is Actor other && Equals(other);
public override int GetHashCode()
=> Address.GetHashCode();
public static bool operator ==(Actor lhs, Actor rhs)
=> lhs.Address == rhs.Address;
public static bool operator !=(Actor lhs, Actor rhs)
=> lhs.Address != rhs.Address;
/*
/// <summary> Only valid for characters. </summary>
public CharacterArmor GetArmor(EquipSlot slot)
=> ((CharacterArmor*)&AsCharacter->DrawData.Head)[slot.ToIndex()];
public CharacterWeapon GetMainhand()
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).ModelId.Value);
public CharacterWeapon GetOffhand()
=> new(AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).ModelId.Value);
public Customize GetCustomize()
=> *(Customize*)&AsCharacter->DrawData.CustomizeData;
*/
public override string ToString()
=> $"0x{Address:X}";
}

View File

@@ -1,4 +1,6 @@
namespace CustomizePlus.GameData.Data;
using Penumbra.GameData.Interop;
namespace CustomizePlus.GameData.Data;
/// <summary>
/// A single actor with its label and the list of associated game objects.

View File

@@ -1,199 +0,0 @@
using System;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Object = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.Object;
using ObjectType = FFXIVClientStructs.FFXIV.Client.Graphics.Scene.ObjectType;
namespace CustomizePlus.GameData.Data;
public readonly unsafe struct Model : IEquatable<Model>
{
private Model(nint address)
=> Address = address;
public readonly nint Address;
public static readonly Model Null = new(0);
public DrawObject* AsDrawObject
=> (DrawObject*)Address;
public CharacterBase* AsCharacterBase
=> (CharacterBase*)Address;
public Weapon* AsWeapon
=> (Weapon*)Address;
public Human* AsHuman
=> (Human*)Address;
public static implicit operator Model(nint? pointer)
=> new(pointer ?? nint.Zero);
public static implicit operator Model(Object* pointer)
=> new((nint)pointer);
public static implicit operator Model(DrawObject* pointer)
=> new((nint)pointer);
public static implicit operator Model(Human* pointer)
=> new((nint)pointer);
public static implicit operator Model(CharacterBase* pointer)
=> new((nint)pointer);
public static implicit operator nint(Model model)
=> model.Address;
public bool Valid
=> Address != nint.Zero;
public bool IsCharacterBase
=> Valid && AsDrawObject->Object.GetObjectType() == ObjectType.CharacterBase;
public bool IsHuman
=> IsCharacterBase && AsCharacterBase->GetModelType() == CharacterBase.ModelType.Human;
public bool IsWeapon
=> IsCharacterBase && AsCharacterBase->GetModelType() == CharacterBase.ModelType.Weapon;
public static implicit operator bool(Model actor)
=> actor.Address != nint.Zero;
public static bool operator true(Model actor)
=> actor.Address != nint.Zero;
public static bool operator false(Model actor)
=> actor.Address == nint.Zero;
public static bool operator !(Model actor)
=> actor.Address == nint.Zero;
public bool Equals(Model other)
=> Address == other.Address;
public override bool Equals(object? obj)
=> obj is Model other && Equals(other);
public override int GetHashCode()
=> Address.GetHashCode();
public static bool operator ==(Model lhs, Model rhs)
=> lhs.Address == rhs.Address;
public static bool operator !=(Model lhs, Model rhs)
=> lhs.Address != rhs.Address;
/*
/// <summary> Only valid for humans. </summary>
public CharacterArmor GetArmor(EquipSlot slot)
=> ((CharacterArmor*)&AsHuman->Head)[slot.ToIndex()];
public Customize GetCustomize()
=> *(Customize*)&AsHuman->Customize;
public (Model Address, CharacterWeapon Data) GetMainhand()
{
Model weapon = AsDrawObject->Object.ChildObject;
return !weapon.IsWeapon
? (Null, CharacterWeapon.Empty)
: (weapon, new CharacterWeapon(weapon.AsWeapon->ModelSetId, weapon.AsWeapon->SecondaryId, (Variant)weapon.AsWeapon->Variant,
(StainId)weapon.AsWeapon->ModelUnknown));
}
public (Model Address, CharacterWeapon Data) GetOffhand()
{
var mainhand = AsDrawObject->Object.ChildObject;
if (mainhand == null)
return (Null, CharacterWeapon.Empty);
Model offhand = mainhand->NextSiblingObject;
if (offhand == mainhand || !offhand.IsWeapon)
return (Null, CharacterWeapon.Empty);
return (offhand, new CharacterWeapon(offhand.AsWeapon->ModelSetId, offhand.AsWeapon->SecondaryId, (Variant)offhand.AsWeapon->Variant,
(StainId)offhand.AsWeapon->ModelUnknown));
}
/// <summary> Obtain the mainhand and offhand and their data by guesstimating which child object is which. </summary>
public (Model Mainhand, Model Offhand, CharacterWeapon MainData, CharacterWeapon OffData) GetWeapons()
{
var (first, second, count) = GetChildrenWeapons();
switch (count)
{
case 0: return (Null, Null, CharacterWeapon.Empty, CharacterWeapon.Empty);
case 1:
return (first, Null, new CharacterWeapon(first.AsWeapon->ModelSetId, first.AsWeapon->SecondaryId,
(Variant)first.AsWeapon->Variant,
(StainId)first.AsWeapon->ModelUnknown), CharacterWeapon.Empty);
default:
var (main, off) = DetermineMainhand(first, second);
var mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant,
(StainId)main.AsWeapon->ModelUnknown);
var offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant,
(StainId)off.AsWeapon->ModelUnknown);
return (main, off, mainData, offData);
}
}
/// <summary> Obtain the mainhand and offhand and their data by using the drawdata container from the corresponding actor. </summary>
public (Model Mainhand, Model Offhand, CharacterWeapon MainData, CharacterWeapon OffData) GetWeapons(Actor actor)
{
if (!Valid || !actor.IsCharacter || actor.Model.Address != Address)
return (Null, Null, CharacterWeapon.Empty, CharacterWeapon.Empty);
Model main = actor.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.MainHand).DrawObject;
var mainData = CharacterWeapon.Empty;
if (main.IsWeapon)
mainData = new CharacterWeapon(main.AsWeapon->ModelSetId, main.AsWeapon->SecondaryId, (Variant)main.AsWeapon->Variant,
(StainId)main.AsWeapon->ModelUnknown);
else
main = Null;
Model off = actor.AsCharacter->DrawData.Weapon(DrawDataContainer.WeaponSlot.OffHand).DrawObject;
var offData = CharacterWeapon.Empty;
if (off.IsWeapon)
offData = new CharacterWeapon(off.AsWeapon->ModelSetId, off.AsWeapon->SecondaryId, (Variant)off.AsWeapon->Variant,
(StainId)off.AsWeapon->ModelUnknown);
else
off = Null;
return (main, off, mainData, offData);
}
private (Model, Model, int) GetChildrenWeapons()
{
Span<Model> weapons = stackalloc Model[2];
weapons[0] = Null;
weapons[1] = Null;
var count = 0;
if (!Valid || AsDrawObject->Object.ChildObject == null)
return (weapons[0], weapons[1], count);
Model starter = AsDrawObject->Object.ChildObject;
var iterator = starter;
do
{
if (iterator.IsWeapon)
weapons[count++] = iterator;
if (count == 2)
return (weapons[0], weapons[1], count);
iterator = iterator.AsDrawObject->Object.NextSiblingObject;
} while (iterator.Address != starter.Address);
return (weapons[0], weapons[1], count);
}
/// <summary> I don't know a safe way to do this but in experiments this worked.
/// The first uint at +0x8 was set to non-zero for the mainhand and zero for the offhand. </summary>
private static (Model Mainhand, Model Offhand) DetermineMainhand(Model first, Model second)
{
var discriminator1 = *(ulong*)(first.Address + 0x10);
var discriminator2 = *(ulong*)(second.Address + 0x10);
return discriminator1 == 0 && discriminator2 != 0 ? (second, first) : (first, second);
}
*/
public override string ToString()
=> $"0x{Address:X}";
}

View File

@@ -5,6 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.Control;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -14,25 +15,11 @@ using System.Threading.Tasks;
namespace CustomizePlus.GameData.Services;
public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
public class ObjectManager(IFramework framework, IClientState clientState, global::Penumbra.GameData.Interop.ObjectManager objects, ActorManager actorManager, ITargetManager targetManager)
: IReadOnlyDictionary<ActorIdentifier, ActorData>
{
private readonly IFramework _framework;
private readonly IClientState _clientState;
private readonly IObjectTable _objects;
private readonly ActorManager _actorManager;
private readonly ITargetManager _targets;
public IObjectTable Objects
=> _objects;
public ObjectManager(IFramework framework, IClientState clientState, IObjectTable objects, ActorManager actorManager, ITargetManager targets)
{
_framework = framework;
_clientState = clientState;
_objects = objects;
_actorManager = actorManager;
_targets = targets;
}
public global::Penumbra.GameData.Interop.ObjectManager Objects
=> objects;
public DateTime LastUpdate { get; private set; }
@@ -48,39 +35,39 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
public void Update()
{
var lastUpdate = _framework.LastUpdate;
var lastUpdate = framework.LastUpdate;
if (lastUpdate <= LastUpdate)
return;
LastUpdate = lastUpdate;
World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
World = (ushort)(clientState.LocalPlayer?.CurrentWorld.Id ?? 0u);
_identifiers.Clear();
_allWorldIdentifiers.Clear();
_nonOwnedIdentifiers.Clear();
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
{
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(_actorManager, out var identifier))
Actor character = objects[i];
if (character.Identifier(actorManager, out var identifier))
HandleIdentifier(identifier, character);
}
for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i)
{
Actor character = _objects.GetObjectAddress(i);
Actor character = objects[i];
// Technically the game does not create holes in cutscenes or GPose.
// But for Brio compatibility, we allow holes in GPose.
// Since GPose always has the event actor in the first cutscene slot, we can still optimize in this case.
if (!character.Valid && i == (int)ScreenActor.CutsceneStart)
break;
HandleIdentifier(character.GetIdentifier(_actorManager), character);
HandleIdentifier(character.GetIdentifier(actorManager), character);
}
void AddSpecial(ScreenActor idx, string label)
{
Actor actor = _objects.GetObjectAddress((int)idx);
if (actor.Identifier(_actorManager, out var ident))
Actor actor = objects[(int)idx];
if (actor.Identifier(actorManager, out var ident))
{
var data = new ActorData(actor, label);
_identifiers.Add(ident, data);
@@ -96,10 +83,10 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
AddSpecial(ScreenActor.Card7, "Card Actor 7");
AddSpecial(ScreenActor.Card8, "Card Actor 8");
for (var i = (int)ScreenActor.ScreenEnd; i < _objects.Length; ++i)
for (var i = (int)ScreenActor.ScreenEnd; i < objects.Count; ++i)
{
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(_actorManager, out var identifier))
Actor character = objects[i];
if (character.Identifier(actorManager, out var identifier))
HandleIdentifier(identifier, character);
}
@@ -124,7 +111,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
if (identifier.Type is IdentifierType.Player or IdentifierType.Owned)
{
var allWorld = _actorManager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
var allWorld = actorManager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
identifier.Kind,
identifier.DataId);
@@ -141,7 +128,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
if (identifier.Type is IdentifierType.Owned)
{
var nonOwned = _actorManager.CreateNpc(identifier.Kind, identifier.DataId);
var nonOwned = actorManager.CreateNpc(identifier.Kind, identifier.DataId);
if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData))
{
nonOwnedData = new ActorData(character, nonOwned.ToString());
@@ -155,26 +142,26 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
}
public Actor GPosePlayer
=> _objects.GetObjectAddress((int)ScreenActor.GPosePlayer);
=> objects[(int)ScreenActor.GPosePlayer];
public Actor Player
=> _objects.GetObjectAddress(0);
=> objects[0];
public unsafe Actor Target
=> _clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target;
=> clientState.IsGPosing ? TargetSystem.Instance()->GPoseTarget : TargetSystem.Instance()->Target;
public Actor Focus
=> _targets.FocusTarget?.Address ?? nint.Zero;
=> targetManager.FocusTarget?.Address ?? nint.Zero;
public Actor MouseOver
=> _targets.MouseOverTarget?.Address ?? nint.Zero;
=> targetManager.MouseOverTarget?.Address ?? nint.Zero;
public (ActorIdentifier Identifier, ActorData Data) PlayerData
{
get
{
Update();
return Player.Identifier(_actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
return Player.Identifier(actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
? (ident, data)
: (ident, ActorData.Invalid);
}
@@ -185,7 +172,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
get
{
Update();
return Target.Identifier(_actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
return Target.Identifier(actorManager, out var ident) && _identifiers.TryGetValue(ident, out var data)
? (ident, data)
: (ident, ActorData.Invalid);
}

View File

@@ -21,6 +21,7 @@ using CustomizePlus.Core.Extensions;
using CustomizePlus.Armatures.Events;
using CustomizePlus.Armatures.Data;
using CustomizePlus.GameData.Extensions;
using Penumbra.GameData.Interop;
namespace CustomizePlus.Api.Compatibility;

View File

@@ -1,26 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Plugin.Services;
using OtterGui.Log;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using System.Numerics;
using CustomizePlus.Core.Data;
using CustomizePlus.Armatures.Events;
using CustomizePlus.Armatures.Data;
using CustomizePlus.Armatures.Events;
using CustomizePlus.Core.Data;
using CustomizePlus.Core.Extensions;
using CustomizePlus.Game.Services;
using CustomizePlus.GameData.Extensions;
using CustomizePlus.GameData.Services;
using CustomizePlus.Profiles;
using CustomizePlus.Profiles.Data;
using CustomizePlus.Game.Services;
using CustomizePlus.Templates.Events;
using CustomizePlus.Profiles.Events;
using CustomizePlus.Core.Extensions;
using CustomizePlus.GameData.Data;
using CustomizePlus.GameData.Services;
using CustomizePlus.GameData.Extensions;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using System.Drawing;
using CustomizePlus.Templates.Events;
using Dalamud.Plugin.Services;
using OtterGui.Classes;
using OtterGui.Log;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
namespace CustomizePlus.Armatures.Services;

View File

@@ -33,6 +33,7 @@ using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using OtterGui;
using OtterGui.Raii;
namespace CustomizePlus.Core;
@@ -61,7 +62,7 @@ public static class ServiceManagerBuilder
services.AddIServices(typeof(EquipItem).Assembly);
services.AddIServices(typeof(Plugin).Assembly);
services.AddIServices(typeof(ObjectManager).Assembly);
services.AddIServices(typeof(ImGuiUtil).Assembly);
services.AddIServices(typeof(ImRaii).Assembly);
services.CreateProvider();

View File

@@ -25,6 +25,8 @@ public class DalamudServices
.AddDalamudService<IKeyState>(pi)
.AddDalamudService<IDataManager>(pi)
.AddDalamudService<IPluginLog>(pi)
.AddDalamudService<ITargetManager>(pi);
.AddDalamudService<ITargetManager>(pi)
.AddDalamudService<INotificationManager>(pi)
.AddDalamudService<IContextMenu>(pi);
}
}

View File

@@ -10,6 +10,7 @@ using CustomizePlus.Configuration.Data;
using CustomizePlus.Profiles;
using CustomizePlus.Armatures.Services;
using CustomizePlus.GameData.Data;
using Penumbra.GameData.Interop;
namespace CustomizePlus.Core.Services;

View File

@@ -13,7 +13,7 @@
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<Platforms>x64</Platforms>
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
@@ -24,12 +24,6 @@
<OutputPath>bin\$(Configuration)\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Util\**" />
<EmbeddedResource Remove="Util\**" />
<None Remove="Util\**" />
</ItemGroup>
<ItemGroup>
<Content Include="..\Data\icon.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -44,7 +38,7 @@
<ItemGroup>
<!--<PackageReference Include="DalamudPackager" Version="2.1.12" />-->
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -1,11 +1,11 @@
using Dalamud.Plugin.Services;
using Penumbra.GameData.Actors;
using System.Collections.Generic;
using System.Collections.Generic;
using CustomizePlus.Core.Data;
using CustomizePlus.GameData.Data;
using CustomizePlus.GameData.Services;
using CustomizePlus.GameData.Extensions;
using Dalamud.Plugin.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using ObjectManager = CustomizePlus.GameData.Services.ObjectManager;
namespace CustomizePlus.Game.Services;

View File

@@ -23,6 +23,7 @@ using CustomizePlus.GameData.Services;
using CustomizePlus.GameData.Extensions;
using CustomizePlus.Profiles.Enums;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
namespace CustomizePlus.Profiles;

View File

@@ -30,7 +30,7 @@ public abstract class TemplateComboBase : FilterComboCache<Tuple<Template, strin
TemplateChanged templateChanged,
//TabSelected tabSelected,
PluginConfiguration configuration)
: base(generator, logger)
: base(generator, MouseWheelType.Control, logger)
{
_templateChanged = templateChanged;
//TabSelected = tabSelected;