Compare commits

..

No commits in common. "main" and "testing_1.5.0.9" have entirely different histories.

25 changed files with 52 additions and 176 deletions

@ -1 +1 @@
Subproject commit 59a7ab5fa9941eb754757b62e4cb189e455e9514
Subproject commit 54c1944dc7db704733b4788520e494761bb0b58e

View file

@ -1,13 +1,13 @@
using Glamourer.Api.Enums;
using Glamourer.Designs;
using Glamourer.State;
using OtterGui;
using OtterGui.Extensions;
using OtterGui.Log;
using OtterGui.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using Penumbra.String;
namespace Glamourer.Api;
@ -15,23 +15,14 @@ namespace Glamourer.Api;
public class ApiHelpers(ActorObjectManager objects, StateManager stateManager, ActorManager actors) : IApiService
{
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
internal IEnumerable<ActorState> FindExistingStates(string actorName, ushort worldId = ushort.MaxValue)
internal IEnumerable<ActorState> FindExistingStates(string actorName)
{
if (actorName.Length == 0 || !ByteString.FromString(actorName, out var byteString))
yield break;
if (worldId == WorldId.AnyWorld.Id)
{
foreach (var state in stateManager.Values.Where(state
=> state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString))
yield return state;
}
else
{
var identifier = actors.CreatePlayer(byteString, worldId);
if (stateManager.TryGetValue(identifier, out var state))
yield return state;
}
foreach (var state in stateManager.Values.Where(state
=> state.Identifier.Type is IdentifierType.Player && state.Identifier.PlayerName == byteString))
yield return state;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]

View file

@ -6,7 +6,7 @@ namespace Glamourer.Api;
public class GlamourerApi(DesignsApi designs, StateApi state, ItemsApi items) : IGlamourerApi, IApiService
{
public const int CurrentApiVersionMajor = 1;
public const int CurrentApiVersionMinor = 7;
public const int CurrentApiVersionMinor = 6;
public (int Major, int Minor) ApiVersion
=> (CurrentApiVersionMajor, CurrentApiVersionMinor);

View file

@ -54,7 +54,6 @@ public sealed class IpcProviders : IDisposable, IApiService
IpcSubscribers.RevertStateName.Provider(pi, api.State),
IpcSubscribers.UnlockState.Provider(pi, api.State),
IpcSubscribers.UnlockStateName.Provider(pi, api.State),
IpcSubscribers.DeletePlayerState.Provider(pi, api.State),
IpcSubscribers.UnlockAll.Provider(pi, api.State),
IpcSubscribers.RevertToAutomation.Provider(pi, api.State),
IpcSubscribers.RevertToAutomationName.Provider(pi, api.State),

View file

@ -8,7 +8,6 @@ using Glamourer.State;
using Newtonsoft.Json.Linq;
using OtterGui.Services;
using Penumbra.GameData.Interop;
using Penumbra.GameData.Structs;
using StateChanged = Glamourer.Events.StateChanged;
namespace Glamourer.Api;
@ -18,6 +17,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
private readonly ApiHelpers _helpers;
private readonly StateManager _stateManager;
private readonly DesignConverter _converter;
private readonly Configuration _config;
private readonly AutoDesignApplier _autoDesigns;
private readonly ActorObjectManager _objects;
private readonly StateChanged _stateChanged;
@ -27,6 +27,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
public StateApi(ApiHelpers helpers,
StateManager stateManager,
DesignConverter converter,
Configuration config,
AutoDesignApplier autoDesigns,
ActorObjectManager objects,
StateChanged stateChanged,
@ -36,6 +37,7 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
_helpers = helpers;
_stateManager = stateManager;
_converter = converter;
_config = config;
_autoDesigns = autoDesigns;
_objects = objects;
_stateChanged = stateChanged;
@ -200,27 +202,6 @@ public sealed class StateApi : IGlamourerApiState, IApiService, IDisposable
return ApiHelpers.Return(GlamourerApiEc.Success, args);
}
public GlamourerApiEc DeletePlayerState(string playerName, ushort worldId, uint key)
{
var args = ApiHelpers.Args("Name", playerName, "World", worldId, "Key", key);
var states = _helpers.FindExistingStates(playerName).ToList();
if (states.Count is 0)
return ApiHelpers.Return(GlamourerApiEc.NothingDone, args);
var anyLocked = false;
foreach (var state in states)
{
if (state.CanUnlock(key))
_stateManager.DeleteState(state.Identifier);
else
anyLocked = true;
}
return ApiHelpers.Return(anyLocked
? GlamourerApiEc.InvalidKey
: GlamourerApiEc.Success, args);
}
public int UnlockAll(uint key)
=> _stateManager.Values.Count(state => state.Unlock(key));

View file

@ -100,7 +100,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
public new JObject JsonSerialize()
{
var ret = new JObject
var ret = new JObject()
{
["FileVersion"] = FileVersion,
["Identifier"] = Identifier,
@ -131,17 +131,12 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
var ret = new JArray();
foreach (var (mod, settings) in AssociatedMods)
{
var obj = new JObject
var obj = new JObject()
{
["Name"] = mod.Name,
["Directory"] = mod.DirectoryName,
["Enabled"] = settings.Enabled,
};
if (settings.Remove)
obj["Remove"] = true;
else if (settings.ForceInherit)
obj["Inherit"] = true;
else
obj["Enabled"] = settings.Enabled;
if (settings.Enabled)
{
obj["Priority"] = settings.Priority;

View file

@ -6,13 +6,12 @@ using Glamourer.GameData;
using Glamourer.Interop.Material;
using Glamourer.Interop.Penumbra;
using Glamourer.Services;
using OtterGui.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui.Extensions;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
namespace Glamourer.Designs;
public sealed class DesignManager : DesignEditor
@ -229,7 +228,7 @@ public sealed class DesignManager : DesignEditor
design.Tags = design.Tags.Append(tag).OrderBy(t => t).ToArray();
design.LastEdit = DateTimeOffset.UtcNow;
var idx = design.Tags.AsEnumerable().IndexOf(tag);
var idx = design.Tags.IndexOf(tag);
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Added tag {tag} at {idx} to design {design.Identifier}.");
DesignChanged.Invoke(DesignChanged.Type.AddedTag, design, new TagAddedTransaction(tag, idx));
@ -262,7 +261,7 @@ public sealed class DesignManager : DesignEditor
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Renamed tag {oldTag} at {tagIdx} to {newTag} in design {design.Identifier} and reordered tags.");
DesignChanged.Invoke(DesignChanged.Type.ChangedTag, design,
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.AsEnumerable().IndexOf(newTag)));
new TagChangedTransaction(oldTag, newTag, tagIdx, design.Tags.IndexOf(newTag)));
}
/// <summary> Add an associated mod to a design. </summary>
@ -557,7 +556,7 @@ public sealed class DesignManager : DesignEditor
try
{
File.Move(SaveService.FileNames.MigrationDesignFile,
Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"), true);
Path.ChangeExtension(SaveService.FileNames.MigrationDesignFile, ".json.bak"));
Glamourer.Log.Information($"Moved migrated design file {SaveService.FileNames.MigrationDesignFile} to backup file.");
}
catch (Exception ex)

View file

@ -1,4 +1,4 @@
<Project Sdk="Dalamud.NET.Sdk/13.1.0">
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
<PropertyGroup>
<RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer</AssemblyName>

View file

@ -44,7 +44,6 @@ public class GlamourerChangelog
Add1_3_8_0(Changelog);
Add1_4_0_0(Changelog);
Add1_5_0_0(Changelog);
Add1_5_1_0(Changelog);
}
private (int, ChangeLogDisplayType) ConfigData()
@ -65,16 +64,6 @@ public class GlamourerChangelog
}
}
private static void Add1_5_1_0(Changelog log)
=> log.NextVersion("Version 1.5.1.0")
.RegisterHighlight("Added support for Penumbras PCP functionality to add the current state of the character as a design.")
.RegisterEntry("On import, a design for the PCP is created and, if possible, applied to the character.", 1)
.RegisterEntry("No automation is assigned.", 1)
.RegisterEntry("Finer control about this can be found in the settings.", 1)
.RegisterEntry("Fixed an issue with static visors not toggling through Glamourer (1.5.0.7).")
.RegisterEntry("The advanced dye slot combo now contains glasses (1.5.0.7).")
.RegisterEntry("Several fixes for patch-related issues (1.5.0.1 - 1.5.0.6");
private static void Add1_5_0_0(Changelog log)
=> log.NextVersion("Version 1.5.0.0")
.RegisterImportant("Updated for game version 7.30 and Dalamud API13, which uses a new GUI backend. Some things may not work as expected. Please let me know any issues you encounter.")

View file

@ -22,12 +22,11 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
private readonly CustomizeService _customize;
private readonly GPoseService _gpose;
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2 + BonusExtensions.AllFlags.Count];
private readonly EquipItem[] _lastItems = new EquipItem[EquipFlagExtensions.NumEquipFlags / 2];
public IEnumerable<KeyValuePair<object, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Cast<object>().Append(EquipSlot.MainHand).Append(EquipSlot.OffHand)
.Concat(BonusExtensions.AllFlags.Cast<object>()).Zip(_lastItems)
.Select(p => new KeyValuePair<object, EquipItem>(p.First, p.Second));
public IEnumerable<KeyValuePair<EquipSlot, EquipItem>> LastItems
=> EquipSlotExtensions.EqdpSlots.Append(EquipSlot.MainHand).Append(EquipSlot.OffHand).Zip(_lastItems)
.Select(p => new KeyValuePair<EquipSlot, EquipItem>(p.First, p.Second));
public ChangedItemType LastType { get; private set; } = ChangedItemType.None;
public uint LastId { get; private set; }
@ -73,21 +72,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
if (!Player())
return;
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
using (_ = !openTooltip ? null : ImRaii.Tooltip())
{
ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor.");
if (glasses.Valid)
ImGui.TextUnformatted($"{prefix}Control + Right-Click to re-apply {glasses.Name} to current actor.");
}
return;
}
var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()];
switch (slot)
@ -125,27 +109,6 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
public void ApplyItem(ActorState state, EquipItem item)
{
var bonusSlot = item.Type.ToBonus();
if (bonusSlot is not BonusItemFlag.Unknown)
{
// + 2 due to weapons.
var glasses = _lastItems[bonusSlot.ToSlot() + 2];
if (ImGui.GetIO().KeyCtrl && glasses.Valid)
{
Glamourer.Log.Debug($"Re-Applying {glasses.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, default, state);
_stateManager.ChangeBonusItem(state, bonusSlot, glasses, ApplySettings.Manual);
}
else
{
Glamourer.Log.Debug($"Applying {item.Name} to {bonusSlot.ToName()}.");
SetLastItem(bonusSlot, item, state);
_stateManager.ChangeBonusItem(state, bonusSlot, item, ApplySettings.Manual);
}
return;
}
var slot = item.Type.ToSlot();
var last = _lastItems[slot.ToIndex()];
switch (slot)
@ -302,22 +265,7 @@ public sealed class PenumbraChangedItemTooltip : IDisposable
{
var oldItem = state.ModelData.Item(slot);
if (oldItem.Id != item.Id)
last = oldItem;
}
}
private void SetLastItem(BonusItemFlag slot, EquipItem item, ActorState state)
{
ref var last = ref _lastItems[slot.ToSlot() + 2];
if (!item.Valid)
{
last = default;
}
else
{
var oldItem = state.ModelData.BonusItem(slot);
if (oldItem.Id != item.Id)
last = oldItem;
_lastItems[slot.ToIndex()] = oldItem;
}
}

View file

@ -89,13 +89,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.Separator();
foreach (var (slot, item) in _penumbraTooltip.LastItems)
{
switch (slot)
{
case EquipSlot e: ImGuiUtil.DrawTableColumn($"{e.ToName()} Revert-Item"); break;
case BonusItemFlag f: ImGuiUtil.DrawTableColumn($"{f.ToName()} Revert-Item"); break;
default: ImGuiUtil.DrawTableColumn("Unk Revert-Item"); break;
}
ImGuiUtil.DrawTableColumn($"{slot.ToName()} Revert-Item");
ImGuiUtil.DrawTableColumn(item.Valid ? item.Name : "None");
ImGui.TableNextColumn();
}

View file

@ -71,8 +71,6 @@ public class ModAssociationsTab(PenumbraService penumbra, DesignFileSystemSelect
private void DrawApplyAllButton()
{
var (id, name) = penumbra.CurrentCollection;
if (config.Ephemeral.IncognitoMode)
name = id.ShortGuid();
if (ImGuiUtil.DrawDisabledButton($"Try Applying All Associated Mods to {name}##applyAll",
new Vector2(ImGui.GetContentRegionAvail().X, 0), string.Empty, id == Guid.Empty))
ApplyAll();

View file

@ -490,7 +490,7 @@ public class MultiDesignPanel(
foreach (var leaf in selector.SelectedPaths.OfType<DesignFileSystem.Leaf>())
{
var index = leaf.Value.Tags.AsEnumerable().IndexOf(_tag);
var index = leaf.Value.Tags.IndexOf(_tag);
if (index >= 0)
_removeDesigns.Add((leaf.Value, index));
else

View file

@ -62,7 +62,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
var drawData = type switch
{
MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, (HumanSlot)slotId),
MaterialValueIndex.DrawObjectType.Human => GetTempSlot((Human*)characterBase, slotId),
_ => GetTempSlot((Weapon*)characterBase),
};
var mode = PrepareColorSet.GetMode(material);
@ -192,24 +192,13 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
}
/// <summary> We need to get the temporary set, variant and stain that is currently being set if it is available. </summary>
private static CharacterWeapon GetTempSlot(Human* human, HumanSlot slotId)
private static CharacterWeapon GetTempSlot(Human* human, byte slotId)
{
if (human->ChangedEquipData is null)
return slotId.ToSpecificEnum() switch
{
EquipSlot slot => ((Model)human).GetArmor(slot).ToWeapon(0),
BonusItemFlag bonus => ((Model)human).GetBonus(bonus).ToWeapon(0),
_ => default,
};
if (human->ChangedEquipData == null)
return ((Model)human).GetArmor(((uint)slotId).ToEquipSlot()).ToWeapon(0);
if (!slotId.ToSlotIndex(out var index))
return default;
var item = (ChangedEquipData*)human->ChangedEquipData + index;
if (index < 10)
return ((CharacterArmor*)item)->ToWeapon(0);
return new CharacterWeapon(item->BonusModel, 0, item->BonusVariant, StainIds.None);
var item = (ChangedEquipData*)human->ChangedEquipData + slotId;
return ((CharacterArmor*)item)->ToWeapon(0);
}
/// <summary>

View file

@ -1,5 +1,6 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
using Lumina.Data.Files;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Interop;
using Texture = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Texture;
@ -68,9 +69,9 @@ public static unsafe class MaterialService
return null;
var material = (MaterialResourceHandle*) model.AsCharacterBase->MaterialsSpan[index].Value;
if (material == null || material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable)
if (material == null || material->ColorTable == null)
return null;
return (ColorTable.Table*)material->DataSet;
return (ColorTable.Table*)material->ColorTable;
}
}

View file

@ -69,13 +69,13 @@ public sealed unsafe class PrepareColorSet
public static bool TryGetColorTable(MaterialResourceHandle* material, StainIds stainIds,
out ColorTable.Table table)
{
if (material->DataSet == null || material->DataSetSize < sizeof(ColorTable.Table) || !material->HasColorTable)
if (material->ColorTable == null)
{
table = default;
return false;
}
var newTable = *(ColorTable.Table*)material->DataSet;
var newTable = *(ColorTable.Table*)material->ColorTable;
if (GetDyeTable(material, out var dyeTable))
{
if (stainIds.Stain1.Id != 0)

View file

@ -1,10 +1,9 @@
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Services;
namespace Glamourer.Services;
public class BackupService : IAsyncService
public class BackupService
{
private readonly Logger _logger;
private readonly DirectoryInfo _configDirectory;
@ -15,7 +14,7 @@ public class BackupService : IAsyncService
_logger = logger;
_fileNames = GlamourerFiles(fileNames);
_configDirectory = new DirectoryInfo(fileNames.ConfigDirectory);
Awaiter = Task.Run(() => Backup.CreateAutomaticBackup(logger, new DirectoryInfo(fileNames.ConfigDirectory), _fileNames));
Backup.CreateAutomaticBackup(logger, _configDirectory, _fileNames);
}
/// <summary> Create a permanent backup with a given name for migrations. </summary>
@ -41,9 +40,4 @@ public class BackupService : IAsyncService
return list;
}
public Task Awaiter { get; }
public bool Finished
=> Awaiter.IsCompletedSuccessfully;
}

View file

@ -50,8 +50,7 @@ public class CodeService
| CodeFlag.OopsMiqote
| CodeFlag.OopsRoegadyn
| CodeFlag.OopsAuRa
| CodeFlag.OopsHrothgar
| CodeFlag.OopsViera;
| CodeFlag.OopsHrothgar;
public const CodeFlag FullCodes = CodeFlag.Face | CodeFlag.Manderville | CodeFlag.Smiles;
@ -251,4 +250,3 @@ public class CodeService
_ => (false, 0, string.Empty, string.Empty, string.Empty),
};
}

View file

@ -385,7 +385,7 @@ public class StateApplier(
var actors = ChangeMetaState(state, MetaIndex.Wetness, true);
if (redraw)
{
if (withLock && actors.Valid)
if (withLock)
state.TempLock();
ForceRedraw(actors);
}

View file

@ -4,9 +4,9 @@
"net9.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[13.1.0, )",
"resolved": "13.1.0",
"contentHash": "XdoNhJGyFby5M/sdcRhnc5xTop9PHy+H50PTWpzLhJugjB19EDBiHD/AsiDF66RETM+0qKUdJBZrNuebn7qswQ=="
"requested": "[13.0.0, )",
"resolved": "13.0.0",
"contentHash": "Mb3cUDSK/vDPQ8gQIeuCw03EMYrej1B4J44a1AvIJ9C759p9XeqdU9Hg4WgOmlnlPe0G7ILTD32PKSUpkQNa8w=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",

@ -1 +1 @@
Subproject commit 1459e2b8f5e1687f659836709e23571235d4206c
Subproject commit 4a9b71a93e76aa5eed818542288329e34ec0dd89

@ -1 +1 @@
Subproject commit c23ee05c1e9fa103eaa52e6aa7e855ef568ee669
Subproject commit 297941bc22300f4a8368f4d0177f62943eb69727

@ -1 +1 @@
Subproject commit d889f9ef918514a46049725052d378b441915b00
Subproject commit 15e7c8eb41867e6bbd3fe6a8885404df087bc7e7

@ -1 +1 @@
Subproject commit c8611a0c546b6b2ec29214ab319fc2c38fe74793
Subproject commit 878acce46e286867d6ef1f8ecedb390f7bac34fd

View file

@ -17,8 +17,8 @@
"Character"
],
"InternalName": "Glamourer",
"AssemblyVersion": "1.5.1.5",
"TestingAssemblyVersion": "1.5.1.5",
"AssemblyVersion": "1.5.0.7",
"TestingAssemblyVersion": "1.5.0.8",
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
"ApplicableVersion": "any",
"DalamudApiLevel": 13,
@ -27,9 +27,9 @@
"IsTestingExclusive": "False",
"DownloadCount": 1,
"LastUpdate": 1618608322,
"DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.5/Glamourer.zip",
"DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.5/Glamourer.zip",
"DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.1.5/Glamourer.zip",
"DownloadLinkInstall": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.0.7/Glamourer.zip",
"DownloadLinkUpdate": "https://github.com/Ottermandias/Glamourer/releases/download/1.5.0.7/Glamourer.zip",
"DownloadLinkTesting": "https://github.com/Ottermandias/Glamourer/releases/download/testing_1.5.0.8/Glamourer.zip",
"IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/main/images/icon.png"
}
]