diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..ca1847ac --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,24 @@ +name: .NET Build + +on: + push: + branches-ignore: main + tags-ignore: '*' + pull_request: + +jobs: + build: + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + submodules: recursive + - name: Prepare Environment + uses: Aether-Tools/Deployment/.github/actions/prepare-environment@master + with: + project: CustomizePlus + - name: Build and Upload Artifact + uses: Aether-Tools/Deployment/.github/actions/build-with-artifact@master + with: + project: CustomizePlus diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eb8b996c..856a02a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,81 +9,28 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - - name: Setup .NET - uses: actions/setup-dotnet@v5 + - name: Prepare Environment + uses: Aether-Tools/Deployment/.github/actions/prepare-environment@master with: - dotnet-version: | - 10.x.x - 9.x.x - - name: Restore dependencies - run: dotnet restore - - name: Download Dalamud - run: | - Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip - Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev" - - name: Build - run: | - $ver = '${{ github.ref_name }}' - invoke-expression 'dotnet build --no-restore --configuration Release --nologo -p:Version=$ver -p:FileVersion=$ver -p:AssemblyVersion=$ver' - - name: write version into jsons - run: | - $ver = '${{ github.ref_name }}' - $path = './CustomizePlus/bin/Release/CustomizePlus.json' - $json = Get-Content -Raw $path | ConvertFrom-Json - $json.AssemblyVersion = $ver - $content = $json | ConvertTo-Json - set-content -Path $path -Value $content - - name: Archive - run: Compress-Archive -Path CustomizePlus/bin/Release/* -DestinationPath CustomizePlus.zip - - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 + project: CustomizePlus + - name: Build and Upload Artifact + uses: Aether-Tools/Deployment/.github/actions/build-with-artifact@master with: - path: | - ./CustomizePlus/bin/Release/* + project: CustomizePlus + version: ${{ github.ref_name }} - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: Aether-Tools/Deployment/.github/actions/create-release@master with: - tag_name: ${{ github.ref }} - release_name: CustomizePlus ${{ github.ref }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} + project: CustomizePlus + version: ${{ github.ref_name }} + - name: Update repo.json + uses: Aether-Tools/Deployment/.github/actions/update-repo@master with: - upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ./CustomizePlus.zip - asset_name: CustomizePlus.zip - asset_content_type: application/zip - - - name: Write out repo.json - run: | - $ver = '${{ github.ref_name }}' - $path = './repo.json' - $json = Get-Content -Raw $path | ConvertFrom-Json - $json[0].AssemblyVersion = $ver - $json[0].TestingAssemblyVersion = $ver - $json[0].DownloadLinkInstall = $json.DownloadLinkInstall -replace '[^/]+/CustomizePlus.zip',"$ver/CustomizePlus.zip" - $json[0].DownloadLinkTesting = $json.DownloadLinkTesting -replace '[^/]+/CustomizePlus.zip',"$ver/CustomizePlus.zip" - $json[0].DownloadLinkUpdate = $json.DownloadLinkUpdate -replace '[^/]+/CustomizePlus.zip',"$ver/CustomizePlus.zip" - $content = $json | ConvertTo-Json -AsArray - set-content -Path $path -Value $content - - - name: Commit repo.json - run: | - git config --global user.name "Actions User" - git config --global user.email "actions@github.com" - git fetch origin main - git branch -f main ${{ github.sha }} - git checkout main - git add repo.json - git commit -m "[CI] Updating repo.json for ${{ github.ref_name }}" || true - git push origin main + token: ${{ secrets.GITHUB_TOKEN }} + project: CustomizePlus + version: ${{ github.ref_name }} + branch: main diff --git a/.github/workflows/test_release.yml b/.github/workflows/test_release.yml index e71a8e20..6e81fdf7 100644 --- a/.github/workflows/test_release.yml +++ b/.github/workflows/test_release.yml @@ -1,4 +1,4 @@ -name: Create Test Release +name: Create Release on: push: @@ -9,79 +9,31 @@ jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - - name: Setup .NET - uses: actions/setup-dotnet@v5 + - name: Prepare Environment + uses: Aether-Tools/Deployment/.github/actions/prepare-environment@master with: - dotnet-version: | - 10.x.x - 9.x.x - - name: Restore dependencies - run: dotnet restore - - name: Download Dalamud - run: | - Invoke-WebRequest -Uri https://goatcorp.github.io/dalamud-distrib/stg/latest.zip -OutFile latest.zip - Expand-Archive -Force latest.zip "$env:AppData\XIVLauncher\addon\Hooks\dev" - - name: Build - run: | - $ver = '${{ github.ref_name }}' -replace 'testing_' - invoke-expression 'dotnet build --no-restore --configuration Release --nologo -p:Version=$ver -p:FileVersion=$ver -p:AssemblyVersion=$ver' - - name: write version into json - run: | - $ver = '${{ github.ref_name }}' -replace 'testing_' - $path = './CustomizePlus/bin/Release/CustomizePlus.json' - $json = Get-Content -Raw $path | ConvertFrom-Json - $json.AssemblyVersion = $ver - $content = $json | ConvertTo-Json - set-content -Path $path -Value $content - - name: Archive - run: Compress-Archive -Path CustomizePlus/bin/Release/* -DestinationPath CustomizePlus.zip - - name: Upload a Build Artifact - uses: actions/upload-artifact@v4 + dalamud-distrib: stg/ + project: CustomizePlus + - name: Build and Upload Artifact + uses: Aether-Tools/Deployment/.github/actions/build-with-artifact@master with: - path: | - ./CustomizePlus/bin/Release/* + configuration: Debug + project: CustomizePlus + version: ${{ github.ref_name }} - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: Aether-Tools/Deployment/.github/actions/create-release@master with: - tag_name: ${{ github.ref }} - release_name: CustomizePlus ${{ github.ref }} - draft: false - prerelease: false - - name: Upload Release Asset - id: upload-release-asset - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} + project: CustomizePlus + version: ${{ github.ref_name }} + - name: Update repo.json + uses: Aether-Tools/Deployment/.github/actions/update-repo@master with: - upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps - asset_path: ./CustomizePlus.zip - asset_name: CustomizePlus.zip - asset_content_type: application/zip - - - name: Write out repo.json - run: | - $verT = '${{ github.ref_name }}' - $ver = $verT -replace 'testing_' - $path = './repo.json' - $json = Get-Content -Raw $path | ConvertFrom-Json - $json[0].TestingAssemblyVersion = $ver - $json[0].DownloadLinkTesting = $json.DownloadLinkTesting -replace '[^/]+/CustomizePlus.zip',"$verT/CustomizePlus.zip" - $content = $json | ConvertTo-Json -AsArray - set-content -Path $path -Value $content - - - name: Commit repo.json - run: | - git config --global user.name "Actions User" - git config --global user.email "actions@github.com" - git fetch origin main - git branch -f main ${{ github.sha }} - git checkout main - git add repo.json - git commit -m "[CI] Updating repo.json for ${{ github.ref_name }}" || true - git push origin main + token: ${{ secrets.GITHUB_TOKEN }} + project: CustomizePlus + version: ${{ github.ref_name }} + branch: main + testing: true \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 7ac2102b..e7d6113d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "submodules/OtterGui"] - path = submodules/OtterGui - url = https://github.com/Ottermandias/OtterGui.git - branch = main [submodule "submodules/Penumbra.GameData"] path = submodules/Penumbra.GameData url = https://github.com/Ottermandias/Penumbra.GameData @@ -17,4 +13,8 @@ [submodule "submodules/ECommonsLite"] path = submodules/ECommonsLite url = https://github.com/Aether-Tools/ECommonsLite.git + branch = api15 +[submodule "submodules/Luna"] + path = submodules/Luna + url = https://github.com/Ottermandias/Luna.git branch = main diff --git a/CustomizePlus.GameData/CustomizePlus.GameData.csproj b/CustomizePlus.GameData/CustomizePlus.GameData.csproj index 32629102..55ef5ce1 100644 --- a/CustomizePlus.GameData/CustomizePlus.GameData.csproj +++ b/CustomizePlus.GameData/CustomizePlus.GameData.csproj @@ -1,10 +1,11 @@ - + enable + diff --git a/CustomizePlus.GameData/Data/ReverseNameDicts.cs b/CustomizePlus.GameData/Data/ReverseNameDicts.cs index 22d0fb15..dfd8cccc 100644 --- a/CustomizePlus.GameData/Data/ReverseNameDicts.cs +++ b/CustomizePlus.GameData/Data/ReverseNameDicts.cs @@ -1,6 +1,6 @@ using CustomizePlus.GameData.ReverseSearchDictionaries; using Dalamud.Game.ClientState.Objects.Enums; -using OtterGui.Services; +using Luna; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -52,7 +52,7 @@ public bool TryGetID(ObjectKind kind, string name, [NotNullWhen(true)] out uint npcId = default; return kind switch { - ObjectKind.MountType => Mounts.TryGetValue(name, out npcId), + ObjectKind.Mount => Mounts.TryGetValue(name, out npcId), ObjectKind.Companion => Companions.TryGetValue(name, out npcId), ObjectKind.BattleNpc => BNpcs.TryGetValue(name, out npcId), ObjectKind.EventNpc => ENpcs.TryGetValue(name, out npcId), diff --git a/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs b/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs index f92cdf1b..ebadfd78 100644 --- a/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs +++ b/CustomizePlus.GameData/Extensions/ActorIdentifierExtensions.cs @@ -101,7 +101,7 @@ public static string TypeToString(this ActorIdentifier identifier) _ => " (Retainer)", }}", IdentifierType.Owned => $" ({identifier.Kind switch { - ObjectKind.MountType => "Mount", + ObjectKind.Mount => "Mount", ObjectKind.Companion => "Companion", ObjectKind.Ornament => "Accessory", _ => $"Owned {identifier.Kind}", @@ -130,7 +130,7 @@ public static bool IsAllowedForProfiles(this ActorIdentifier identifier) case IdentifierType.Owned: return identifier.Kind == ObjectKind.BattleNpc || - //identifier.Kind == ObjectKind.MountType || + //identifier.Kind == ObjectKind.Mount || identifier.Kind == ObjectKind.Companion || identifier.Kind == ObjectKind.Ornament; default: diff --git a/CustomizePlus.GameData/GlobalUsings.cs b/CustomizePlus.GameData/GlobalUsings.cs new file mode 100644 index 00000000..6181a117 --- /dev/null +++ b/CustomizePlus.GameData/GlobalUsings.cs @@ -0,0 +1 @@ +global using Logger = Luna.MainLogger; diff --git a/CustomizePlus.GameData/Hooks/Objects/CharacterDestructor.cs b/CustomizePlus.GameData/Hooks/Objects/CharacterDestructor.cs index 0d2b0e70..01db8676 100644 --- a/CustomizePlus.GameData/Hooks/Objects/CharacterDestructor.cs +++ b/CustomizePlus.GameData/Hooks/Objects/CharacterDestructor.cs @@ -1,35 +1,32 @@ -using Dalamud.Hooking; +using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Classes; -using OtterGui.Services; +using Luna; using Penumbra.GameData; +using Penumbra.GameData.Interop; namespace CustomizePlus.GameData.Hooks.Objects; -public sealed unsafe class CharacterDestructor : EventWrapperPtr, IHookService + +public sealed unsafe class CharacterDestructor : EventBase, IHookService { public enum Priority { - /// CutsceneService = 0, - - /// - IdentifiedCollectionCache = 0, } - public CharacterDestructor(HookManager hooks) - : base("Character Destructor") + public CharacterDestructor(LunaLogger log, HookManager hooks) + : base("Character Destructor", log) => _task = hooks.CreateHook(Name, Sigs.CharacterDestructor, Detour, true); - private readonly Task> _task; + private readonly Task?> _task; public nint Address - => _task.Result.Address; + => _task.Result?.Address ?? nint.Zero; public void Enable() - => _task.Result.Enable(); + => _task.Result?.Enable(); public void Disable() - => _task.Result.Disable(); + => _task.Result?.Disable(); public Task Awaiter => _task; @@ -41,8 +38,12 @@ public bool Finished private void Detour(Character* character) { - //Penumbra.Log.Verbose($"[{Name}] Triggered with 0x{(nint)character:X}."); - Invoke(character); - _task.Result.Original(character); + MainLogger.GlobalPluginLogger.Verbose($"[{Name}] Triggered with 0x{(nint)character:X}."); + Invoke(new Arguments(character)); + _task.Result!.Original(character); } + + /// The arguments for a character destructor event. + /// The game object that is being destroyed. + public readonly record struct Arguments(Actor Character); } \ No newline at end of file diff --git a/CustomizePlus.GameData/Hooks/Objects/ConstructCutsceneCharacter.cs b/CustomizePlus.GameData/Hooks/Objects/ConstructCutsceneCharacter.cs new file mode 100644 index 00000000..d7e73185 --- /dev/null +++ b/CustomizePlus.GameData/Hooks/Objects/ConstructCutsceneCharacter.cs @@ -0,0 +1,72 @@ +using CustomizePlus.GameData.Interop; +using Dalamud.Hooking; +using Luna; +using Penumbra.GameData; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Interop; + +namespace CustomizePlus.GameData.Hooks.Objects; + +public sealed unsafe class ConstructCutsceneCharacter : EventBase, IHookService +{ + private readonly GameState _gameState; + private readonly ObjectManager _objects; + + public enum Priority + { + CutsceneService = 0, + } + + public ConstructCutsceneCharacter(LunaLogger log, GameState gameState, HookManager hooks, ObjectManager objects) + : base("ConstructCutsceneCharacter", log) + { + _gameState = gameState; + _objects = objects; + _task = hooks.CreateHook(Name, Sigs.ConstructCutsceneCharacter, Detour, true); + } + + private readonly Task?> _task; + + public delegate int Delegate(SetupPlayerNpc.SchedulerStruct* scheduler); + + public int Detour(SetupPlayerNpc.SchedulerStruct* scheduler) + { + // This is the function that actually creates the new game object + // and fills it into the object table at a free index etc. + var ret = _task.Result!.Original(scheduler); + // Check for the copy state from SetupPlayerNpc. + if (_gameState.CharacterAssociated.Value) + { + // If the newly created character exists, invoke the event. + var character = _objects[ret + (int)ScreenActor.CutsceneStart].AsCharacter; + if (character is not null) + { + Invoke(new Arguments(character)); + MainLogger.GlobalPluginLogger.Verbose( + $"[{Name}] Created indirect copy of player character at 0x{(nint)character}, index {character->ObjectIndex}."); + } + _gameState.CharacterAssociated.Value = false; + } + + return ret; + } + + public nint Address + => _task.Result?.Address ?? nint.Zero; + + public void Enable() + => _task.Result?.Enable(); + + public void Disable() + => _task.Result?.Disable(); + + public Task Awaiter + => _task; + + public bool Finished + => _task.IsCompletedSuccessfully; + + /// The arguments for a construct cutscene character event. + /// The game object that is being destroyed. + public readonly record struct Arguments(Actor Character); +} \ No newline at end of file diff --git a/CustomizePlus.GameData/Hooks/Objects/CopyCharacter.cs b/CustomizePlus.GameData/Hooks/Objects/CopyCharacter.cs index 6b11f925..c458bd54 100644 --- a/CustomizePlus.GameData/Hooks/Objects/CopyCharacter.cs +++ b/CustomizePlus.GameData/Hooks/Objects/CopyCharacter.cs @@ -1,31 +1,31 @@ -using Dalamud.Hooking; +using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Classes; -using OtterGui.Services; +using Luna; +using Penumbra.GameData.Interop; namespace CustomizePlus.GameData.Hooks.Objects; -public sealed unsafe class CopyCharacter : EventWrapperPtr, IHookService + +public sealed unsafe class CopyCharacter : EventBase, IHookService { public enum Priority { - /// CutsceneService = 0, } - public CopyCharacter(HookManager hooks) - : base("Copy Character") + public CopyCharacter(LunaLogger log, HookManager hooks) + : base("Copy Character", log) => _task = hooks.CreateHook(Name, Address, Detour, true); - private readonly Task> _task; + private readonly Task?> _task; public nint Address => (nint)CharacterSetupContainer.MemberFunctionPointers.CopyFromCharacter; public void Enable() - => _task.Result.Enable(); + => _task.Result?.Enable(); public void Disable() - => _task.Result.Disable(); + => _task.Result?.Disable(); public Task Awaiter => _task; @@ -37,10 +37,14 @@ public bool Finished private ulong Detour(CharacterSetupContainer* target, Character* source, uint unk) { - // TODO: update when CS updated. - var character = ((Character**)target)[1]; - //Penumbra.Log.Verbose($"[{Name}] Triggered with target: 0x{(nint)target:X}, source : 0x{(nint)source:X} unk: {unk}."); - Invoke(character, source); - return _task.Result.Original(target, source, unk); + var character = target->OwnerObject; + Logger.GlobalPluginLogger.Verbose($"[{Name}] Triggered with target: 0x{(nint)target:X}, source : 0x{(nint)source:X} unk: {unk}."); + Invoke(new Arguments(character, source)); + return _task.Result!.Original(target, source, unk); } + + /// The arguments for a copy character event. + /// The character that is being created by a copy. + /// The character that is being copied. + public readonly record struct Arguments(Actor TargetCharacter, Actor SourceCharacter); } \ No newline at end of file diff --git a/CustomizePlus.GameData/Hooks/Objects/SetupPlayerNpc.cs b/CustomizePlus.GameData/Hooks/Objects/SetupPlayerNpc.cs new file mode 100644 index 00000000..b5ee0b98 --- /dev/null +++ b/CustomizePlus.GameData/Hooks/Objects/SetupPlayerNpc.cs @@ -0,0 +1,56 @@ +using CustomizePlus.GameData.Interop; +using FFXIVClientStructs.FFXIV.Client.Game.Character; +using Luna; +using Penumbra.GameData; +using System.Runtime.InteropServices; + +namespace CustomizePlus.GameData.Hooks.Objects; + +public sealed unsafe class SetupPlayerNpc : FastHook +{ + private readonly GameState _gameState; + + public SetupPlayerNpc(GameState gameState, HookManager hooks) + { + _gameState = gameState; + Task = hooks.CreateHook("SetupPlayerNPC", Sigs.SetupPlayerNpc, Detour, true); + } + + public delegate SchedulerStruct* Delegate(byte* npcType, nint unk, NpcSetupData* setupData); + + public SchedulerStruct* Detour(byte* npcType, nint unk, NpcSetupData* setupData) + { + // This function actually seems to generate all NPC. + + // If an ENPC is being created, check the creation parameters. + // If CopyPlayerCustomize is true, the event NPC gets a timeline that copies its customize and glasses from the local player. + // Keep track of this, so we can associate the actor to be created for this with the player character, see ConstructCutsceneCharacter. + if (setupData->CopyPlayerCustomize && npcType != null && *npcType is 8) + _gameState.CharacterAssociated.Value = true; + + var ret = Task.Result!.Original.Invoke(npcType, unk, setupData); + MainLogger.GlobalPluginLogger.Verbose( + $"[Setup Player NPC] Invoked for type {*npcType} with 0x{unk:X} and Copy Player Customize: {setupData->CopyPlayerCustomize}."); + return ret; + } + + [StructLayout(LayoutKind.Explicit)] + public struct NpcSetupData + { + [FieldOffset(0x0B)] + private byte _copyPlayerCustomize; + + public bool CopyPlayerCustomize + { + get => _copyPlayerCustomize != 0; + set => _copyPlayerCustomize = value ? (byte)1 : (byte)0; + } + } + + [StructLayout(LayoutKind.Explicit)] + public struct SchedulerStruct + { + public static Character* GetCharacter(SchedulerStruct* s) + => ((delegate* unmanaged**)s)[0][19](s); + } +} \ No newline at end of file diff --git a/CustomizePlus.GameData/Interop/GameState.cs b/CustomizePlus.GameData/Interop/GameState.cs new file mode 100644 index 00000000..df1c0365 --- /dev/null +++ b/CustomizePlus.GameData/Interop/GameState.cs @@ -0,0 +1,6 @@ +namespace CustomizePlus.GameData.Interop; + +public class GameState : Luna.IService +{ + public readonly ThreadLocal CharacterAssociated = new(() => false); +} diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/Bases/ReverseNameDictionary.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/Bases/ReverseNameDictionary.cs index 96fe1149..5513348f 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/Bases/ReverseNameDictionary.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/Bases/ReverseNameDictionary.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using Penumbra.GameData.Data; using Penumbra.GameData.DataContainers.Bases; using Penumbra.GameData.Structs; diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictBNpc.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictBNpc.cs index 593cf475..74eb8820 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictBNpc.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictBNpc.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using CustomizePlus.GameData.ReverseSearchDictionaries.Bases; diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictCompanion.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictCompanion.cs index 53b5ea41..a65326b8 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictCompanion.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictCompanion.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using CustomizePlus.GameData.ReverseSearchDictionaries.Bases; diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictENpc.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictENpc.cs index 65c89ce0..72fd2781 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictENpc.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictENpc.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using CustomizePlus.GameData.ReverseSearchDictionaries.Bases; diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictMount.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictMount.cs index 637e1851..7d1510bd 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictMount.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictMount.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using Penumbra.GameData.Data; using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; diff --git a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictOrnament.cs b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictOrnament.cs index c227e39c..e71a0123 100644 --- a/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictOrnament.cs +++ b/CustomizePlus.GameData/ReverseSearchDictionaries/ReverseSearchDictOrnament.cs @@ -1,6 +1,5 @@ using Dalamud.Plugin.Services; using Dalamud.Plugin; -using OtterGui.Log; using Penumbra.GameData.Data; using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; diff --git a/CustomizePlus.GameData/Services/CutsceneService.cs b/CustomizePlus.GameData/Services/CutsceneService.cs index 3230a180..3e74e827 100644 --- a/CustomizePlus.GameData/Services/CutsceneService.cs +++ b/CustomizePlus.GameData/Services/CutsceneService.cs @@ -2,7 +2,7 @@ using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.Game.Character; -using OtterGui.Services; +using Luna; using Penumbra.GameData.Enums; using Penumbra.GameData.Interop; using Penumbra.String; @@ -10,7 +10,7 @@ namespace CustomizePlus.GameData.Services; -public sealed class CutsceneService : IRequiredService, IDisposable +public sealed class CutsceneService : Luna.IRequiredService, IDisposable { public const int CutsceneStartIdx = (int)ScreenActor.CutsceneStart; public const int CutsceneEndIdx = (int)ScreenActor.CutsceneEnd; @@ -19,6 +19,7 @@ public sealed class CutsceneService : IRequiredService, IDisposable private readonly ObjectManager _objects; private readonly CopyCharacter _copyCharacter; private readonly CharacterDestructor _characterDestructor; + private readonly ConstructCutsceneCharacter _constructCutsceneCharacter; private readonly short[] _copiedCharacters = Enumerable.Repeat((short)-1, CutsceneSlots).ToArray(); public IEnumerable> Actors @@ -26,14 +27,16 @@ public IEnumerable> Actors .Where(i => _objects[i].Valid) .Select(i => KeyValuePair.Create(i, this[i] ?? _objects.GetDalamudObject(i)!)); - public unsafe CutsceneService(ObjectManager objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor, - IClientState clientState) + public CutsceneService(ObjectManager objects, CopyCharacter copyCharacter, CharacterDestructor characterDestructor, + ConstructCutsceneCharacter constructCutsceneCharacter, IClientState clientState) { _objects = objects; _copyCharacter = copyCharacter; _characterDestructor = characterDestructor; + _constructCutsceneCharacter = constructCutsceneCharacter; _copyCharacter.Subscribe(OnCharacterCopy, CopyCharacter.Priority.CutsceneService); _characterDestructor.Subscribe(OnCharacterDestructor, CharacterDestructor.Priority.CutsceneService); + _constructCutsceneCharacter.Subscribe(OnSetupPlayerNpc, ConstructCutsceneCharacter.Priority.CutsceneService); if (clientState.IsGPosing) RecoverGPoseActors(); } @@ -73,6 +76,7 @@ public bool SetParentIndex(int copyIdx, int parentIdx) return false; _copiedCharacters[copyIdx - CutsceneStartIdx] = (short)parentIdx; + _objects.InvokeRequiredUpdates(); return true; } @@ -84,14 +88,16 @@ public short GetParentIndex(ushort idx) return -1; } - public unsafe void Dispose() + public void Dispose() { _copyCharacter.Unsubscribe(OnCharacterCopy); _characterDestructor.Unsubscribe(OnCharacterDestructor); + _constructCutsceneCharacter.Unsubscribe(OnSetupPlayerNpc); } - private unsafe void OnCharacterDestructor(Character* character) + private unsafe void OnCharacterDestructor(in CharacterDestructor.Arguments arguments) { + var character = arguments.Character.AsCharacter; if (character->GameObject.ObjectIndex < CutsceneStartIdx) { // Remove all associations for now non-existing actor. @@ -116,13 +122,22 @@ private unsafe void OnCharacterDestructor(Character* character) } } - private unsafe void OnCharacterCopy(Character* target, Character* source) + private void OnCharacterCopy(in CopyCharacter.Arguments arguments) { - if (target == null || target->GameObject.ObjectIndex is < CutsceneStartIdx or >= CutsceneEndIdx) + if (!arguments.TargetCharacter.Valid || arguments.TargetCharacter.Index.Index is < CutsceneStartIdx or >= CutsceneEndIdx) return; - var idx = target->GameObject.ObjectIndex - CutsceneStartIdx; - _copiedCharacters[idx] = (short)(source != null ? source->GameObject.ObjectIndex : -1); + var idx = arguments.TargetCharacter.Index.Index - CutsceneStartIdx; + _copiedCharacters[idx] = (short)(arguments.SourceCharacter.Valid ? arguments.SourceCharacter.Index : -1); + } + + private void OnSetupPlayerNpc(in ConstructCutsceneCharacter.Arguments arguments) + { + if (!arguments.Character.Valid || arguments.Character.Index.Index is < CutsceneStartIdx or >= CutsceneEndIdx) + return; + + var idx = arguments.Character.Index.Index - CutsceneStartIdx; + _copiedCharacters[idx] = 0; } /// Try to recover GPose actors on reloads into a running game. diff --git a/CustomizePlus.GameData/Services/GameEventManager.cs b/CustomizePlus.GameData/Services/GameEventManager.cs deleted file mode 100644 index 898facf8..00000000 --- a/CustomizePlus.GameData/Services/GameEventManager.cs +++ /dev/null @@ -1,187 +0,0 @@ -using Dalamud.Hooking; -using Dalamud.Plugin.Services; -using Dalamud.Utility.Signatures; -using FFXIVClientStructs.FFXIV.Client.Game.Character; -using FFXIVClientStructs.FFXIV.Client.Game.Object; -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using Penumbra.GameData; - -namespace CustomizePlus.GameData.Services; - -public unsafe class GameEventManager : IDisposable -{ - private const string Prefix = $"[{nameof(GameEventManager)}]"; - - public event CharacterDestructorEvent? CharacterDestructor; - public event CopyCharacterEvent? CopyCharacter; - public event CreatingCharacterBaseEvent? CreatingCharacterBase; - public event CharacterBaseCreatedEvent? CharacterBaseCreated; - public event CharacterBaseDestructorEvent? CharacterBaseDestructor; - - public GameEventManager(IGameInteropProvider interop) - { - interop.InitializeFromAttributes(this); - - _copyCharacterHook = - interop.HookFromAddress((nint)CharacterSetupContainer.MemberFunctionPointers.CopyFromCharacter, CopyCharacterDetour); - _characterBaseCreateHook = - interop.HookFromAddress((nint)CharacterBase.MemberFunctionPointers.Create, CharacterBaseCreateDetour); - _characterBaseDestructorHook = - interop.HookFromAddress((nint)CharacterBase.MemberFunctionPointers.Destroy, - CharacterBaseDestructorDetour); - _characterDtorHook.Enable(); - _copyCharacterHook.Enable(); - _characterBaseCreateHook.Enable(); - _characterBaseDestructorHook.Enable(); - } - - public void Dispose() - { - _characterDtorHook.Dispose(); - _copyCharacterHook.Dispose(); - _characterBaseCreateHook.Dispose(); - _characterBaseDestructorHook.Dispose(); - } - - #region Character Destructor - - private delegate void CharacterDestructorDelegate(Character* character); - - [Signature(Sigs.CharacterDestructor, DetourName = nameof(CharacterDestructorDetour))] - private readonly Hook _characterDtorHook = null!; - - private void CharacterDestructorDetour(Character* character) - { - if (CharacterDestructor != null) - foreach (var subscriber in CharacterDestructor.GetInvocationList()) - { - try - { - ((CharacterDestructorEvent)subscriber).Invoke(character); - } - catch (Exception ex) - { - //Penumbra.Log.Error($"{Prefix} Error in {nameof(CharacterDestructor)} event when executing {subscriber.Method.Name}:\n{ex}"); - //todo: log - } - } - - //Penumbra.Log.Verbose($"{Prefix} {nameof(CharacterDestructor)} triggered with 0x{(nint)character:X}."); - //todo: log - _characterDtorHook.Original(character); - } - - public delegate void CharacterDestructorEvent(Character* character); - - #endregion - - #region Copy Character - - private delegate ulong CopyCharacterDelegate(CharacterSetupContainer* target, GameObject* source, uint unk); - - private readonly Hook _copyCharacterHook; - - private ulong CopyCharacterDetour(CharacterSetupContainer* target, GameObject* source, uint unk) - { - // TODO: update when CS updated. - var character = ((Character**)target)[1]; - if (CopyCharacter != null) - foreach (var subscriber in CopyCharacter.GetInvocationList()) - { - try - { - ((CopyCharacterEvent)subscriber).Invoke(character, (Character*)source); - } - catch (Exception ex) - { - /*Penumbra.Log.Error( - $"{Prefix} Error in {nameof(CopyCharacter)} event when executing {subscriber.Method.Name}:\n{ex}");*/ - //todo: log - } - } - - /*Penumbra.Log.Verbose( - $"{Prefix} {nameof(CopyCharacter)} triggered with target 0x{(nint)target:X} and source 0x{(nint)source:X}.");*/ - //todo: log - return _copyCharacterHook.Original(target, source, unk); - } - - public delegate void CopyCharacterEvent(Character* target, Character* source); - - #endregion - - #region CharacterBaseCreate - - private delegate nint CharacterBaseCreateDelegate(uint a, nint b, nint c, byte d); - - private readonly Hook _characterBaseCreateHook; - - private nint CharacterBaseCreateDetour(uint a, nint b, nint c, byte d) - { - if (CreatingCharacterBase != null) - foreach (var subscriber in CreatingCharacterBase.GetInvocationList()) - { - try - { - ((CreatingCharacterBaseEvent)subscriber).Invoke((nint)(&a), b, c); - } - catch (Exception ex) - { - /*Penumbra.Log.Error( - $"{Prefix} Error in {nameof(CharacterBaseCreateDetour)} event when executing {subscriber.Method.Name}:\n{ex}");*/ - //todo: log - } - } - - var ret = _characterBaseCreateHook.Original(a, b, c, d); - if (CharacterBaseCreated != null) - foreach (var subscriber in CharacterBaseCreated.GetInvocationList()) - { - try - { - ((CharacterBaseCreatedEvent)subscriber).Invoke(a, b, c, ret); - } - catch (Exception ex) - { - /*Penumbra.Log.Error( - $"{Prefix} Error in {nameof(CharacterBaseCreateDetour)} event when executing {subscriber.Method.Name}:\n{ex}");*/ - //todo: log - } - } - - return ret; - } - - public delegate void CreatingCharacterBaseEvent(nint modelCharaId, nint customize, nint equipment); - public delegate void CharacterBaseCreatedEvent(uint modelCharaId, nint customize, nint equipment, nint drawObject); - - #endregion - - #region CharacterBase Destructor - - public delegate void CharacterBaseDestructorEvent(nint drawBase); - - private readonly Hook _characterBaseDestructorHook; - - private void CharacterBaseDestructorDetour(nint drawBase) - { - if (CharacterBaseDestructor != null) - foreach (var subscriber in CharacterBaseDestructor.GetInvocationList()) - { - try - { - ((CharacterBaseDestructorEvent)subscriber).Invoke(drawBase); - } - catch (Exception ex) - { - /*Penumbra.Log.Error( - $"{Prefix} Error in {nameof(CharacterBaseDestructorDetour)} event when executing {subscriber.Method.Name}:\n{ex}");*/ - //todo: log - } - } - - _characterBaseDestructorHook.Original.Invoke(drawBase); - } - - #endregion -} diff --git a/CustomizePlus.GameData/packages.lock.json b/CustomizePlus.GameData/packages.lock.json index 255b2bab..d70f591c 100644 --- a/CustomizePlus.GameData/packages.lock.json +++ b/CustomizePlus.GameData/packages.lock.json @@ -20,27 +20,65 @@ }, "JetBrains.Annotations": { "type": "Transitive", - "resolved": "2024.3.0", - "contentHash": "ox5pkeLQXjvJdyAB4b2sBYAlqZGLh3PjSnP1bQNVx72ONuTJ9+34/+Rq91Fc0dG29XG9RgZur9+NcP4riihTug==" + "resolved": "2024.2.0", + "contentHash": "GNnqCFW/163p1fOehKx0CnAqjmpPrUSqrgfHM6qca+P+RN39C9rhlfZHQpJhxmQG/dkOYe/b3Z0P8b6Kv5m1qw==" }, "Microsoft.Extensions.DependencyInjection": { "type": "Transitive", - "resolved": "9.0.2", - "contentHash": "ZffbJrskOZ40JTzcTyKwFHS5eACSWp2bUQBBApIgGV+es8RaTD4OxUG7XxFr3RIPLXtYQ1jQzF2DjKB5fZn7Qg==", + "resolved": "9.0.0", + "contentHash": "MCPrg7v3QgNMr0vX4vzRXvkNGgLg8vKWX0nKCWUxu2uPyMsaRgiRc1tHBnbTcfJMhMKj2slE/j2M9oGkd25DNw==", "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.2" + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0" } }, "Microsoft.Extensions.DependencyInjection.Abstractions": { "type": "Transitive", - "resolved": "9.0.2", - "contentHash": "MNe7GSTBf3jQx5vYrXF0NZvn6l7hUKF6J54ENfAgCO8y6xjN1XUmKKWG464LP2ye6QqDiA1dkaWEZBYnhoZzjg==" + "resolved": "9.0.0", + "contentHash": "+6f2qv2a3dLwd5w6JanPIPs47CxRbnk+ZocMJUhv9NxP88VlOcJYZs9jY+MYSjxvady08bUZn6qgiNh7DadGgg==" }, - "ottergui": { + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "crjWyORoug0kK7RSNJBTeSE6VX8IQgLf3nUpTB9m62bPXp/tzbnOsnbe8TXEG0AASNaKZddnpHKw7fET8E++Pg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.0", + "Microsoft.Extensions.Logging.Abstractions": "9.0.0", + "Microsoft.Extensions.Options": "9.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "g0UfujELzlLbHoVG8kPKVBaW470Ewi+jnptGS9KUi6jcb+k2StujtK3m26DFSGGwQ/+bVgZfsWqNzlP6YOejvw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "y2146b3jrPI3Q0lokKXdKLpmXqakYbDIPDV6r3M8SqvSf45WwOTzkyfDpxnZXJsJQEpAsAqjUq5Pu8RCJMjubg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.0", + "Microsoft.Extensions.Primitives": "9.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "N3qEBzmLMYiASUlKxxFIISP4AiwuPTHF5uCh+2CWSwwzAJiIYx0kBJsS30cp1nvhSySFAVi30jecD307jV+8Kg==" + }, + "System.IO.Hashing": { + "type": "Transitive", + "resolved": "9.0.8", + "contentHash": "5TJUS9EIYrp0VEcm06EPYxXmLmsVUakewFnM/CAxQfvlasI9fGkTKM9afSf2dodZcMCzFna/o7Fn+gYRt3uTiA==" + }, + "luna": { "type": "Project", "dependencies": { - "JetBrains.Annotations": "[2024.3.0, )", - "Microsoft.Extensions.DependencyInjection": "[9.0.2, )" + "JetBrains.Annotations": "[2024.2.0, )", + "Microsoft.Extensions.Logging": "[9.0.0, )", + "System.IO.Hashing": "[9.0.8, )" } }, "penumbra.api": { @@ -51,9 +89,9 @@ "dependencies": { "FlatSharp.Compiler": "[7.9.0, )", "FlatSharp.Runtime": "[7.9.0, )", - "OtterGui": "[1.0.0, )", - "Penumbra.Api": "[5.12.0, )", - "Penumbra.String": "[1.0.6, )" + "Luna": "[1.0.0, )", + "Penumbra.Api": "[5.15.1, )", + "Penumbra.String": "[1.0.7, )" } }, "penumbra.string": { diff --git a/CustomizePlus.sln b/CustomizePlus.sln index 73c69250..f5f26861 100644 --- a/CustomizePlus.sln +++ b/CustomizePlus.sln @@ -12,8 +12,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomizePlus", "CustomizeP EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "submodules", "submodules", "{121C2200-A844-44FD-85C4-22D6C7E35553}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtterGui", "submodules\OtterGui\OtterGui.csproj", "{0D465539-6133-4088-B4BB-F260FA2A1557}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomizePlus.GameData", "CustomizePlus.GameData\CustomizePlus.GameData.csproj", "{CDB26C94-1200-45AA-AF96-D4526DC76AD5}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "submodules\Penumbra.GameData\Penumbra.GameData.csproj", "{D79C8833-D241-4867-BF6F-8097E0ED8067}" @@ -24,65 +22,181 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.String", "submodul EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ECommonsLite", "submodules\ECommonsLite\ECommonsLite\ECommonsLite.csproj", "{41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Luna", "Luna", "{1DCA913C-5266-F6A9-9DF7-A9D612F05C20}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luna", "submodules\Luna\Luna\Luna.csproj", "{DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luna.Generators", "submodules\Luna\Luna.Generators\Luna.Generators.csproj", "{5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 Release|x64 = Release|x64 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 ReleaseValidate|x64 = ReleaseValidate|x64 + ReleaseValidate|Any CPU = ReleaseValidate|Any CPU + ReleaseValidate|x86 = ReleaseValidate|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|x64.ActiveCfg = Debug|x64 {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|x64.Build.0 = Debug|x64 + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|x86.ActiveCfg = Debug|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Debug|x86.Build.0 = Debug|Any CPU {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|x64.ActiveCfg = Release|x64 {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|x64.Build.0 = Release|x64 + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|Any CPU.Build.0 = Release|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|x86.ActiveCfg = Release|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.Release|x86.Build.0 = Release|Any CPU {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|x64.ActiveCfg = Release|x64 {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|x64.Build.0 = Release|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.Debug|x64.ActiveCfg = Debug|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.Debug|x64.Build.0 = Debug|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.Release|x64.ActiveCfg = Release|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.Release|x64.Build.0 = Release|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.ReleaseValidate|x64.ActiveCfg = Release|x64 - {0D465539-6133-4088-B4BB-F260FA2A1557}.ReleaseValidate|x64.Build.0 = Release|x64 + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {5BA385F5-C17E-4CE4-828A-24F7F19C434B}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|x64.ActiveCfg = Debug|x64 {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|x64.Build.0 = Debug|x64 + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|x86.ActiveCfg = Debug|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Debug|x86.Build.0 = Debug|Any CPU {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|x64.ActiveCfg = Release|x64 {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|x64.Build.0 = Release|x64 + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|Any CPU.Build.0 = Release|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|x86.ActiveCfg = Release|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.Release|x86.Build.0 = Release|Any CPU {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|x64.ActiveCfg = Release|x64 {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|x64.Build.0 = Release|x64 + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {CDB26C94-1200-45AA-AF96-D4526DC76AD5}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|x64.ActiveCfg = Debug|x64 {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|x64.Build.0 = Debug|x64 + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|x86.ActiveCfg = Debug|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Debug|x86.Build.0 = Debug|Any CPU {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|x64.ActiveCfg = Release|x64 {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|x64.Build.0 = Release|x64 + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|Any CPU.Build.0 = Release|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|x86.ActiveCfg = Release|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.Release|x86.Build.0 = Release|Any CPU {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|x64.ActiveCfg = Release|x64 {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|x64.Build.0 = Release|x64 + {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {D79C8833-D241-4867-BF6F-8097E0ED8067}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|x64.ActiveCfg = Debug|x64 {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|x64.Build.0 = Debug|x64 + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|x86.ActiveCfg = Debug|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Debug|x86.Build.0 = Debug|Any CPU {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|x64.ActiveCfg = Release|x64 {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|x64.Build.0 = Release|x64 + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|Any CPU.Build.0 = Release|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|x86.ActiveCfg = Release|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.Release|x86.Build.0 = Release|Any CPU {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|x64.ActiveCfg = Release|x64 {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|x64.Build.0 = Release|x64 + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {CC460943-1E07-4FA0-8B8C-67F0EF385290}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|x64.ActiveCfg = Debug|x64 {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|x64.Build.0 = Debug|x64 + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|x86.ActiveCfg = Debug|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Debug|x86.Build.0 = Debug|Any CPU {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|x64.ActiveCfg = Release|x64 {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|x64.Build.0 = Release|x64 + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|Any CPU.Build.0 = Release|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|x86.ActiveCfg = Release|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.Release|x86.Build.0 = Release|Any CPU {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|x64.ActiveCfg = Release|x64 {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|x64.Build.0 = Release|x64 + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|x64.ActiveCfg = Debug|x64 {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|x64.Build.0 = Debug|x64 + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|x86.ActiveCfg = Debug|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Debug|x86.Build.0 = Debug|Any CPU {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|x64.ActiveCfg = Release|x64 {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|x64.Build.0 = Release|x64 + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|Any CPU.Build.0 = Release|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|x86.ActiveCfg = Release|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.Release|x86.Build.0 = Release|Any CPU {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|x64.ActiveCfg = Release|x64 {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|x64.Build.0 = Release|x64 + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|Any CPU.ActiveCfg = ReleaseValidate|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|Any CPU.Build.0 = ReleaseValidate|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|x86.ActiveCfg = ReleaseValidate|Any CPU + {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C}.ReleaseValidate|x86.Build.0 = ReleaseValidate|Any CPU + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|x64.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|x64.Build.0 = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|Any CPU.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|Any CPU.Build.0 = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|x86.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Debug|x86.Build.0 = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|x64.ActiveCfg = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|x64.Build.0 = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|Any CPU.ActiveCfg = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|Any CPU.Build.0 = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|x86.ActiveCfg = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.Release|x86.Build.0 = Release|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|x64.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|x64.Build.0 = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|Any CPU.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|Any CPU.Build.0 = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|x86.ActiveCfg = Debug|x64 + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A}.ReleaseValidate|x86.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|x64.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|x64.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|Any CPU.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|Any CPU.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|x86.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Debug|x86.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|x64.ActiveCfg = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|x64.Build.0 = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|Any CPU.ActiveCfg = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|Any CPU.Build.0 = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|x86.ActiveCfg = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.Release|x86.Build.0 = Release|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|x64.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|x64.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|Any CPU.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|Any CPU.Build.0 = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|x86.ActiveCfg = Debug|x64 + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA}.ReleaseValidate|x86.Build.0 = Debug|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {0D465539-6133-4088-B4BB-F260FA2A1557} = {121C2200-A844-44FD-85C4-22D6C7E35553} {D79C8833-D241-4867-BF6F-8097E0ED8067} = {121C2200-A844-44FD-85C4-22D6C7E35553} {CC460943-1E07-4FA0-8B8C-67F0EF385290} = {121C2200-A844-44FD-85C4-22D6C7E35553} {CB1DFB63-22D9-4E90-A8C1-A4F7CFEF7823} = {121C2200-A844-44FD-85C4-22D6C7E35553} {41F4BB08-FCFD-420F-AD18-ED9D7FB3251C} = {121C2200-A844-44FD-85C4-22D6C7E35553} + {1DCA913C-5266-F6A9-9DF7-A9D612F05C20} = {121C2200-A844-44FD-85C4-22D6C7E35553} + {DFF4DE5D-924D-4602-A723-CBAF05BFCD2A} = {1DCA913C-5266-F6A9-9DF7-A9D612F05C20} + {5FDDD49F-C9E6-45A7-A922-78208B2A5ECA} = {1DCA913C-5266-F6A9-9DF7-A9D612F05C20} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B17E85B1-5F60-4440-9F9A-3DDE877E8CDF} diff --git a/CustomizePlus/Anamnesis/Data/PoseFile.cs b/CustomizePlus/Anamnesis/Data/PoseFile.cs index c02b5c22..021a0dd8 100644 --- a/CustomizePlus/Anamnesis/Data/PoseFile.cs +++ b/CustomizePlus/Anamnesis/Data/PoseFile.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using Newtonsoft.Json; using System.Globalization; -using Newtonsoft.Json; namespace CustomizePlus.Anamnesis.Data; diff --git a/CustomizePlus/Anamnesis/PoseFileBoneLoader.cs b/CustomizePlus/Anamnesis/PoseFileBoneLoader.cs index d391a064..e146fb04 100644 --- a/CustomizePlus/Anamnesis/PoseFileBoneLoader.cs +++ b/CustomizePlus/Anamnesis/PoseFileBoneLoader.cs @@ -2,10 +2,6 @@ using CustomizePlus.Core.Data; using CustomizePlus.Core.Extensions; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Numerics; namespace CustomizePlus.Anamnesis; diff --git a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs index f29bbf68..29644b7d 100644 --- a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs +++ b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs @@ -1,13 +1,11 @@ -using CustomizePlus.Api.Data; +using CustomizePlus.Api.Data; using CustomizePlus.Api.Enums; -using CustomizePlus.Armatures.Data; using CustomizePlus.Armatures.Events; using CustomizePlus.Core.Extensions; using CustomizePlus.GameData.Extensions; using CustomizePlus.Profiles.Data; using CustomizePlus.Profiles.Enums; using CustomizePlus.Profiles.Exceptions; -using CustomizePlus.Templates.Data; using CustomizePlus.Templates.Events; using Dalamud.Game.ClientState.Objects.Types; using ECommonsLite.EzIpcManager; @@ -16,9 +14,6 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.String; -using System; -using System.Collections.Generic; -using System.Linq; namespace CustomizePlus.Api; @@ -48,7 +43,7 @@ private IList GetProfileList() .Where(x => x.ProfileType == ProfileType.Normal) .Select(x => { - string path = _profileFileSystem.TryGetValue(x, out var leaf) ? leaf.FullName() : x.Name.Text; + var path = x.Node?.FullPath ?? x.Name; var charactersList = new List(x.Characters.Count); foreach (var character in x.Characters) @@ -62,7 +57,7 @@ private IList GetProfileList() charactersList.Add(tuple); } - return (x.UniqueId, x.Name.Text, path, charactersList, x.Priority, x.Enabled); + return (x.UniqueId, x.Name, path, charactersList, x.Priority, x.Enabled); }) .ToList(); } @@ -119,8 +114,8 @@ private int AddPlayerCharacterToProfile(Guid uniqueId, string name, ushort world var playerIdentifier = _actorManager.CreatePlayer(byteString, worldId); if (playerIdentifier == ActorIdentifier.Invalid) return (int)ErrorCode.InvalidCharacter; - - if(!_profileManager.AddCharacter(profile, playerIdentifier)) + + if (!_profileManager.AddCharacter(profile, playerIdentifier)) return (int)ErrorCode.InvalidArgument; //Returned if character is already associated with provided profile return (int)ErrorCode.Success; @@ -145,8 +140,8 @@ private int RemovePlayerCharacterToProfile(Guid uniqueId, string name, ushort wo var playerIdentifier = _actorManager.CreatePlayer(byteString, worldId); if (playerIdentifier == ActorIdentifier.Invalid) return (int)ErrorCode.InvalidCharacter; - - if(!_profileManager.DeleteCharacter(profile, playerIdentifier)) + + if (!_profileManager.DeleteCharacter(profile, playerIdentifier)) return (int)ErrorCode.InvalidArgument; //Returned if character is not associated with provided profile return (int)ErrorCode.Success; @@ -365,10 +360,10 @@ private int DeleteTemporaryProfileOnCharacter(ushort gameObjectIndex) _profileManager.RemoveTemporaryProfile(actor.Value); return (int)ErrorCode.Success; } - catch(ProfileException ex) + catch (ProfileException ex) { - switch(ex) - { + switch (ex) + { case ActorNotFoundException _: return (int)ErrorCode.InvalidCharacter; case ProfileNotFoundException: @@ -378,7 +373,7 @@ private int DeleteTemporaryProfileOnCharacter(ushort gameObjectIndex) return (int)ErrorCode.UnknownError; } } - catch(Exception ex) + catch (Exception ex) { _logger.Error($"Exception in DeleteTemporaryProfileOnCharacter. Character: {actor.Value.Utf8Name.ToString().Incognify()}. Exception: {ex}"); return (int)ErrorCode.UnknownError; @@ -420,8 +415,9 @@ private int DeleteTemporaryProfileByUniqueId(Guid uniqueId) } //Send profile update if any of the templates were changed in currently active profile - private void OnTemplateChanged(TemplateChanged.Type type, Template? template, object? arg3) + private void OnTemplateChanged(in TemplateChanged.Arguments args) { + var (type, template, arg3) = args; if (type != TemplateChanged.Type.EditorDisabled) return; @@ -449,8 +445,9 @@ private void OnTemplateChanged(TemplateChanged.Type type, Template? template, ob } //warn: intended limitation - ignores default profiles because why you would use default profile on your own character - private void OnArmatureChanged(ArmatureChanged.Type type, Armature armature, object? arg3) + private void OnArmatureChanged(in ArmatureChanged.Arguments args) { + var (type, armature, arg3) = args; if (armature.ActorIdentifier != _gameObjectService.GetCurrentPlayerActorIdentifier()) return; diff --git a/CustomizePlus/Api/CustomizePlusIpc.cs b/CustomizePlus/Api/CustomizePlusIpc.cs index 6cbc581b..db776e5c 100644 --- a/CustomizePlus/Api/CustomizePlusIpc.cs +++ b/CustomizePlus/Api/CustomizePlusIpc.cs @@ -6,8 +6,6 @@ using CustomizePlus.Templates.Events; using Dalamud.Plugin; using ECommonsLite.EzIpcManager; -using OtterGui.Log; -using System; using Penumbra.GameData.Actors; namespace CustomizePlus.Api; @@ -27,7 +25,6 @@ public partial class CustomizePlusIpc : IDisposable private readonly ProfileManager _profileManager; private readonly ActorManager _actorManager; private readonly GameObjectService _gameObjectService; - private readonly ProfileFileSystem _profileFileSystem; private readonly CutsceneService _cutsceneService; private readonly ArmatureChanged _armatureChangedEvent; @@ -45,7 +42,6 @@ public CustomizePlusIpc( ProfileManager profileManager, ActorManager actorManager, GameObjectService gameObjectService, - ProfileFileSystem profileFileSystem, CutsceneService cutsceneService, ArmatureChanged armatureChangedEvent, TemplateChanged templateChangedEvent) @@ -56,7 +52,6 @@ public CustomizePlusIpc( _profileManager = profileManager; _actorManager = actorManager; _gameObjectService = gameObjectService; - _profileFileSystem = profileFileSystem; _cutsceneService = cutsceneService; _armatureChangedEvent = armatureChangedEvent; diff --git a/CustomizePlus/Api/Data/IPCCharacterProfile.cs b/CustomizePlus/Api/Data/IPCCharacterProfile.cs index c8af98e2..eb571f6b 100644 --- a/CustomizePlus/Api/Data/IPCCharacterProfile.cs +++ b/CustomizePlus/Api/Data/IPCCharacterProfile.cs @@ -1,9 +1,6 @@ using CustomizePlus.Core.Data; using CustomizePlus.Profiles.Data; using CustomizePlus.Templates.Data; -using System; -using System.Collections.Generic; -using System.Numerics; namespace CustomizePlus.Api.Data; diff --git a/CustomizePlus/Armatures/Data/Armature.cs b/CustomizePlus/Armatures/Data/Armature.cs index ad83f88f..9a0d4cc7 100644 --- a/CustomizePlus/Armatures/Data/Armature.cs +++ b/CustomizePlus/Armatures/Data/Armature.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using Penumbra.GameData.Actors; -using CustomizePlus.Core.Data; +using CustomizePlus.Core.Data; +using CustomizePlus.GameData.Extensions; using CustomizePlus.Profiles.Data; using CustomizePlus.Templates.Data; -using CustomizePlus.GameData.Extensions; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using Penumbra.GameData.Actors; namespace CustomizePlus.Armatures.Data; @@ -151,7 +148,7 @@ public Armature(ActorIdentifier actorIdentifier, Profile profile) Profile.Armatures.Add(this); - Plugin.Logger.Debug($"Instantiated {this}, attached to {Profile}"); + CustomizePlus.Logger.Debug($"Instantiated {this}, attached to {Profile}"); } /// @@ -183,16 +180,16 @@ public bool IsSkeletonUpdated(CharacterBase* cBase) } //handle hair separately because different hairstyles can have the same amount of bones. - if(cBase->Skeleton->PartialSkeletonCount > 2) + if (cBase->Skeleton->PartialSkeletonCount > 2) { var newPose = cBase->Skeleton->PartialSkeletons[2].GetHavokPose(Constants.TruePoseIndex); - if(newPose != null) + if (newPose != null) { - if(newPose->Skeleton->Bones.Length != _partialSkeletons[2].Length) + if (newPose->Skeleton->Bones.Length != _partialSkeletons[2].Length) return true; - for(var i = 0; i < newPose->Skeleton->Bones.Length; i++) + for (var i = 0; i < newPose->Skeleton->Bones.Length; i++) { if (newPose->Skeleton->Bones[i].Name.String != _partialSkeletons[2][i].BoneName) return true; @@ -218,7 +215,7 @@ public void RebuildSkeleton(CharacterBase* cBase) RebuildBoneTemplateBinding(); //todo: intentionally not calling ArmatureChanged.Type.Updated because this is pending rewrite - Plugin.Logger.Debug($"Rebuilt {this}"); + CustomizePlus.Logger.Debug($"Rebuilt {this}"); } public BoneTransform? GetAppliedBoneTransform(string boneName) @@ -229,7 +226,7 @@ public void RebuildSkeleton(CharacterBase* cBase) if (template.Bones.TryGetValue(boneName, out var boneTransform)) return boneTransform; else - Plugin.Logger.Error($"Bone {boneName} is null in template {template.UniqueId}"); + CustomizePlus.Logger.Error($"Bone {boneName} is null in template {template.UniqueId}"); } return null; @@ -240,7 +237,7 @@ public void RebuildSkeleton(CharacterBase* cBase) /// public void UpdateLastSeen(DateTime? dateTime = null) { - if(dateTime == null) + if (dateTime == null) dateTime = DateTime.UtcNow; LastSeen = (DateTime)dateTime; @@ -270,7 +267,7 @@ private static unsafe List> ParseBonesFromObject(Armature arm, C { //time to build a new bone ModelBone newBone = new(arm, boneName, pSkeleIndex, boneIndex); - Plugin.Logger.Verbose($"Created new bone: {boneName} on {pSkeleIndex}->{boneIndex} arm: {arm._localId}"); + CustomizePlus.Logger.Verbose($"Created new bone: {boneName} on {pSkeleIndex}->{boneIndex} arm: {arm._localId}"); if (currentPose->Skeleton->ParentIndices[boneIndex] is short parentIndex && parentIndex >= 0) @@ -295,7 +292,7 @@ private static unsafe List> ParseBonesFromObject(Armature arm, C } else { - Plugin.Logger.Error($"Failed to process bone @ <{pSkeleIndex}, {boneIndex}> while parsing bones from {cBase->ToString()}"); + CustomizePlus.Logger.Error($"Failed to process bone @ <{pSkeleIndex}, {boneIndex}> while parsing bones from {cBase->ToString()}"); } } } @@ -304,7 +301,7 @@ private static unsafe List> ParseBonesFromObject(Armature arm, C } catch (Exception ex) { - Plugin.Logger.Error($"Error parsing armature skeleton from {cBase->ToString()}:\n\t{ex}"); + CustomizePlus.Logger.Error($"Error parsing armature skeleton from {cBase->ToString()}:\n\t{ex}"); } return newPartials; @@ -328,7 +325,7 @@ public void RebuildBoneTemplateBinding() foreach (var bone in GetAllBones()) bone.LinkToTemplate(BoneTemplateBinding.ContainsKey(bone.BoneName) ? BoneTemplateBinding[bone.BoneName] : null); - Plugin.Logger.Debug($"Rebuilt template binding for armature {_localId}"); + CustomizePlus.Logger.Debug($"Rebuilt template binding for armature {_localId}"); } private static bool AreTwinnedNames(string name1, string name2) diff --git a/CustomizePlus/Armatures/Data/Interop.cs b/CustomizePlus/Armatures/Data/Interop.cs index 2d939c41..fced77e3 100644 --- a/CustomizePlus/Armatures/Data/Interop.cs +++ b/CustomizePlus/Armatures/Data/Interop.cs @@ -1,12 +1,10 @@ -using System; -using System.Numerics; -using System.Runtime.InteropServices; - -using FFXIVClientStructs.FFXIV.Client.System.Memory; +using FFXIVClientStructs.FFXIV.Client.System.Memory; using FFXIVClientStructs.Havok.Common.Base.Math.Matrix; using FFXIVClientStructs.Havok.Common.Base.Math.QsTransform; +using System.Runtime.InteropServices; namespace CustomizePlus.Armatures.Data; + internal static class InteropAlloc { // Allocations diff --git a/CustomizePlus/Armatures/Data/ModelBone.cs b/CustomizePlus/Armatures/Data/ModelBone.cs index d72cef4d..4fff9262 100644 --- a/CustomizePlus/Armatures/Data/ModelBone.cs +++ b/CustomizePlus/Armatures/Data/ModelBone.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using CustomizePlus.Core.Data; +using CustomizePlus.Core.Data; using CustomizePlus.Core.Extensions; using CustomizePlus.Templates.Data; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; @@ -101,7 +97,7 @@ public bool LinkToTemplate(Template? template) CustomizedTransform = null; - Plugin.Logger.Verbose($"Unlinked {BoneName} from all templates"); + CustomizePlus.Logger.Verbose($"Unlinked {BoneName} from all templates"); return true; } @@ -109,7 +105,7 @@ public bool LinkToTemplate(Template? template) if (!template.Bones.ContainsKey(BoneName)) return false; - Plugin.Logger.Verbose($"Linking {BoneName} to {template.Name}"); + CustomizePlus.Logger.Verbose($"Linking {BoneName} to {template.Name}"); CustomizedTransform = template.Bones[BoneName]; return true; diff --git a/CustomizePlus/Armatures/Events/ArmatureChanged.cs b/CustomizePlus/Armatures/Events/ArmatureChanged.cs index cb3ec199..ab6b7ed9 100644 --- a/CustomizePlus/Armatures/Events/ArmatureChanged.cs +++ b/CustomizePlus/Armatures/Events/ArmatureChanged.cs @@ -1,13 +1,15 @@ -using CustomizePlus.Armatures.Data; -using OtterGui.Classes; +using CustomizePlus.Armatures.Data; namespace CustomizePlus.Armatures.Events; /// /// Triggered when armature is changed /// -public sealed class ArmatureChanged() : EventWrapper(nameof(ArmatureChanged)) +public sealed class ArmatureChanged(LunaLogger log) + : EventBase(nameof(ArmatureChanged), log) { + public readonly record struct Arguments(Type Type, Armature Armature, object? Data); + public enum Type { Created, diff --git a/CustomizePlus/Armatures/Services/ArmatureManager.cs b/CustomizePlus/Armatures/Services/ArmatureManager.cs index dbb304e4..97a354e4 100644 --- a/CustomizePlus/Armatures/Services/ArmatureManager.cs +++ b/CustomizePlus/Armatures/Services/ArmatureManager.cs @@ -1,4 +1,4 @@ -using CustomizePlus.Armatures.Data; +using CustomizePlus.Armatures.Data; using CustomizePlus.Armatures.Events; using CustomizePlus.Core.Data; using CustomizePlus.Core.Extensions; @@ -10,15 +10,9 @@ using CustomizePlus.Profiles.Events; 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 System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; namespace CustomizePlus.Armatures.Services; @@ -166,7 +160,7 @@ private void RefreshArmatures() TryLinkSkeleton(newArm); Armatures.Add(actorIdentifier, newArm); _logger.Debug($"Added '{newArm}' for {actorIdentifier.IncognitoDebug()} to cache"); - _event.Invoke(ArmatureChanged.Type.Created, newArm, activeProfile); + _event.Invoke(new ArmatureChanged.Arguments(ArmatureChanged.Type.Created, newArm, activeProfile)); continue; } @@ -232,7 +226,7 @@ private void RefreshArmatures() } } - _event.Invoke(ArmatureChanged.Type.Updated, armature, (activeProfile, oldProfile)); + _event.Invoke(new ArmatureChanged.Arguments(ArmatureChanged.Type.Updated, armature, (activeProfile, oldProfile))); } //Needed because: @@ -301,7 +295,7 @@ private void ApplyPiecewiseTransformation(Armature armature, Actor actor, ActorI var cBase = actor.Model.AsCharacterBase; var isMount = actorIdentifier.Type == IdentifierType.Owned && - actorIdentifier.Kind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.MountType; + actorIdentifier.Kind == Dalamud.Game.ClientState.Objects.Enums.ObjectKind.Mount; Actor? mountOwner = null; Armature? mountOwnerArmature = null; @@ -414,11 +408,12 @@ private void RemoveArmature(Armature armature, ArmatureChanged.DeletionReason re Armatures.Remove(armature.ActorIdentifier); _logger.Debug($"Armature {armature} removed from cache"); - _event.Invoke(ArmatureChanged.Type.Deleted, armature, reason); + _event.Invoke(new ArmatureChanged.Arguments(ArmatureChanged.Type.Deleted, armature, reason)); } - private void OnTemplateChange(TemplateChanged.Type type, Templates.Data.Template? template, object? arg3) + private void OnTemplateChange(in TemplateChanged.Arguments args) { + var (type, template, arg3) = args; if (type is not TemplateChanged.Type.NewBone && type is not TemplateChanged.Type.DeletedBone && type is not TemplateChanged.Type.EditorCharacterChanged && @@ -434,7 +429,7 @@ type is not TemplateChanged.Type.EditorEnabled && { foreach (var profile in _profileManager.GetProfilesUsingTemplate(template)) { - _logger.Debug($"ArmatureManager.OnTemplateChange New/Deleted bone or character changed: {type}, template: {template.Name.Text.Incognify()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}->{profile.Armatures.Count} armatures"); + _logger.Debug($"ArmatureManager.OnTemplateChange New/Deleted bone or character changed: {type}, template: {template.Name.Incognify()}, profile: {profile.Name.Incognify()}->{profile.Enabled}->{profile.Armatures.Count} armatures"); if (!profile.Enabled || profile.Armatures.Count == 0) continue; @@ -462,7 +457,7 @@ type is not TemplateChanged.Type.EditorEnabled && foreach (var armature in profile.Armatures) armature.IsPendingProfileRebind = true; - _logger.Debug($"ArmatureManager.OnTemplateChange Editor profile character name changed, armature rebind scheduled: {type}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}, new name: {character.Incognito(null)}"); + _logger.Debug($"ArmatureManager.OnTemplateChange Editor profile character name changed, armature rebind scheduled: {type}, profile: {profile.Name.Incognify()}->{profile.Enabled}, new name: {character.Incognito(null)}"); return; } @@ -473,7 +468,7 @@ type is not TemplateChanged.Type.EditorEnabled && ActorIdentifier actor; bool hasChanges; - if(type == TemplateChanged.Type.EditorEnabled) + if (type == TemplateChanged.Type.EditorEnabled) actor = (ActorIdentifier)arg3; else (actor, hasChanges) = ((ActorIdentifier, bool))arg3; @@ -488,8 +483,9 @@ type is not TemplateChanged.Type.EditorEnabled && } } - private void OnProfileChange(ProfileChanged.Type type, Profile? profile, object? arg3) + private void OnProfileChange(in ProfileChanged.Arguments args) { + var (type, profile, arg3) = args; if (type is not ProfileChanged.Type.AddedTemplate && type is not ProfileChanged.Type.RemovedTemplate && type is not ProfileChanged.Type.EnabledTemplate && @@ -517,7 +513,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && foreach (var armature in oldProfile.Armatures) armature.IsPendingProfileRebind = true; - _logger.Debug($"ArmatureManager.OnProfileChange Profile no longer default/default for local player, armatures rebind scheduled: {type}, old profile: {oldProfile.Name.Text.Incognify()}->{oldProfile.Enabled}"); + _logger.Debug($"ArmatureManager.OnProfileChange Profile no longer default/default for local player, armatures rebind scheduled: {type}, old profile: {oldProfile.Name.Incognify()}->{oldProfile.Enabled}"); return; } @@ -528,7 +524,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && return; } - if(type == ProfileChanged.Type.PriorityChanged) + if (type == ProfileChanged.Type.PriorityChanged) { if (!profile.Enabled) return; @@ -569,7 +565,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && return; } - foreach(var character in profile.Characters) + foreach (var character in profile.Characters) { if (!character.IsValid) continue; @@ -586,7 +582,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && if (type == ProfileChanged.Type.TemporaryProfileAdded) { - foreach(var character in profile.Characters) + foreach (var character in profile.Characters) { if (!character.IsValid || !Armatures.ContainsKey(character)) continue; @@ -601,7 +597,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && armature.IsPendingProfileRebind = true; } - _logger.Debug($"ArmatureManager.OnProfileChange TemporaryProfileAdded, calling rebind for existing armature: {type}, data payload: {arg3?.ToString()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}"); + _logger.Debug($"ArmatureManager.OnProfileChange TemporaryProfileAdded, calling rebind for existing armature: {type}, data payload: {arg3?.ToString()}, profile: {profile.Name.Incognify()}->{profile.Enabled}"); return; } @@ -619,8 +615,8 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && foreach (var armature in GetArmaturesForCharacter(actorIdentifier)) armature.IsPendingProfileRebind = true; - _logger.Debug($"ArmatureManager.OnProfileChange AC/RC, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}"); - + _logger.Debug($"ArmatureManager.OnProfileChange AC/RC, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Incognify()}->{profile.Enabled}"); + return; } @@ -638,7 +634,7 @@ type is not ProfileChanged.Type.ChangedDefaultProfile && armature.IsPendingProfileRebind = true; } - _logger.Debug($"ArmatureManager.OnProfileChange DEL/TPD, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Text.Incognify()}->{profile.Enabled}"); + _logger.Debug($"ArmatureManager.OnProfileChange DEL/TPD, armature rebind scheduled: {type}, data payload: {arg3?.ToString()?.Incognify()}, profile: {profile.Name.Incognify()}->{profile.Enabled}"); return; } diff --git a/CustomizePlus/Configuration/Data/LunaUiConfiguration.cs b/CustomizePlus/Configuration/Data/LunaUiConfiguration.cs new file mode 100644 index 00000000..1296b4f8 --- /dev/null +++ b/CustomizePlus/Configuration/Data/LunaUiConfiguration.cs @@ -0,0 +1,43 @@ +using CustomizePlus.Core.Services; +using Luna.Generators; +using Newtonsoft.Json.Linq; +using Penumbra.GameData.Actors; +using System.Text.Json; + +namespace CustomizePlus.Configuration.Data; + +public sealed partial class LunaUiConfiguration : ConfigurationFile +{ + private readonly ActorManager _actors; + + public LunaUiConfiguration(SaveService saveService, MessageService messageService, ActorManager actors) + : base(saveService, messageService, TimeSpan.FromMinutes(5)) + { + _actors = actors; + Load(); + } + + [ConfigProperty] + private TwoPanelWidth _templatesTabScale = new(250, ScalingMode.Absolute); + + [ConfigProperty] + private TwoPanelWidth _profilesTabScale = new(0.3f, ScalingMode.Percentage); + + public override int CurrentVersion + => 1; + + protected override void AddData(Utf8JsonWriter j) + { + TemplatesTabScale.WriteJson(j, "TemplatesTab"u8); + ProfilesTabScale.WriteJson(j, "ProfilesTab"u8); + } + + protected override void LoadData(JObject j) + { + _templatesTabScale = TwoPanelWidth.ReadJson(j, "TemplatesTab", new TwoPanelWidth(250, ScalingMode.Absolute)); + _profilesTabScale = TwoPanelWidth.ReadJson(j, "ProfilesTab", new TwoPanelWidth(0.3f, ScalingMode.Percentage)); + } + + public override string ToFilePath(FilenameService fileNames) + => fileNames.UiConfigurationFile; +} diff --git a/CustomizePlus/Configuration/Data/PluginConfiguration.cs b/CustomizePlus/Configuration/Data/PluginConfiguration.cs index 0450366e..241c6f6a 100644 --- a/CustomizePlus/Configuration/Data/PluginConfiguration.cs +++ b/CustomizePlus/Configuration/Data/PluginConfiguration.cs @@ -1,18 +1,13 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Dalamud.Configuration; -using Newtonsoft.Json; -using OtterGui.Classes; -using OtterGui.Widgets; -using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; -using CustomizePlus.Core.Services; -using CustomizePlus.Core.Data; using CustomizePlus.Configuration.Services; +using CustomizePlus.Core.Data; +using CustomizePlus.Core.Helpers; +using CustomizePlus.Core.Services; using CustomizePlus.UI.Windows; +using Dalamud.Configuration; using Dalamud.Interface.ImGuiNotification; +using Newtonsoft.Json; using Penumbra.GameData.Actors; -using CustomizePlus.Core.Helpers; +using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs; namespace CustomizePlus.Configuration.Data; @@ -49,7 +44,7 @@ public class ChangelogSettingsEntries [Serializable] public class UISettingsEntries { - public DoubleModifier DeleteTemplateModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); + public DoubleModifier DeleteModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift); public bool FoldersDefaultOpen { get; set; } = true; @@ -92,6 +87,8 @@ public class EditorConfigurationEntries public bool BoneMirroringEnabled { get; set; } = false; + [JsonConverter(typeof(ActorIdentifierJsonConverter))] + public ActorIdentifier PreviewCharacter { get; set; } = ActorIdentifier.Invalid; public int EditorValuesPrecision { get; set; } = 3; @@ -133,6 +130,13 @@ public class IntegrationSettingsEntries public IntegrationSettingsEntries IntegrationSettings { get; set; } = new(); + [JsonConverter(typeof(SortModeConverter))] + [JsonProperty(Order = int.MaxValue)] + public ISortMode SortMode { get; set; } = ISortMode.FoldersFirst; + + [JsonIgnore] + public LunaUiConfiguration LunaUiConfiguration { get; internal set; } + [JsonIgnore] private readonly SaveService _saveService; @@ -142,10 +146,12 @@ public class IntegrationSettingsEntries public PluginConfiguration( SaveService saveService, MessageService messageService, - ConfigurationMigrator migrator) + ConfigurationMigrator migrator, + LunaUiConfiguration lunaUiConfiguration) { _saveService = saveService; _messageService = messageService; + LunaUiConfiguration = lunaUiConfiguration; Load(migrator); } @@ -154,17 +160,17 @@ public void Load(ConfigurationMigrator migrator) { static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs) { - Plugin.Logger.Error( + CustomizePlus.Logger.Error( $"Error parsing configuration at {errorArgs.ErrorContext.Path}, using default or migrating:\n{errorArgs.ErrorContext.Error}"); errorArgs.ErrorContext.Handled = true; } - if (!File.Exists(_saveService.FileNames.ConfigFile)) + if (!File.Exists(_saveService.FileNames.ConfigurationFile)) return; try { - var text = File.ReadAllText(_saveService.FileNames.ConfigFile); + var text = File.ReadAllText(_saveService.FileNames.ConfigurationFile); JsonConvert.PopulateObject(text, this, new JsonSerializerSettings { Error = HandleDeserializationError, @@ -181,17 +187,37 @@ static void HandleDeserializationError(object? sender, ErrorEventArgs errorArgs) migrator.Migrate(this); } - public string ToFilename(FilenameService fileNames) - => fileNames.ConfigFile; + public string ToFilePath(FilenameService fileNames) + => fileNames.ConfigurationFile; - public void Save(StreamWriter writer) + public void Save(Stream stream) { - using var jWriter = new JsonTextWriter(writer) { Formatting = Formatting.Indented }; + using var writer = new StreamWriter(stream); + using var jWriter = new JsonTextWriter(writer); + jWriter.Formatting = Formatting.Indented; var serializer = new JsonSerializer { Formatting = Formatting.Indented }; - serializer.Converters.Add(new ActorIdentifierJsonConverter()); serializer.Serialize(jWriter, this); } public void Save() => _saveService.DelaySave(this); -} \ No newline at end of file + + /// Convert SortMode Types to their name. + private class SortModeConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, ISortMode? value, JsonSerializer serializer) + { + value ??= ISortMode.FoldersFirst; + serializer.Serialize(writer, value.GetType().Name); + } + + public override ISortMode ReadJson(JsonReader reader, Type objectType, ISortMode? existingValue, bool hasExistingValue, + JsonSerializer serializer) + { + if (serializer.Deserialize(reader) is { } name) + return ISortMode.Valid.GetValueOrDefault(name, existingValue ?? ISortMode.FoldersFirst); + + return existingValue ?? ISortMode.FoldersFirst; + } + } +} diff --git a/CustomizePlus/Configuration/Data/Version2/V2BoneEditsContainer.cs b/CustomizePlus/Configuration/Data/Version2/V2BoneEditsContainer.cs index 5748ed4d..4d4c0651 100644 --- a/CustomizePlus/Configuration/Data/Version2/V2BoneEditsContainer.cs +++ b/CustomizePlus/Configuration/Data/Version2/V2BoneEditsContainer.cs @@ -1,7 +1,4 @@ -using System; -using System.Numerics; - -namespace CustomizePlus.Configuration.Data.Version2; +namespace CustomizePlus.Configuration.Data.Version2; [Serializable] public struct V2BoneEditsContainer diff --git a/CustomizePlus/Configuration/Data/Version2/Version2Profile.cs b/CustomizePlus/Configuration/Data/Version2/Version2Profile.cs index 71ca3aed..7e37df1e 100644 --- a/CustomizePlus/Configuration/Data/Version2/Version2Profile.cs +++ b/CustomizePlus/Configuration/Data/Version2/Version2Profile.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; - -namespace CustomizePlus.Configuration.Data.Version2; +namespace CustomizePlus.Configuration.Data.Version2; [Serializable] public class Version2Profile diff --git a/CustomizePlus/Configuration/Data/Version3/V3BoneTransform.cs b/CustomizePlus/Configuration/Data/Version3/V3BoneTransform.cs index 343303bb..a539ee32 100644 --- a/CustomizePlus/Configuration/Data/Version3/V3BoneTransform.cs +++ b/CustomizePlus/Configuration/Data/Version3/V3BoneTransform.cs @@ -1,7 +1,4 @@ -using System; -using System.Numerics; - -namespace CustomizePlus.Configuration.Data.Version3; +namespace CustomizePlus.Configuration.Data.Version3; public class V3BoneTransform { diff --git a/CustomizePlus/Configuration/Data/Version3/Version3Profile.cs b/CustomizePlus/Configuration/Data/Version3/Version3Profile.cs index a37206d8..cf62b13b 100644 --- a/CustomizePlus/Configuration/Data/Version3/Version3Profile.cs +++ b/CustomizePlus/Configuration/Data/Version3/Version3Profile.cs @@ -1,6 +1,4 @@ using CustomizePlus.Core.Data; -using System; -using System.Collections.Generic; namespace CustomizePlus.Configuration.Data.Version3; diff --git a/CustomizePlus/Configuration/Helpers/V2ProfileToV3Converter.cs b/CustomizePlus/Configuration/Helpers/V2ProfileToV3Converter.cs index 9bdff0b3..17eab781 100644 --- a/CustomizePlus/Configuration/Helpers/V2ProfileToV3Converter.cs +++ b/CustomizePlus/Configuration/Helpers/V2ProfileToV3Converter.cs @@ -1,7 +1,6 @@ using CustomizePlus.Configuration.Data.Version2; using CustomizePlus.Configuration.Data.Version3; using CustomizePlus.Core.Data; -using System.Numerics; namespace CustomizePlus.Configuration.Helpers; diff --git a/CustomizePlus/Configuration/Helpers/V3ProfileToV4Converter.cs b/CustomizePlus/Configuration/Helpers/V3ProfileToV4Converter.cs index 0a6dc622..cbf7f84f 100644 --- a/CustomizePlus/Configuration/Helpers/V3ProfileToV4Converter.cs +++ b/CustomizePlus/Configuration/Helpers/V3ProfileToV4Converter.cs @@ -2,8 +2,6 @@ using CustomizePlus.Core.Data; using CustomizePlus.Profiles.Data; using CustomizePlus.Templates.Data; -using System; -using System.Collections.Generic; namespace CustomizePlus.Configuration.Helpers; diff --git a/CustomizePlus/Configuration/Services/ConfigurationMigrator.cs b/CustomizePlus/Configuration/Services/ConfigurationMigrator.cs index cd4437a1..fd5af279 100644 --- a/CustomizePlus/Configuration/Services/ConfigurationMigrator.cs +++ b/CustomizePlus/Configuration/Services/ConfigurationMigrator.cs @@ -1,34 +1,29 @@ -using OtterGui.Classes; -using OtterGui.Log; +using CustomizePlus.Configuration.Data; using CustomizePlus.Core.Data; using CustomizePlus.Core.Services; -using CustomizePlus.Configuration.Data; -using CustomizePlus.Core.Events; +using Dalamud.Game.ClientState.Keys; using Dalamud.Interface.ImGuiNotification; +using Newtonsoft.Json.Linq; namespace CustomizePlus.Configuration.Services; public class ConfigurationMigrator { + private readonly MessageService _messageService; //we can't use popups here since they rely on PluginConfiguration and using them here hangs plugin loading private readonly SaveService _saveService; private readonly BackupService _backupService; - private readonly MessageService _messageService; //we can't use popups here since they rely on PluginConfiguration and using them here hangs plugin loading private readonly Logger _logger; - private readonly ReloadEvent _reloadEvent; public ConfigurationMigrator( + MessageService messageService, SaveService saveService, BackupService backupService, - MessageService messageService, - Logger logger, - ReloadEvent reloadEvent - ) + Logger logger) { + _messageService = messageService; _saveService = saveService; _backupService = backupService; - _messageService = messageService; _logger = logger; - _reloadEvent = reloadEvent; } public void Migrate(PluginConfiguration config) @@ -51,7 +46,28 @@ public void Migrate(PluginConfiguration config) _logger.Information("Migrating configuration from V4 to V5 (ChildScaling feature)"); } + var data = JObject.Parse(File.ReadAllText(_saveService.FileNames.ConfigurationFile)); + + if (configVersion == 5) + { + _logger.Information("Migrating configuration from V4 to V5"); + + _backupService.CreateMigrationBackup("pre_filesystem_update", + _saveService.FileNames.MigrationProfileFileSystem, _saveService.FileNames.MigrationTemplateFileSystem); + + if (data["UISettings"] is JObject uiSettings && uiSettings["DeleteTemplateModifier"] is JObject deleteModifier) + { + _logger.Debug("Migrating DeleteTemplateModifier"); + + var modifier1 = deleteModifier["Modifier1"]?["Modifier"]?.Value() ?? (ushort)VirtualKey.CONTROL; + var modifier2 = deleteModifier["Modifier2"]?["Modifier"]?.Value() ?? (ushort)VirtualKey.SHIFT; + config.UISettings.DeleteModifier = new DoubleModifier((VirtualKey)modifier1, (VirtualKey)modifier2); + + config.Save(); + } + } + config.Version = Constants.ConfigurationVersion; - _saveService.ImmediateSave(config); + return; } } diff --git a/CustomizePlus/Core/Data/BoneData.cs b/CustomizePlus/Core/Data/BoneData.cs index de1e4a45..b2da1b3b 100644 --- a/CustomizePlus/Core/Data/BoneData.cs +++ b/CustomizePlus/Core/Data/BoneData.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using Dalamud.Utility; using System.Text; using System.Text.RegularExpressions; -using Dalamud.Utility; namespace CustomizePlus.Core.Data; @@ -615,7 +612,7 @@ private static IEnumerable ParseHairstyle(params string[] boneNames) } catch (Exception e) { - Plugin.Logger.Error($"Failed to dynamically parse bones for hairstyle of '{boneNames[index]}'"); + CustomizePlus.Logger.Error($"Failed to dynamically parse bones for hairstyle of '{boneNames[index]}'"); } index++; diff --git a/CustomizePlus/Core/Data/BoneTransform.cs b/CustomizePlus/Core/Data/BoneTransform.cs index b562ef65..7be4de2f 100644 --- a/CustomizePlus/Core/Data/BoneTransform.cs +++ b/CustomizePlus/Core/Data/BoneTransform.cs @@ -1,9 +1,7 @@ -using System; -using System.Numerics; -using System.Runtime.Serialization; -using CustomizePlus.Core.Extensions; +using CustomizePlus.Core.Extensions; using CustomizePlus.Game.Services.GPose.ExternalTools; using FFXIVClientStructs.Havok.Common.Base.Math.QsTransform; +using System.Runtime.Serialization; namespace CustomizePlus.Core.Data; diff --git a/CustomizePlus/Core/Data/Constants.cs b/CustomizePlus/Core/Data/Constants.cs index 347add09..754fda68 100644 --- a/CustomizePlus/Core/Data/Constants.cs +++ b/CustomizePlus/Core/Data/Constants.cs @@ -1,7 +1,6 @@ using FFXIVClientStructs.Havok.Common.Base.Math.QsTransform; using FFXIVClientStructs.Havok.Common.Base.Math.Quaternion; using FFXIVClientStructs.Havok.Common.Base.Math.Vector; -using System.Numerics; namespace CustomizePlus.Core.Data; @@ -10,7 +9,7 @@ internal static class Constants /// /// Version of the configuration file, when increased a converter should be implemented if necessary. /// - public const int ConfigurationVersion = 5; + public const int ConfigurationVersion = 6; /// /// The name of the root bone. diff --git a/CustomizePlus/Core/Events/ReloadEvent.cs b/CustomizePlus/Core/Events/ReloadEvent.cs index 4347d34f..7a30889a 100644 --- a/CustomizePlus/Core/Events/ReloadEvent.cs +++ b/CustomizePlus/Core/Events/ReloadEvent.cs @@ -1,12 +1,12 @@ -using OtterGui.Classes; - namespace CustomizePlus.Core.Events; /// /// Triggered when complete plugin reload is requested /// -public sealed class ReloadEvent() : EventWrapper(nameof(ReloadEvent)) +public sealed class ReloadEvent(LunaLogger log) : EventBase(nameof(ReloadEvent), log) { + public readonly record struct Arguments(Type Type); + public enum Type { ReloadAll, diff --git a/CustomizePlus/Core/Extensions/StringExtensions.cs b/CustomizePlus/Core/Extensions/StringExtensions.cs index a9c9721c..fd528cb9 100644 --- a/CustomizePlus/Core/Extensions/StringExtensions.cs +++ b/CustomizePlus/Core/Extensions/StringExtensions.cs @@ -1,5 +1,4 @@ using Dalamud.Utility; -using System; namespace CustomizePlus.Core.Extensions; @@ -32,7 +31,7 @@ public static string Incognify(this string str) private static string GetCutString(this string str, int maxLength = 5) { - if(str.Length > maxLength) + if (str.Length > maxLength) return $"{str[..maxLength]}..."; else return str[0..Math.Min(str.Length, maxLength)]; diff --git a/CustomizePlus/Core/Extensions/TransformExtensions.cs b/CustomizePlus/Core/Extensions/TransformExtensions.cs index ab749f81..2f4b06c5 100644 --- a/CustomizePlus/Core/Extensions/TransformExtensions.cs +++ b/CustomizePlus/Core/Extensions/TransformExtensions.cs @@ -1,6 +1,4 @@ -using System; -using System.Numerics; -using CustomizePlus.Core.Data; +using CustomizePlus.Core.Data; using FFXIVClientStructs.Havok.Common.Base.Math.QsTransform; using FFXIVClientStructs.Havok.Common.Base.Math.Vector; diff --git a/CustomizePlus/Core/Extensions/VectorExtensions.cs b/CustomizePlus/Core/Extensions/VectorExtensions.cs index 3a2afc4b..39a72df3 100644 --- a/CustomizePlus/Core/Extensions/VectorExtensions.cs +++ b/CustomizePlus/Core/Extensions/VectorExtensions.cs @@ -1,6 +1,4 @@ -using System; -using System.Numerics; -using CustomizePlus.Anamnesis.Data; +using CustomizePlus.Anamnesis.Data; using FFXIVClientStructs.Havok.Common.Base.Math.Quaternion; using FFXIVClientStructs.Havok.Common.Base.Math.Vector; diff --git a/CustomizePlus/Core/Helpers/ActorIdentifierJsonConverter.cs b/CustomizePlus/Core/Helpers/ActorIdentifierJsonConverter.cs index dddd9e0a..165fd3bd 100644 --- a/CustomizePlus/Core/Helpers/ActorIdentifierJsonConverter.cs +++ b/CustomizePlus/Core/Helpers/ActorIdentifierJsonConverter.cs @@ -1,7 +1,6 @@ -using System; -using Newtonsoft.Json; -using Penumbra.GameData.Actors; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Penumbra.GameData.Actors; namespace CustomizePlus.Core.Helpers; diff --git a/CustomizePlus/Core/Helpers/Base64Helper.cs b/CustomizePlus/Core/Helpers/Base64Helper.cs index 543edbcb..4f65b7ed 100644 --- a/CustomizePlus/Core/Helpers/Base64Helper.cs +++ b/CustomizePlus/Core/Helpers/Base64Helper.cs @@ -1,17 +1,13 @@ -using CustomizePlus.Core.Data; -using CustomizePlus.Templates.Data; -using CustomizePlus.Api.Data; +using CustomizePlus.Api.Data; +using CustomizePlus.Core.Data; using CustomizePlus.Profiles.Data; +using CustomizePlus.Templates.Data; using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; -using System.Linq; -using System.Numerics; using System.Text; namespace CustomizePlus.Core.Helpers; + public class BoneTransformData // literally not cooking { public string BoneCodeName { get; set; } diff --git a/CustomizePlus/Core/Helpers/CtrlHelper.cs b/CustomizePlus/Core/Helpers/CtrlHelper.cs index d27eefb2..18f155cf 100644 --- a/CustomizePlus/Core/Helpers/CtrlHelper.cs +++ b/CustomizePlus/Core/Helpers/CtrlHelper.cs @@ -1,8 +1,5 @@ -using System; -using Dalamud.Interface; +using Dalamud.Interface; using Dalamud.Utility; -using Dalamud.Bindings.ImGui; -using System.Text; namespace CustomizePlus.Core.Helpers; @@ -12,12 +9,12 @@ public static class CtrlHelper /// Gets the width of an icon button, checkbox, etc... /// /// per https://github.com/ocornut/imgui/issues/3714#issuecomment-759319268 - public static float IconButtonWidth => ImGui.GetFrameHeight() + 2 * ImGui.GetStyle().ItemInnerSpacing.X; + public static float IconButtonWidth => Im.Style.FrameHeight + 2 * Im.Style.ItemInnerSpacing.X; public static bool TextBox(string label, ref string value) { - ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X); - return ImGui.InputText(label, ref value, 1024); + Im.Item.SetNextWidthFull(); + return Im.Input.Text(label, ref value, maxLength: 1024); } public static bool TextPropertyBox(string label, Func get, Action set) @@ -34,21 +31,13 @@ public static bool TextPropertyBox(string label, Func get, Action toggle) { var temp = shown; - var toggled = ImGui.Checkbox(label, ref temp); + var toggled = Im.Checkbox(label, ref temp); if (toggled) { @@ -68,48 +57,35 @@ public static bool CheckboxToggle(string label, in bool shown, Action togg public static bool ArrowToggle(string label, ref bool value) { - unsafe // temporary fix - { - var utf8Label = Encoding.UTF8.GetBytes(label + "\0"); - - fixed (byte* labelPtr = utf8Label) - { - bool toggled = ImGuiNative.ArrowButton(labelPtr, value ? ImGuiDir.Down : ImGuiDir.Right) != 0; - - if (toggled) - value = !value; + if (Im.ArrowButton(label, value ? Direction.Down : Direction.Right)) + value = !value; - return value; - } - } + return value; } - public static void AddHoverText(string text) { - if (ImGui.IsItemHovered()) - { - ImGui.SetTooltip(text); - } + Im.Tooltip.OnHover(text); } public enum TextAlignment { Left, Center, Right }; + public static void StaticLabel(string? text, TextAlignment align = TextAlignment.Left, string tooltip = "") { if (text != null) { if (align == TextAlignment.Center) { - ImGui.Dummy(new System.Numerics.Vector2((ImGui.GetContentRegionAvail().X - ImGui.CalcTextSize(text).X) / 2, 0)); - ImGui.SameLine(); + Im.Dummy((Im.ContentRegion.Available.X - Im.Font.CalculateSize(text).X) / 2, 0); + Im.Line.Same(); } else if (align == TextAlignment.Right) { - ImGui.Dummy(new System.Numerics.Vector2(ImGui.GetContentRegionAvail().X - ImGui.CalcTextSize(text).X, 0)); - ImGui.SameLine(); + Im.Dummy(Im.ContentRegion.Available.X - Im.Font.CalculateSize(text).X, 0); + Im.Line.Same(); } - ImGui.Text(text); + Im.Text(text); if (!tooltip.IsNullOrWhitespace()) { AddHoverText(tooltip); @@ -120,11 +96,9 @@ public static void StaticLabel(string? text, TextAlignment align = TextAlignment public static void LabelWithIcon(FontAwesomeIcon icon, string text, bool isSameLine = true) { if (isSameLine) - ImGui.SameLine(); - ImGui.PushFont(UiBuilder.IconFont); - ImGui.Text(icon.ToIconString()); - ImGui.PopFont(); - ImGui.SameLine(); - ImGui.TextWrapped(text); + Im.Line.Same(); + icon.Draw(); + Im.Line.Same(); + Im.TextWrapped(text); } } \ No newline at end of file diff --git a/CustomizePlus/Core/Helpers/SortMode.cs b/CustomizePlus/Core/Helpers/SortMode.cs new file mode 100644 index 00000000..9ffbd15b --- /dev/null +++ b/CustomizePlus/Core/Helpers/SortMode.cs @@ -0,0 +1,121 @@ +using CustomizePlus.Profiles.Data; +using CustomizePlus.Templates.Data; +using System.Collections.Frozen; + +namespace CustomizePlus.Core.Helpers; + +public readonly struct CreationDate : ISortMode +{ + public static readonly CreationDate Instance = new(); + + public ReadOnlySpan Name + => "Creation Date (Older First)"u8; + + public ReadOnlySpan Description + => "In each folder, sort all subfolders lexicographically, then sort all leaves using their creation date."u8; + + public IEnumerable GetChildren(IFileSystemFolder f) + => ISortMode.GetFolderLike(f).Concat(ISortMode.GetLeaveLike(f).OrderBy(l => l switch + { + IFileSystemData