Move some stuff to shared things, improve some filesystem rename handling.

This commit is contained in:
Ottermandias 2023-07-05 16:13:11 +02:00
parent 8ea6893fc3
commit 00bc17c57a
11 changed files with 67 additions and 136 deletions

@ -1 +1 @@
Subproject commit adce3030c9dc125f2ebbaefbef6c756977c047c3 Subproject commit d43be3287a4782be091635e81ef2ec64849ba462

View file

@ -0,0 +1,44 @@
using System.Collections;
using System.Linq;
using Dalamud;
using Dalamud.Data;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Lumina.Excel.GeneratedSheets;
namespace Penumbra.GameData.Data;
public sealed class HumanModelList : DataSharer
{
public const string Tag = "HumanModels";
public const int CurrentVersion = 1;
private readonly BitArray _humanModels;
public HumanModelList(DalamudPluginInterface pluginInterface, DataManager gameData)
: base(pluginInterface, ClientLanguage.English, CurrentVersion)
{
_humanModels = TryCatchData(Tag, () => GetValidHumanModels(gameData));
}
public bool IsHuman(uint modelId)
=> modelId < _humanModels.Count && _humanModels[(int)modelId];
protected override void DisposeInternal()
{
DisposeTag(Tag);
}
/// <summary>
/// Go through all ModelChara rows and return a bitfield of those that resolve to human models.
/// </summary>
private static BitArray GetValidHumanModels(DataManager gameData)
{
var sheet = gameData.GetExcelSheet<ModelChara>()!;
var ret = new BitArray((int)sheet.RowCount, false);
foreach (var (_, idx) in sheet.Select((m, i) => (m, i)).Where(p => p.m.Type == (byte)CharacterBase.ModelType.Human))
ret[idx] = true;
return ret;
}
}

View file

@ -4,6 +4,7 @@ using System.Linq;
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Interface.Internal.Notifications; using Dalamud.Interface.Internal.Notifications;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using OtterGui.Custom;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.String; using Penumbra.String;

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using OtterGui.Custom;
using OtterGui.Filesystem; using OtterGui.Filesystem;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Services; using Penumbra.Services;

View file

@ -1,15 +1,11 @@
using System; using System;
using System.Collections;
using System.Linq;
using Dalamud.Data;
using Dalamud.Game.ClientState; using Dalamud.Game.ClientState;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Lumina.Excel.GeneratedSheets;
using OtterGui;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.Services; using Penumbra.Services;
using Penumbra.Util; using Penumbra.Util;
@ -23,7 +19,7 @@ public unsafe class CollectionResolver
{ {
private readonly PerformanceTracker _performance; private readonly PerformanceTracker _performance;
private readonly IdentifiedCollectionCache _cache; private readonly IdentifiedCollectionCache _cache;
private readonly BitArray _validHumanModels; private readonly HumanModelList _humanModels;
private readonly ClientState _clientState; private readonly ClientState _clientState;
private readonly GameGui _gameGui; private readonly GameGui _gameGui;
@ -36,8 +32,8 @@ public unsafe class CollectionResolver
private readonly DrawObjectState _drawObjectState; private readonly DrawObjectState _drawObjectState;
public CollectionResolver(PerformanceTracker performance, IdentifiedCollectionCache cache, ClientState clientState, GameGui gameGui, public CollectionResolver(PerformanceTracker performance, IdentifiedCollectionCache cache, ClientState clientState, GameGui gameGui,
DataManager gameData, ActorService actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager, ActorService actors, CutsceneService cutscenes, Configuration config, CollectionManager collectionManager,
TempCollectionManager tempCollections, DrawObjectState drawObjectState) TempCollectionManager tempCollections, DrawObjectState drawObjectState, HumanModelList humanModels)
{ {
_performance = performance; _performance = performance;
_cache = cache; _cache = cache;
@ -49,7 +45,7 @@ public unsafe class CollectionResolver
_collectionManager = collectionManager; _collectionManager = collectionManager;
_tempCollections = tempCollections; _tempCollections = tempCollections;
_drawObjectState = drawObjectState; _drawObjectState = drawObjectState;
_validHumanModels = GetValidHumanModels(gameData); _humanModels = humanModels;
} }
/// <summary> /// <summary>
@ -115,7 +111,7 @@ public unsafe class CollectionResolver
/// <summary> Return whether the given ModelChara id refers to a human-type model. </summary> /// <summary> Return whether the given ModelChara id refers to a human-type model. </summary>
public bool IsModelHuman(uint modelCharaId) public bool IsModelHuman(uint modelCharaId)
=> modelCharaId < _validHumanModels.Length && _validHumanModels[(int)modelCharaId]; => _humanModels.IsHuman(modelCharaId);
/// <summary> Return whether the given character has a human model. </summary> /// <summary> Return whether the given character has a human model. </summary>
public bool IsModelHuman(Character* character) public bool IsModelHuman(Character* character)
@ -254,17 +250,4 @@ public unsafe class CollectionResolver
return CheckYourself(id, owner) return CheckYourself(id, owner)
?? CollectionByAttributes(owner, ref notYetReady); ?? CollectionByAttributes(owner, ref notYetReady);
} }
/// <summary>
/// Go through all ModelChara rows and return a bitfield of those that resolve to human models.
/// </summary>
private static BitArray GetValidHumanModels(DataManager gameData)
{
var sheet = gameData.GetExcelSheet<ModelChara>()!;
var ret = new BitArray((int)sheet.RowCount, false);
foreach (var (_, idx) in sheet.WithIndex().Where(p => p.Value.Type == (byte)CharacterBase.ModelType.Human))
ret[idx] = true;
return ret;
}
} }

View file

@ -4,7 +4,7 @@ using Dalamud.Data;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
using FFXIVClientStructs.FFXIV.Client.Game.Character; using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Game.Object; using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData; using OtterGui.Custom;
using Penumbra.GameData.Actors; using Penumbra.GameData.Actors;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Penumbra.Services; using Penumbra.Services;

View file

@ -83,12 +83,12 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable
// Update sort order when defaulted mod names change. // Update sort order when defaulted mod names change.
private void OnDataChange(ModDataChangeType type, Mod mod, string? oldName) private void OnDataChange(ModDataChangeType type, Mod mod, string? oldName)
{ {
if (type.HasFlag(ModDataChangeType.Name) && oldName != null) if (!type.HasFlag(ModDataChangeType.Name) || oldName == null || !FindLeaf(mod, out var leaf))
{ return;
var old = oldName.FixName();
if (Find(old, out var child) && child is not Folder) var old = oldName.FixName();
Rename(child, mod.Name.Text); if (old == leaf.Name || leaf.Name.IsDuplicateName(out var baseName, out _) && baseName == old)
} RenameWithDuplicates(leaf, mod.Name.Text);
} }
// Update the filesystem if a mod has been added or removed. // Update the filesystem if a mod has been added or removed.
@ -98,13 +98,7 @@ public sealed class ModFileSystem : FileSystem<Mod>, IDisposable, ISavable
switch (type) switch (type)
{ {
case ModPathChangeType.Added: case ModPathChangeType.Added:
var originalName = mod.Name.Text.FixName(); CreateDuplicateLeaf(Root, mod.Name.Text, mod);
var name = originalName;
var counter = 1;
while (Find(name, out _))
name = $"{originalName} ({++counter})";
CreateLeaf(Root, name, mod);
break; break;
case ModPathChangeType.Deleted: case ModPathChangeType.Deleted:
if (FindLeaf(mod, out var leaf)) if (FindLeaf(mod, out var leaf))

View file

@ -1,4 +1,3 @@
using System.Collections.Concurrent;
using Dalamud.Plugin; using Dalamud.Plugin;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes; using OtterGui.Classes;
@ -18,7 +17,7 @@ using Penumbra.Mods.Editor;
using Penumbra.Mods.Manager; using Penumbra.Mods.Manager;
using Penumbra.UI; using Penumbra.UI;
using Penumbra.UI.AdvancedWindow; using Penumbra.UI.AdvancedWindow;
using Penumbra.UI.Classes; using Penumbra.UI.Classes;
using Penumbra.UI.ModsTab; using Penumbra.UI.ModsTab;
using Penumbra.UI.Tabs; using Penumbra.UI.Tabs;
@ -70,7 +69,8 @@ public static class ServiceManager
.AddSingleton<IdentifierService>() .AddSingleton<IdentifierService>()
.AddSingleton<StainService>() .AddSingleton<StainService>()
.AddSingleton<ItemService>() .AddSingleton<ItemService>()
.AddSingleton<ActorService>(); .AddSingleton<ActorService>()
.AddSingleton<HumanModelList>();
private static IServiceCollection AddInterop(this IServiceCollection services) private static IServiceCollection AddInterop(this IServiceCollection services)
=> services.AddSingleton<GameEventManager>() => services.AddSingleton<GameEventManager>()

View file

@ -2,7 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Enums;
using ImGuiNET; using ImGuiNET;
using OtterGui.Raii; using OtterGui.Custom;
using Penumbra.Collections; using Penumbra.Collections;
using Penumbra.Collections.Manager; using Penumbra.Collections.Manager;
using Penumbra.Communication; using Penumbra.Communication;
@ -63,22 +63,8 @@ public class IndividualAssignmentUi : IDisposable
public void DrawObjectKindCombo(float width) public void DrawObjectKindCombo(float width)
{ {
if (!_ready) if (_ready && IndividualHelpers.DrawObjectKindCombo(width, _newKind, out _newKind, ObjectKinds))
return;
ImGui.SetNextItemWidth(width);
using var combo = ImRaii.Combo("##newKind", _newKind.ToName());
if (!combo)
return;
foreach (var kind in ObjectKinds)
{
if (!ImGui.Selectable(kind.ToName(), _newKind == kind))
continue;
_newKind = kind;
UpdateIdentifiersInternal(); UpdateIdentifiersInternal();
}
} }
public void DrawNewPlayerCollection(float width) public void DrawNewPlayerCollection(float width)

View file

@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Utility;
using ImGuiNET;
using OtterGui.Widgets;
namespace Penumbra.UI.CollectionTab;
public sealed class NpcCombo : FilterComboCache<(string Name, uint[] Ids)>
{
private readonly string _label;
public NpcCombo(string label, IReadOnlyDictionary<uint, string> names)
: base(() => names.GroupBy(kvp => kvp.Value).Select(g => (g.Key, g.Select(g => g.Key).ToArray())).OrderBy(g => g.Key, Comparer)
.ToList())
=> _label = label;
protected override string ToString((string Name, uint[] Ids) obj)
=> obj.Name;
protected override bool DrawSelectable(int globalIdx, bool selected)
{
var (name, ids) = Items[globalIdx];
var ret = ImGui.Selectable(name, selected);
if (ImGui.IsItemHovered())
ImGui.SetTooltip(string.Join('\n', ids.Select(i => i.ToString())));
return ret;
}
public bool Draw(float width)
=> Draw(_label, CurrentSelection.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
/// <summary> Compare strings in a way that letters and numbers are sorted before any special symbols. </summary>
private class NameComparer : IComparer<string>
{
public int Compare(string? x, string? y)
{
if (x.IsNullOrEmpty() || y.IsNullOrEmpty())
return StringComparer.OrdinalIgnoreCase.Compare(x, y);
return (char.IsAsciiLetterOrDigit(x[0]), char.IsAsciiLetterOrDigit(y[0])) switch
{
(true, false) => -1,
(false, true) => 1,
_ => StringComparer.OrdinalIgnoreCase.Compare(x, y),
};
}
}
private static readonly NameComparer Comparer = new();
}

View file

@ -1,24 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using ImGuiNET;
using OtterGui.Widgets;
namespace Penumbra.UI.CollectionTab;
public sealed class WorldCombo : FilterComboCache<KeyValuePair<ushort, string>>
{
private static readonly KeyValuePair<ushort, string> AllWorldPair = new(ushort.MaxValue, "Any World");
public WorldCombo(IReadOnlyDictionary<ushort, string> worlds)
: base(worlds.OrderBy(kvp => kvp.Value).Prepend(AllWorldPair))
{
CurrentSelection = AllWorldPair;
CurrentSelectionIdx = 0;
}
protected override string ToString(KeyValuePair<ushort, string> obj)
=> obj.Value;
public bool Draw(float width)
=> Draw("##worldCombo", CurrentSelection.Value, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
}