Update to Lumina 5 (new Excel parsing) (#2022)

* Refactor and upgrade to new excel design

* Obsolete ExcelResolver<T> and use only RowRef<T>

* Better benchmarking for Lumina

* Add custom game-supported RSV provider

* Refactor and move Lazy<T> and nullable/cached row objects to RowRefs

* Convert IRSVProvider to delegate, resolve strings by default

* Split IExcelRow into IExcelSubrow

* Extra lumina documentation

* Minor RSV CS fixes

* Fix UIGlowPayload warning

* Fix rebase

* Update to Lumina 5
This commit is contained in:
Asriel Camora 2024-10-20 19:59:03 -07:00 committed by GitHub
parent 08d8605871
commit 0b9af0e3f4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
49 changed files with 460 additions and 403 deletions

View file

@ -5,7 +5,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.Text;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets2;
using Lumina.Excel.Sheets;
using Lumina.Text.Expressions;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;

View file

@ -18,7 +18,7 @@ using FFXIVClientStructs.FFXIV.Client.UI;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets2;
using Lumina.Excel.Sheets;
using Lumina.Text.Parse;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;

View file

@ -1,4 +1,4 @@
using Dalamud.Game.ClientState.Aetherytes;
using Dalamud.Game.ClientState.Aetherytes;
using ImGuiNET;
@ -56,7 +56,7 @@ internal class AetherytesWidget : IDataWindowWidget
ImGui.TextUnformatted($"{i}");
ImGui.TableNextColumn(); // Name
ImGui.TextUnformatted($"{info.AetheryteData.GameData?.PlaceName.Value?.Name}");
ImGui.TextUnformatted($"{info.AetheryteData.ValueNullable?.PlaceName.ValueNullable?.Name}");
ImGui.TableNextColumn(); // ID
ImGui.TextUnformatted($"{info.AetheryteId}");

View file

@ -1,4 +1,4 @@
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.JobGauge;
using Dalamud.Game.ClientState.JobGauge.Types;
using Dalamud.Utility;
@ -39,7 +39,7 @@ internal class GaugeWidget : IDataWindowWidget
return;
}
var jobID = player.ClassJob.Id;
var jobID = player.ClassJob.RowId;
JobGaugeBase? gauge = jobID switch
{
19 => jobGauges.Get<PLDGauge>(),

View file

@ -1,4 +1,4 @@
using System.Numerics;
using System.Numerics;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects;
@ -56,8 +56,8 @@ internal class ObjectTableWidget : IDataWindowWidget
{
stateString += $"ObjectTableLen: {objectTable.Length}\n";
stateString += $"LocalPlayerName: {clientState.LocalPlayer.Name}\n";
stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.GameData?.Name : clientState.LocalPlayer.CurrentWorld.Id.ToString())}\n";
stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.GameData?.Name : clientState.LocalPlayer.HomeWorld.Id.ToString())}\n";
stateString += $"CurrentWorldName: {(this.resolveGameData ? clientState.LocalPlayer.CurrentWorld.ValueNullable?.Name : clientState.LocalPlayer.CurrentWorld.RowId.ToString())}\n";
stateString += $"HomeWorldName: {(this.resolveGameData ? clientState.LocalPlayer.HomeWorld.ValueNullable?.Name : clientState.LocalPlayer.HomeWorld.RowId.ToString())}\n";
stateString += $"LocalCID: {clientState.LocalContentId:X}\n";
stateString += $"LastLinkedItem: {chatGui.LastLinkedItemId}\n";
stateString += $"TerritoryType: {clientState.TerritoryType}\n\n";

View file

@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using System.Numerics;
using System.Text;
@ -16,7 +16,8 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets2;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using Lumina.Text;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
@ -33,7 +34,7 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
private static readonly string[] ThemeNames = ["Dark", "Light", "Classic FF", "Clear Blue"];
private ImVectorWrapper<byte> testStringBuffer;
private string testString = string.Empty;
private Addon[]? addons;
private ExcelSheet<Addon> addons;
private ReadOnlySeString? logkind;
private SeStringDrawParams style;
private bool interactable;
@ -53,7 +54,7 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
public void Load()
{
this.style = new() { GetEntity = this.GetEntity };
this.addons = null;
this.addons = Service<DataManager>.Get().GetExcelSheet<Addon>();
this.logkind = null;
this.testString = string.Empty;
this.interactable = this.useEntity = true;
@ -155,9 +156,9 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
if (this.logkind is null)
{
var tt = new SeStringBuilder();
foreach (var uc in Service<DataManager>.Get().GetExcelSheet<LogKind>()!)
foreach (var uc in Service<DataManager>.Get().GetExcelSheet<LogKind>())
{
var ucsp = uc.Format.AsReadOnly().AsSpan();
var ucsp = uc.Format.AsSpan();
if (ucsp.IsEmpty)
continue;
@ -184,7 +185,6 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
if (ImGui.CollapsingHeader("Addon Table"))
{
this.addons ??= Service<DataManager>.Get().GetExcelSheet<Addon>()!.ToArray();
if (ImGui.BeginTable("Addon Sheet", 3))
{
ImGui.TableSetupScrollFreeze(0, 1);
@ -197,25 +197,27 @@ internal unsafe class SeStringRendererTestWidget : IDataWindowWidget
ImGui.TableHeadersRow();
var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
clipper.Begin(this.addons.Length);
clipper.Begin(this.addons.Count);
while (clipper.Step())
{
for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
var row = this.addons.GetRowAt(i);
ImGui.TableNextRow();
ImGui.PushID(i);
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.TextUnformatted($"{this.addons[i].RowId}");
ImGui.TextUnformatted($"{row.RowId}");
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGuiHelpers.SeStringWrapped(this.addons[i].Text.AsReadOnly(), this.style);
ImGuiHelpers.SeStringWrapped(row.Text, this.style);
ImGui.TableNextColumn();
if (ImGui.Button("Print to Chat"))
Service<ChatGui>.Get().Print(this.addons[i].Text.ToDalamudString());
Service<ChatGui>.Get().Print(row.Text.ToDalamudString());
ImGui.PopID();
}

View file

@ -1,4 +1,4 @@
using System.Buffers.Binary;
using System.Buffers.Binary;
using System.Linq;
using System.Numerics;
using System.Text;
@ -12,7 +12,8 @@ using Dalamud.Storage.Assets;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel;
using Lumina.Excel.Sheets;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@ -21,7 +22,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary>
internal class UiColorWidget : IDataWindowWidget
{
private UIColor[]? colors;
private ExcelSheet<UIColor> colors;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["uicolor"];
@ -36,15 +37,12 @@ internal class UiColorWidget : IDataWindowWidget
public void Load()
{
this.Ready = true;
this.colors = null;
this.colors = Service<DataManager>.Get().GetExcelSheet<UIColor>();
}
/// <inheritdoc/>
public unsafe void Draw()
{
this.colors ??= Service<DataManager>.Get().GetExcelSheet<UIColor>()?.ToArray();
if (this.colors is null) return;
Service<SeStringRenderer>.Get().CompileAndDrawWrapped(
"· Color notation is #" +
"<edgecolor(0xFFEEEE)><color(0xFF0000)>RR<color(stackcolor)><edgecolor(stackcolor)>" +
@ -73,12 +71,24 @@ internal class UiColorWidget : IDataWindowWidget
ImGui.TableHeadersRow();
var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
clipper.Begin(this.colors.Length, ImGui.GetFrameHeightWithSpacing());
clipper.Begin(this.colors.Count, ImGui.GetFrameHeightWithSpacing());
while (clipper.Step())
{
for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
{
var id = this.colors[i].RowId;
var row = this.colors.GetRowAt(i);
UIColor? adjacentRow = null;
if (i + 1 < this.colors.Count)
{
var adjRow = this.colors.GetRowAt(i + 1);
if (adjRow.RowId == row.RowId + 1)
{
adjacentRow = adjRow;
}
}
var id = row.RowId;
ImGui.TableNextRow();
ImGui.TableNextColumn();
@ -88,33 +98,33 @@ internal class UiColorWidget : IDataWindowWidget
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.PushID($"row{id}_col1");
if (this.DrawColorColumn(this.colors[i].UIForeground) &&
i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1)
DrawEdgePreview(id, this.colors[i].UIForeground, this.colors[i + 1].UIForeground);
if (this.DrawColorColumn(row.UIForeground) &&
adjacentRow.HasValue)
DrawEdgePreview(id, row.UIForeground, adjacentRow.Value.UIForeground);
ImGui.PopID();
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.PushID($"row{id}_col2");
if (this.DrawColorColumn(this.colors[i].UIGlow) &&
i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1)
DrawEdgePreview(id, this.colors[i].UIGlow, this.colors[i + 1].UIGlow);
if (this.DrawColorColumn(row.UIGlow) &&
adjacentRow.HasValue)
DrawEdgePreview(id, row.UIGlow, adjacentRow.Value.UIGlow);
ImGui.PopID();
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.PushID($"row{id}_col3");
if (this.DrawColorColumn(this.colors[i].Unknown2) &&
i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1)
DrawEdgePreview(id, this.colors[i].Unknown2, this.colors[i + 1].Unknown2);
if (this.DrawColorColumn(row.Unknown0) &&
adjacentRow.HasValue)
DrawEdgePreview(id, row.Unknown0, adjacentRow.Value.Unknown0);
ImGui.PopID();
ImGui.TableNextColumn();
ImGui.AlignTextToFramePadding();
ImGui.PushID($"row{id}_col4");
if (this.DrawColorColumn(this.colors[i].Unknown3) &&
i + 1 < this.colors.Length && this.colors[i + 1].RowId == id + 1)
DrawEdgePreview(id, this.colors[i].Unknown3, this.colors[i + 1].Unknown3);
if (this.DrawColorColumn(row.Unknown1) &&
adjacentRow.HasValue)
DrawEdgePreview(id, row.Unknown1, adjacentRow.Value.Unknown1);
ImGui.PopID();
}
}

View file

@ -7,10 +7,9 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Gui.ContextMenu;
using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Utility;
using ImGuiNET;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
using Serilog;
namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
@ -45,9 +44,9 @@ internal class ContextMenuAgingStep : IAgingStep
{
var contextMenu = Service<ContextMenu>.Get();
var dataMgr = Service<DataManager>.Get();
this.itemSheet = dataMgr.GetExcelSheet<Item>()!;
this.materiaSheet = dataMgr.GetExcelSheet<Materia>()!;
this.stainSheet = dataMgr.GetExcelSheet<Stain>()!;
this.itemSheet = dataMgr.GetExcelSheet<Item>();
this.materiaSheet = dataMgr.GetExcelSheet<Materia>();
this.stainSheet = dataMgr.GetExcelSheet<Stain>();
ImGui.Text(this.currentSubStep.ToString());
@ -83,7 +82,7 @@ internal class ContextMenuAgingStep : IAgingStep
case SubStep.TestDefault:
if (this.targetCharacter is { } character)
{
ImGui.Text($"Did you click \"{character.Name}\" ({character.ClassJob.GameData!.Abbreviation.ToDalamudString()})?");
ImGui.Text($"Did you click \"{character.Name}\" ({character.ClassJob.Value.Abbreviation.ExtractText()})?");
if (ImGui.Button("Yes"))
this.currentSubStep++;
@ -146,7 +145,7 @@ internal class ContextMenuAgingStep : IAgingStep
var targetItem = (a.Target as MenuTargetInventory)!.TargetItem;
if (targetItem is { } item)
{
name = (this.itemSheet.GetRow(item.ItemId)?.Name.ToDalamudString() ?? $"Unknown ({item.ItemId})") + (item.IsHq ? $" {SeIconChar.HighQuality.ToIconString()}" : string.Empty);
name = (this.itemSheet.GetRowOrDefault(item.ItemId)?.Name.ExtractText() ?? $"Unknown ({item.ItemId})") + (item.IsHq ? $" {SeIconChar.HighQuality.ToIconString()}" : string.Empty);
count = item.Quantity;
}
else
@ -194,7 +193,7 @@ internal class ContextMenuAgingStep : IAgingStep
{
var b = new StringBuilder();
b.AppendLine($"Target: {targetDefault.TargetName}");
b.AppendLine($"Home World: {targetDefault.TargetHomeWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({targetDefault.TargetHomeWorld.Id})");
b.AppendLine($"Home World: {targetDefault.TargetHomeWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({targetDefault.TargetHomeWorld.RowId})");
b.AppendLine($"Content Id: 0x{targetDefault.TargetContentId:X8}");
b.AppendLine($"Object Id: 0x{targetDefault.TargetObjectId:X8}");
Log.Verbose(b.ToString());
@ -209,20 +208,20 @@ internal class ContextMenuAgingStep : IAgingStep
b.AppendLine($"Content Id: 0x{character.ContentId:X8}");
b.AppendLine($"FC Tag: {character.FCTag}");
b.AppendLine($"Job: {character.ClassJob.GameData?.Abbreviation.ToDalamudString() ?? "Unknown"} ({character.ClassJob.Id})");
b.AppendLine($"Statuses: {string.Join(", ", character.Statuses.Select(s => s.GameData?.Name.ToDalamudString() ?? s.Id.ToString()))}");
b.AppendLine($"Home World: {character.HomeWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.HomeWorld.Id})");
b.AppendLine($"Current World: {character.CurrentWorld.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.CurrentWorld.Id})");
b.AppendLine($"Job: {character.ClassJob.ValueNullable?.Abbreviation.ExtractText() ?? "Unknown"} ({character.ClassJob.RowId})");
b.AppendLine($"Statuses: {string.Join(", ", character.Statuses.Select(s => s.ValueNullable?.Name.ExtractText() ?? s.RowId.ToString()))}");
b.AppendLine($"Home World: {character.HomeWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.HomeWorld.RowId})");
b.AppendLine($"Current World: {character.CurrentWorld.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.CurrentWorld.RowId})");
b.AppendLine($"Is From Other Server: {character.IsFromOtherServer}");
b.Append("Location: ");
if (character.Location.GameData is { } location)
b.Append($"{location.PlaceNameRegion.Value?.Name.ToDalamudString() ?? "Unknown"}/{location.PlaceNameZone.Value?.Name.ToDalamudString() ?? "Unknown"}/{location.PlaceName.Value?.Name.ToDalamudString() ?? "Unknown"}");
if (character.Location.ValueNullable is { } location)
b.Append($"{location.PlaceNameRegion.ValueNullable?.Name.ExtractText() ?? "Unknown"}/{location.PlaceNameZone.ValueNullable?.Name.ExtractText() ?? "Unknown"}/{location.PlaceName.ValueNullable?.Name.ExtractText() ?? "Unknown"}");
else
b.Append("Unknown");
b.AppendLine($" ({character.Location.Id})");
b.AppendLine($" ({character.Location.RowId})");
b.AppendLine($"Grand Company: {character.GrandCompany.GameData?.Name.ToDalamudString() ?? "Unknown"} ({character.GrandCompany.Id})");
b.AppendLine($"Grand Company: {character.GrandCompany.ValueNullable?.Name.ExtractText() ?? "Unknown"} ({character.GrandCompany.RowId})");
b.AppendLine($"Client Language: {character.ClientLanguage}");
b.AppendLine($"Languages: {string.Join(", ", character.Languages)}");
b.AppendLine($"Gender: {character.Gender}");
@ -241,7 +240,7 @@ internal class ContextMenuAgingStep : IAgingStep
if (targetInventory.TargetItem is { } item)
{
var b = new StringBuilder();
b.AppendLine($"Item: {(item.IsEmpty ? "None" : this.itemSheet.GetRow(item.ItemId)?.Name.ToDalamudString())} ({item.ItemId})");
b.AppendLine($"Item: {(item.IsEmpty ? "None" : this.itemSheet.GetRowOrDefault(item.ItemId)?.Name.ExtractText())} ({item.ItemId})");
b.AppendLine($"Container: {item.ContainerType}");
b.AppendLine($"Slot: {item.InventorySlot}");
b.AppendLine($"Quantity: {item.Quantity}");
@ -259,7 +258,7 @@ internal class ContextMenuAgingStep : IAgingStep
Log.Verbose($"{materiaId} {materiaGrade}");
if (this.materiaSheet.GetRow(materiaId) is { } materia &&
materia.Item[materiaGrade].Value is { } materiaItem)
materias.Add($"{materiaItem.Name.ToDalamudString()}");
materias.Add($"{materiaItem.Name.ExtractText()}");
else
materias.Add($"Unknown (Id: {materiaId}, Grade: {materiaGrade})");
}
@ -275,7 +274,7 @@ internal class ContextMenuAgingStep : IAgingStep
var stainId = item.Stains[i];
if (stainId != 0)
{
var stainName = this.stainSheet.GetRow(stainId)?.Name.ToDalamudString().ToString() ?? "Unknown";
var stainName = this.stainSheet.GetRowOrDefault(stainId)?.Name.ExtractText() ?? "Unknown";
b.AppendLine($" Stain {i + 1}: {stainName} ({stainId})");
}
else
@ -285,13 +284,13 @@ internal class ContextMenuAgingStep : IAgingStep
}
if (item.Stains[0] != 0)
b.AppendLine($"{this.stainSheet.GetRow(item.Stains[0])?.Name.ToDalamudString() ?? "Unknown"} ({item.Stains[0]})");
b.AppendLine($"{this.stainSheet.GetRowOrDefault(item.Stains[0])?.Name.ExtractText() ?? "Unknown"} ({item.Stains[0]})");
else
b.AppendLine("None");
b.Append("Glamoured Item: ");
if (item.GlamourId != 0)
b.AppendLine($"{this.itemSheet.GetRow(item.GlamourId)?.Name.ToDalamudString() ?? "Unknown"} ({item.GlamourId})");
b.AppendLine($"{this.itemSheet.GetRowOrDefault(item.GlamourId)?.Name.ExtractText() ?? "Unknown"} ({item.GlamourId})");
else
b.AppendLine("None");

View file

@ -1,6 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using Dalamud.Data;
using Dalamud.Utility;
using Lumina.Excel;
@ -11,31 +8,46 @@ namespace Dalamud.Interface.Internal.Windows.SelfTest.AgingSteps;
/// Test setup for Lumina.
/// </summary>
/// <typeparam name="T">ExcelRow to run test on.</typeparam>
internal class LuminaAgingStep<T> : IAgingStep
where T : ExcelRow
/// <param name="isLargeSheet">Whether or not the sheet is large. If it is large, the self test will iterate through the full sheet in one frame and benchmark the time taken.</param>
internal class LuminaAgingStep<T>(bool isLargeSheet) : IAgingStep
where T : struct, IExcelRow<T>
{
private int step = 0;
private List<T> rows;
private ExcelSheet<T> rows;
/// <inheritdoc/>
public string Name => "Test Lumina";
public string Name => $"Test Lumina ({typeof(T).Name})";
/// <inheritdoc/>
public SelfTestStepResult RunStep()
{
var dataManager = Service<DataManager>.Get();
this.rows ??= Service<DataManager>.Get().GetExcelSheet<T>();
this.rows ??= dataManager.GetExcelSheet<T>().ToList();
if (isLargeSheet)
{
var i = 0;
T currentRow = default;
foreach (var row in this.rows)
{
i++;
currentRow = row;
}
Util.ShowObject(this.rows[this.step]);
Util.ShowObject(currentRow);
return SelfTestStepResult.Pass;
}
else
{
Util.ShowObject(this.rows.GetRowAt(this.step));
this.step++;
return this.step >= this.rows.Count ? SelfTestStepResult.Pass : SelfTestStepResult.Waiting;
this.step++;
return this.step >= this.rows.Count ? SelfTestStepResult.Pass : SelfTestStepResult.Waiting;
}
}
/// <inheritdoc/>
public void CleanUp()
{
// ignored
this.step = 0;
}
}

View file

@ -9,7 +9,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Logging.Internal;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;
namespace Dalamud.Interface.Internal.Windows.SelfTest;
@ -39,7 +39,11 @@ internal class SelfTestWindow : Window
new GamepadStateAgingStep(),
new ChatAgingStep(),
new HoverAgingStep(),
new LuminaAgingStep<TerritoryType>(),
new LuminaAgingStep<Item>(true),
new LuminaAgingStep<Level>(true),
new LuminaAgingStep<Lumina.Excel.Sheets.Action>(true),
new LuminaAgingStep<Quest>(true),
new LuminaAgingStep<TerritoryType>(false),
new AddonLifecycleAgingStep(),
new PartyFinderAgingStep(),
new HandledExceptionAgingStep(),