diff --git a/CustomizePlus/Api/Compatibility/CustomizePlusLegacyIpc.cs b/CustomizePlus/Api/Compatibility/CustomizePlusLegacyIpc.cs index de6e946..b24fcf4 100644 --- a/CustomizePlus/Api/Compatibility/CustomizePlusLegacyIpc.cs +++ b/CustomizePlus/Api/Compatibility/CustomizePlusLegacyIpc.cs @@ -25,7 +25,7 @@ using Penumbra.GameData.Interop; namespace CustomizePlus.Api.Compatibility; -[Obsolete("Will be removed in the next release")] +[Obsolete("Will be removed in the near future, do not use this IPC")] public class CustomizePlusLegacyIpc : IDisposable { private readonly IObjectTable _objectTable; @@ -73,7 +73,7 @@ public class CustomizePlusLegacyIpc : IDisposable InitializeProviders(); - _profileChangedEvent.Subscribe(OnProfileChange, ProfileChanged.Priority.CustomizePlusIpc); + _profileChangedEvent.Subscribe(OnProfileChange, ProfileChanged.Priority.CustomizePlusLegacyIpc); _armatureChangedEvent.Subscribe(OnArmatureChanged, ArmatureChanged.Priority.CustomizePlusIpc); } @@ -109,10 +109,10 @@ public class CustomizePlusLegacyIpc : IDisposable return; if (type == ArmatureChanged.Type.Created || - type == ArmatureChanged.Type.Rebound) + type == ArmatureChanged.Type.Updated) { if(armature.Profile == null) - _logger.Warning("Armature created/rebound and profile is null"); + _logger.Warning("[LEGACY IPC DO NOT USE] Armature created/rebound and profile is null"); OnProfileUpdate(armature.Profile); return; @@ -127,7 +127,7 @@ public class CustomizePlusLegacyIpc : IDisposable private void InitializeProviders() { - _logger.Debug("Initializing legacy Customize+ IPC providers."); + _logger.Debug("[LEGACY IPC DO NOT USE] Initializing legacy Customize+ IPC providers."); try { ProviderGetApiVersion = _pluginInterface.GetIpcProvider<(int, int)>(ProviderApiVersionLabel); @@ -135,7 +135,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Error($"Error registering legacy Customize+ IPC provider for {ProviderApiVersionLabel}: {ex}"); + _logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {ProviderApiVersionLabel}: {ex}"); } try @@ -146,7 +146,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Error($"Error registering legacy Customize+ IPC provider for {GetProfileFromCharacterLabel}: {ex}"); + _logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {GetProfileFromCharacterLabel}: {ex}"); } try @@ -157,7 +157,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Error($"Error registering legacy Customize+ IPC provider for {SetProfileToCharacterLabel}: {ex}"); + _logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {SetProfileToCharacterLabel}: {ex}"); } try @@ -168,7 +168,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Error($"Error registering legacy Customize+ IPC provider for {RevertCharacterLabel}: {ex}"); + _logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {RevertCharacterLabel}: {ex}"); } try @@ -177,7 +177,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Error($"Error registering legacy Customize+ IPC provider for {OnProfileUpdateLabel}: {ex}"); + _logger.Error($"[LEGACY IPC DO NOT USE] Error registering legacy Customize+ IPC provider for {OnProfileUpdateLabel}: {ex}"); } } @@ -192,7 +192,7 @@ public class CustomizePlusLegacyIpc : IDisposable private void OnProfileUpdate(Profile? profile) { - _logger.Debug($"Sending local player update message: {(profile != null ? profile.ToString() : "no profile")}"); + _logger.Debug($"[LEGACY IPC DO NOT USE] Sending local player update message: {(profile != null ? profile.ToString() : "no profile")}"); var convertedProfile = profile != null ? GetVersion3Profile(profile) : null; @@ -246,7 +246,7 @@ public class CustomizePlusLegacyIpc : IDisposable } catch (Exception ex) { - _logger.Warning($"Unable to set body profile. Character: {character?.Name}, exception: {ex}, debug data: {GetBase64String(profileJson)}"); + _logger.Warning($"[LEGACY IPC DO NOT USE] Unable to set body profile. Character: {character?.Name}, exception: {ex}, debug data: {GetBase64String(profileJson)}"); } } diff --git a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs index 3f0690f..236adaa 100644 --- a/CustomizePlus/Api/CustomizePlusIpc.Profile.cs +++ b/CustomizePlus/Api/CustomizePlusIpc.Profile.cs @@ -270,27 +270,6 @@ public partial class CustomizePlusIpc } //warn: limitation - ignores default profiles but why you would use default profile on your own character - private void OnProfileChange(ProfileChanged.Type type, Profile? profile, object? arg3) - { - if (type != ProfileChanged.Type.AddedTemplate && - type != ProfileChanged.Type.RemovedTemplate && - type != ProfileChanged.Type.MovedTemplate && - type != ProfileChanged.Type.ChangedTemplate && - type != ProfileChanged.Type.Toggled) - return; - - if (profile == null || - !profile.Enabled || //profile = null event will be sent from OnArmatureChanged - profile.CharacterName.Text != _gameObjectService.GetCurrentPlayerName()) - return; - - Character? localPlayerCharacter = (Character?)_gameObjectService.GetDalamudGameObjectFromActor(_gameObjectService.GetLocalPlayerActor()); - if (localPlayerCharacter == null) - return; - - OnProfileUpdateInternal(localPlayerCharacter, profile); - } - private void OnArmatureChanged(ArmatureChanged.Type type, Armature armature, object? arg3) { string currentPlayerName = _gameObjectService.GetCurrentPlayerName(); @@ -302,13 +281,34 @@ public partial class CustomizePlusIpc if (localPlayerCharacter == null) return; - if (type == ArmatureChanged.Type.Created || //todo: might create second call after OnProfileChange? - type == ArmatureChanged.Type.Rebound) + if (type == ArmatureChanged.Type.Created || + type == ArmatureChanged.Type.Updated) { if (armature.Profile == null) - _logger.Warning("Armature created/rebound and profile is null"); + _logger.Fatal("INTEGRITY ERROR: Armature created/rebound and profile is null"); - OnProfileUpdateInternal(localPlayerCharacter, armature.Profile); + (Profile? activeProfile, Profile? oldProfile) = (null, null); + if (type == ArmatureChanged.Type.Created) + (activeProfile, oldProfile) = ((Profile?)arg3, null); + else + (activeProfile, oldProfile) = ((Profile?, Profile?))arg3; + + if (activeProfile != null) + { + if (activeProfile == _profileManager.DefaultProfile) + return; //default profiles are not allowed to be sent + + if (activeProfile.ProfileType == ProfileType.Editor) + { + if (activeProfile == oldProfile) //ignore any changes while player is in editor + return; + + OnProfileUpdateInternal(localPlayerCharacter, null); //send empty profile when player enters editor + return; + } + } + + OnProfileUpdateInternal(localPlayerCharacter, activeProfile); return; } diff --git a/CustomizePlus/Api/CustomizePlusIpc.cs b/CustomizePlus/Api/CustomizePlusIpc.cs index 3994ba8..81451f8 100644 --- a/CustomizePlus/Api/CustomizePlusIpc.cs +++ b/CustomizePlus/Api/CustomizePlusIpc.cs @@ -18,7 +18,6 @@ public partial class CustomizePlusIpc : IDisposable private readonly ProfileManager _profileManager; private readonly GameObjectService _gameObjectService; - private readonly ProfileChanged _profileChangedEvent; private readonly ArmatureChanged _armatureChangedEvent; /// @@ -32,8 +31,7 @@ public partial class CustomizePlusIpc : IDisposable HookingService hookingService, ProfileManager profileManager, GameObjectService gameObjectService, - ArmatureChanged armatureChangedEvent, - ProfileChanged profileChangedEvent) + ArmatureChanged armatureChangedEvent) { _pluginInterface = pluginInterface; _logger = logger; @@ -41,19 +39,15 @@ public partial class CustomizePlusIpc : IDisposable _profileManager = profileManager; _gameObjectService = gameObjectService; - - _profileChangedEvent = profileChangedEvent; _armatureChangedEvent = armatureChangedEvent; EzIPC.Init(this, "CustomizePlus"); - _profileChangedEvent.Subscribe(OnProfileChange, ProfileChanged.Priority.CustomizePlusIpc); _armatureChangedEvent.Subscribe(OnArmatureChanged, ArmatureChanged.Priority.CustomizePlusIpc); } public void Dispose() { - _profileChangedEvent.Unsubscribe(OnProfileChange); _armatureChangedEvent.Unsubscribe(OnArmatureChanged); } } diff --git a/CustomizePlus/Armatures/Data/Armature.cs b/CustomizePlus/Armatures/Data/Armature.cs index 479defe..6d5e87e 100644 --- a/CustomizePlus/Armatures/Data/Armature.cs +++ b/CustomizePlus/Armatures/Data/Armature.cs @@ -200,7 +200,7 @@ public unsafe class Armature _partialSkeletons = newPartials.Select(x => x.ToArray()).ToArray(); - RebuildBoneTemplateBinding(); + RebuildBoneTemplateBinding(); //todo: intentionally not calling ArmatureChanged.Type.Updated because this is pending rewrite Plugin.Logger.Debug($"Rebuilt {this}"); } @@ -237,7 +237,7 @@ public unsafe class Armature _partialSkeletons = oldPartials.Select(x => x.ToArray()).ToArray(); - RebuildBoneTemplateBinding(); + RebuildBoneTemplateBinding(); //todo: intentionally not calling ArmatureChanged.Type.Updated because this is pending rewrite Plugin.Logger.Debug($"Augmented {this} with new bones"); } diff --git a/CustomizePlus/Armatures/Events/ArmatureChanged.cs b/CustomizePlus/Armatures/Events/ArmatureChanged.cs index ae91d2c..c66fc1d 100644 --- a/CustomizePlus/Armatures/Events/ArmatureChanged.cs +++ b/CustomizePlus/Armatures/Events/ArmatureChanged.cs @@ -13,7 +13,10 @@ public sealed class ArmatureChanged() : EventWrapper + /// Called when armature was rebound to other profile or bone template bindings were rebuilt + /// + Updated } public enum Priority diff --git a/CustomizePlus/Armatures/Services/ArmatureManager.cs b/CustomizePlus/Armatures/Services/ArmatureManager.cs index 443cdec..ad305ca 100644 --- a/CustomizePlus/Armatures/Services/ArmatureManager.cs +++ b/CustomizePlus/Armatures/Services/ArmatureManager.cs @@ -163,28 +163,28 @@ public unsafe sealed class ArmatureManager : IDisposable if (armature.IsPendingProfileRebind) { - _logger.Debug($"Armature {armature} is pending profile rebind, rebinding..."); + _logger.Debug($"Armature {armature} is pending profile/bone rebind, rebinding..."); armature.IsPendingProfileRebind = false; var activeProfile = GetProfileForActor(actorIdentifier); - if (activeProfile == armature.Profile) - continue; - - if (activeProfile == null) + Profile? oldProfile = armature.Profile; + if (activeProfile != armature.Profile) { - _logger.Debug($"Removing armature {armature} because it doesn't have any active profiles"); - RemoveArmature(armature, ArmatureChanged.DeletionReason.NoActiveProfiles); - continue; + if (activeProfile == null) + { + _logger.Debug($"Removing armature {armature} because it doesn't have any active profiles"); + RemoveArmature(armature, ArmatureChanged.DeletionReason.NoActiveProfiles); + continue; + } + + armature.Profile.Armatures.Remove(armature); + armature.Profile = activeProfile; + activeProfile.Armatures.Add(armature); } - Profile oldProfile = armature.Profile; - - armature.Profile.Armatures.Remove(armature); - armature.Profile = activeProfile; - activeProfile.Armatures.Add(armature); armature.RebuildBoneTemplateBinding(); - _event.Invoke(ArmatureChanged.Type.Rebound, armature, activeProfile); + _event.Invoke(ArmatureChanged.Type.Updated, armature, (activeProfile, oldProfile)); } //Needed because skeleton sometimes appears to be not ready when armature is created @@ -379,7 +379,7 @@ public unsafe sealed class ArmatureManager : IDisposable if (!profile.Enabled || profile.Armatures.Count == 0) continue; - profile.Armatures.ForEach(x => x.RebuildBoneTemplateBinding()); + profile.Armatures.ForEach(x => x.IsPendingProfileRebind = true); } }); @@ -548,7 +548,7 @@ public unsafe sealed class ArmatureManager : IDisposable _logger.Debug($"ArmatureManager.OnProfileChange Added/Deleted/Moved/Changed template: {type}, data payload: {arg3?.ToString()}, profile: {profile.Name}->{profile.Enabled}->{profile.Armatures.Count} armatures"); - profile!.Armatures.ForEach(x => x.RebuildBoneTemplateBinding()); + profile!.Armatures.ForEach(x => x.IsPendingProfileRebind = true); } private IEnumerable GetArmaturesForCharacterName(string characterName) diff --git a/CustomizePlus/Profiles/Events/ProfileChanged.cs b/CustomizePlus/Profiles/Events/ProfileChanged.cs index 0655b2c..f8c1b81 100644 --- a/CustomizePlus/Profiles/Events/ProfileChanged.cs +++ b/CustomizePlus/Profiles/Events/ProfileChanged.cs @@ -41,6 +41,6 @@ public sealed class ProfileChanged() : EventWrapper /// Turn on editing of a specific template. If character name not set will default to local player. /// - internal bool EnableEditor(Template template, string? characterName = null) //todo: editor is borked + internal bool EnableEditor(Template template, string? characterName = null) { if (IsEditorActive || IsEditorPaused) return false; diff --git a/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfilePanel.cs b/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfilePanel.cs index f540652..cf406df 100644 --- a/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfilePanel.cs +++ b/CustomizePlus/UI/Windows/MainWindow/Tabs/Profiles/ProfilePanel.cs @@ -11,6 +11,7 @@ using CustomizePlus.Game.Services; using CustomizePlus.Configuration.Data; using CustomizePlus.Profiles.Data; using CustomizePlus.UI.Windows.Controls; +using CustomizePlus.Templates; namespace CustomizePlus.UI.Windows.MainWindow.Tabs.Profiles; @@ -20,6 +21,7 @@ public class ProfilePanel private readonly ProfileManager _manager; private readonly PluginConfiguration _configuration; private readonly TemplateCombo _templateCombo; + private readonly TemplateEditorManager _templateEditorManager; private string? _newName; private string? _newCharacterName; @@ -36,12 +38,14 @@ public class ProfilePanel ProfileFileSystemSelector selector, ProfileManager manager, PluginConfiguration configuration, - TemplateCombo templateCombo) + TemplateCombo templateCombo, + TemplateEditorManager templateEditorManager) { _selector = selector; _manager = manager; _configuration = configuration; _templateCombo = templateCombo; + _templateEditorManager = templateEditorManager; } public void Draw() @@ -139,8 +143,11 @@ public class ProfilePanel using (var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing)) { var enabled = _selector.Selected?.Enabled ?? false; - if (ImGui.Checkbox("##Enabled", ref enabled)) - _manager.SetEnabled(_selector.Selected!, enabled); + using (ImRaii.Disabled(_templateEditorManager.IsEditorActive || _templateEditorManager.IsEditorPaused)) + { + if (ImGui.Checkbox("##Enabled", ref enabled)) + _manager.SetEnabled(_selector.Selected!, enabled); + } ImGuiUtil.LabeledHelpMarker("Enabled", "Whether the templates in this profile should be applied at all. Only one profile can be enabled for a character at the same time.");