Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to Lumina 5 (new Excel parsing) #2022

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
11 changes: 9 additions & 2 deletions Dalamud/Dalamud.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@
<PackageReference Include="goaaats.Reloaded.Hooks" Version="4.2.0-goat.4" />
<PackageReference Include="goaaats.Reloaded.Assembler" Version="1.0.14-goat.2" />
<PackageReference Include="JetBrains.Annotations" Version="2024.2.0" />
<PackageReference Include="Lumina" Version="4.1.1" />
<PackageReference Include="Lumina.Excel" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0-preview.1.24081.5" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.46-beta">
<PrivateAssets>all</PrivateAssets>
Expand Down Expand Up @@ -121,6 +119,15 @@
<EmbeddedResource Include="Interface\ImGuiSeStringRenderer\Internal\TextProcessing\LineBreak.txt" LogicalName="LineBreak.txt" />
</ItemGroup>

<ItemGroup>
<Reference Include="Lumina.Excel">
<HintPath>..\..\Lumina.Excel\src\Lumina.Excel\bin\Release\net8.0\Lumina.Excel.dll</HintPath>
</Reference>
<Reference Include="Lumina">
<HintPath>..\..\Lumina.Excel\src\Lumina.Excel\bin\Release\net8.0\Lumina.dll</HintPath>
</Reference>
</ItemGroup>

<Target Name="AddRuntimeDependenciesToContent" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="GenerateBuildDependencyFile;GenerateBuildRuntimeConfigurationFiles">
<ItemGroup>
<ContentWithTargetPath Include="$(ProjectDepsFilePath)" CopyToOutputDirectory="PreserveNewest" TargetPath="$(ProjectDepsFileName)" />
Expand Down
18 changes: 10 additions & 8 deletions Dalamud/Data/DataManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Lumina;
using Lumina.Data;
using Lumina.Excel;

using Newtonsoft.Json;
using Serilog;

Expand All @@ -28,12 +29,15 @@ internal sealed class DataManager : IInternalDisposableService, IDataManager
{
private readonly Thread luminaResourceThread;
private readonly CancellationTokenSource luminaCancellationTokenSource;
private readonly RsvResolver rsvResolver;

[ServiceManager.ServiceConstructor]
private DataManager(Dalamud dalamud)
{
this.Language = (ClientLanguage)dalamud.StartInfo.Language;

this.rsvResolver = new();

try
{
Log.Verbose("Starting data load...");
Expand All @@ -44,11 +48,8 @@ private DataManager(Dalamud dalamud)
{
LoadMultithreaded = true,
CacheFileResources = true,
#if NEVER // Lumina bug
PanicOnSheetChecksumMismatch = true,
#else
PanicOnSheetChecksumMismatch = false,
#endif
RsvResolver = this.rsvResolver.TryResolve,
DefaultExcelLanguage = this.Language.ToLumina(),
};

Expand Down Expand Up @@ -129,12 +130,12 @@ private DataManager(Dalamud dalamud)
#region Lumina Wrappers

/// <inheritdoc/>
public ExcelSheet<T>? GetExcelSheet<T>() where T : ExcelRow
=> this.Excel.GetSheet<T>();
public ExcelSheet<T> GetExcelSheet<T>(ClientLanguage? language = null, string? name = null) where T : struct, IExcelRow<T>
=> this.Excel.GetSheet<T>(language?.ToLumina(), name);

/// <inheritdoc/>
public ExcelSheet<T>? GetExcelSheet<T>(ClientLanguage language) where T : ExcelRow
=> this.Excel.GetSheet<T>(language.ToLumina());
public SubrowExcelSheet<T> GetSubrowExcelSheet<T>(ClientLanguage? language = null, string? name = null) where T : struct, IExcelSubrow<T>
=> this.Excel.GetSubrowSheet<T>(language?.ToLumina(), name);

/// <inheritdoc/>
public FileResource? GetFile(string path)
Expand Down Expand Up @@ -170,6 +171,7 @@ void IInternalDisposableService.DisposeService()
{
this.luminaCancellationTokenSource.Cancel();
this.GameData.Dispose();
this.rsvResolver.Dispose();
}

private class LauncherTroubleshootingInfo
Expand Down
22 changes: 22 additions & 0 deletions Dalamud/Data/LuminaUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Lumina.Excel;

namespace Dalamud.Data;

/// <summary>
/// A helper class to easily resolve Lumina data within Dalamud.
/// </summary>
internal static class LuminaUtils
{
private static ExcelModule Module => Service<DataManager>.Get().Excel;

/// <summary>
/// Initializes a new instance of the <see cref="RowRef{T}"/> class using the default <see cref="ExcelModule"/>.
/// </summary>
/// <typeparam name="T">The type of Lumina sheet to resolve.</typeparam>
/// <param name="rowId">The id of the row to resolve.</param>
/// <returns>A new <see cref="RowRef{T}"/> object.</returns>
public static RowRef<T> CreateRef<T>(uint rowId) where T : struct, IExcelRow<T>
{
return new(Module, rowId);
}
}
51 changes: 51 additions & 0 deletions Dalamud/Data/RsvResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using System.Collections.Generic;

using Dalamud.Hooking;
using Dalamud.Logging.Internal;
using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Client.LayoutEngine;
using Lumina.Text.ReadOnly;

namespace Dalamud.Data;

/// <summary>
/// Provides functionality for resolving RSV strings.
/// </summary>
internal sealed unsafe class RsvResolver : IDisposable
{
private static readonly ModuleLog Log = new("RsvProvider");

private readonly Hook<LayoutWorld.Delegates.AddRsvString> addRsvStringHook;

/// <summary>
/// Initializes a new instance of the <see cref="RsvResolver"/> class.
/// </summary>
public RsvResolver()
{
this.addRsvStringHook = Hook<LayoutWorld.Delegates.AddRsvString>.FromAddress((nint)LayoutWorld.MemberFunctionPointers.AddRsvString, this.AddRsvStringDetour);

this.addRsvStringHook.Enable();
}

private Dictionary<ReadOnlySeString, ReadOnlySeString> Lookup { get; } = [];

/// <summary>Attemps to resolve an RSV string.</summary>
/// <inheritdoc cref="Lumina.Excel.ExcelModule.ResolveRsvDelegate"/>
public bool TryResolve(ReadOnlySeString rsvString, out ReadOnlySeString resolvedString) =>
this.Lookup.TryGetValue(rsvString, out resolvedString);

/// <inheritdoc/>
public void Dispose()
{
this.addRsvStringHook.Dispose();
}

private bool AddRsvStringDetour(LayoutWorld* @this, byte* rsvString, byte* resolvedString, nuint resolvedStringSize)
{
var rsv = new ReadOnlySeString(MemoryHelper.ReadRawNullTerminated((nint)rsvString));
var resolved = new ReadOnlySeString(new ReadOnlySpan<byte>(resolvedString, (int)resolvedStringSize).ToArray());
Log.Debug($"Resolving RSV \"{rsv}\" to \"{resolved}\".");
this.Lookup[rsv] = resolved;
return this.addRsvStringHook.Original(@this, rsvString, resolvedString, resolvedStringSize);
}
}
9 changes: 6 additions & 3 deletions Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Dalamud.Game.ClientState.Resolvers;
using Dalamud.Data;

using FFXIVClientStructs.FFXIV.Client.Game.UI;

using Lumina.Excel;

namespace Dalamud.Game.ClientState.Aetherytes;

/// <summary>
Expand Down Expand Up @@ -56,7 +59,7 @@ public interface IAetheryteEntry
/// <summary>
/// Gets the Aetheryte data related to this aetheryte.
/// </summary>
ExcelResolver<Lumina.Excel.GeneratedSheets.Aetheryte> AetheryteData { get; }
RowRef<Lumina.Excel.Sheets.Aetheryte> AetheryteData { get; }
}

/// <summary>
Expand Down Expand Up @@ -103,5 +106,5 @@ internal AetheryteEntry(TeleportInfo data)
public bool IsApartment => this.data.IsApartment;

/// <inheritdoc />
public ExcelResolver<Lumina.Excel.GeneratedSheets.Aetheryte> AetheryteData => new(this.AetheryteId);
public RowRef<Lumina.Excel.Sheets.Aetheryte> AetheryteData => LuminaUtils.CreateRef<Lumina.Excel.Sheets.Aetheryte>(this.AetheryteId);
}
16 changes: 9 additions & 7 deletions Dalamud/Game/ClientState/Buddy/BuddyMember.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.ClientState.Resolvers;

using Lumina.Excel;

namespace Dalamud.Game.ClientState.Buddy;

Expand Down Expand Up @@ -45,17 +47,17 @@ public interface IBuddyMember
/// <summary>
/// Gets the Mount data related to this buddy. It should only be used with companion buddies.
/// </summary>
ExcelResolver<Lumina.Excel.GeneratedSheets.Mount> MountData { get; }
RowRef<Lumina.Excel.Sheets.Mount> MountData { get; }

/// <summary>
/// Gets the Pet data related to this buddy. It should only be used with pet buddies.
/// </summary>
ExcelResolver<Lumina.Excel.GeneratedSheets.Pet> PetData { get; }
RowRef<Lumina.Excel.Sheets.Pet> PetData { get; }

/// <summary>
/// Gets the Trust data related to this buddy. It should only be used with battle buddies.
/// </summary>
ExcelResolver<Lumina.Excel.GeneratedSheets.DawnGrowMember> TrustData { get; }
RowRef<Lumina.Excel.Sheets.DawnGrowMember> TrustData { get; }
}

/// <summary>
Expand Down Expand Up @@ -94,13 +96,13 @@ internal BuddyMember(IntPtr address)
public uint DataID => this.Struct->DataId;

/// <inheritdoc />
public ExcelResolver<Lumina.Excel.GeneratedSheets.Mount> MountData => new(this.DataID);
public RowRef<Lumina.Excel.Sheets.Mount> MountData => LuminaUtils.CreateRef<Lumina.Excel.Sheets.Mount>(this.DataID);

/// <inheritdoc />
public ExcelResolver<Lumina.Excel.GeneratedSheets.Pet> PetData => new(this.DataID);
public RowRef<Lumina.Excel.Sheets.Pet> PetData => LuminaUtils.CreateRef<Lumina.Excel.Sheets.Pet>(this.DataID);

/// <inheritdoc />
public ExcelResolver<Lumina.Excel.GeneratedSheets.DawnGrowMember> TrustData => new(this.DataID);
public RowRef<Lumina.Excel.Sheets.DawnGrowMember> TrustData => LuminaUtils.CreateRef<Lumina.Excel.Sheets.DawnGrowMember>(this.DataID);

private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address;
}
2 changes: 1 addition & 1 deletion Dalamud/Game/ClientState/ClientState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;

using Lumina.Excel.GeneratedSheets;
using Lumina.Excel.Sheets;

using Action = System.Action;

Expand Down
11 changes: 6 additions & 5 deletions Dalamud/Game/ClientState/Fates/Fate.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
using System.Numerics;

using Dalamud.Data;
using Dalamud.Game.ClientState.Resolvers;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Memory;

using Lumina.Excel;

namespace Dalamud.Game.ClientState.Fates;

/// <summary>
Expand All @@ -20,7 +21,7 @@ public interface IFate : IEquatable<IFate>
/// <summary>
/// Gets game data linked to this Fate.
/// </summary>
Lumina.Excel.GeneratedSheets.Fate GameData { get; }
RowRef<Lumina.Excel.Sheets.Fate> GameData { get; }

/// <summary>
/// Gets the time this <see cref="Fate"/> started.
Expand Down Expand Up @@ -105,7 +106,7 @@ public interface IFate : IEquatable<IFate>
/// <summary>
/// Gets the territory this <see cref="Fate"/> is located in.
/// </summary>
ExcelResolver<Lumina.Excel.GeneratedSheets.TerritoryType> TerritoryType { get; }
RowRef<Lumina.Excel.Sheets.TerritoryType> TerritoryType { get; }

/// <summary>
/// Gets the address of this Fate in memory.
Expand Down Expand Up @@ -185,7 +186,7 @@ internal unsafe partial class Fate : IFate
public ushort FateId => this.Struct->FateId;

/// <inheritdoc/>
public Lumina.Excel.GeneratedSheets.Fate GameData => Service<DataManager>.Get().GetExcelSheet<Lumina.Excel.GeneratedSheets.Fate>().GetRow(this.FateId);
public RowRef<Lumina.Excel.Sheets.Fate> GameData => LuminaUtils.CreateRef<Lumina.Excel.Sheets.Fate>(this.FateId);

/// <inheritdoc/>
public int StartTimeEpoch => this.Struct->StartTimeEpoch;
Expand Down Expand Up @@ -238,5 +239,5 @@ internal unsafe partial class Fate : IFate
/// <summary>
/// Gets the territory this <see cref="Fate"/> is located in.
/// </summary>
public ExcelResolver<Lumina.Excel.GeneratedSheets.TerritoryType> TerritoryType => new(this.Struct->TerritoryId);
public RowRef<Lumina.Excel.Sheets.TerritoryType> TerritoryType => LuminaUtils.CreateRef<Lumina.Excel.Sheets.TerritoryType>(this.Struct->TerritoryId);
}
4 changes: 2 additions & 2 deletions Dalamud/Game/ClientState/JobGauge/Types/SMNGauge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ internal SMNGauge(IntPtr address)

/// <summary>
/// Gets the summon that will return after the current summon expires.
/// This maps to the <see cref="Lumina.Excel.GeneratedSheets.Pet"/> sheet.
/// This maps to the <see cref="Lumina.Excel.Sheets.Pet"/> sheet.
/// </summary>
public SummonPet ReturnSummon => (SummonPet)this.Struct->ReturnSummon;

/// <summary>
/// Gets the summon glam for the <see cref="ReturnSummon"/>.
/// This maps to the <see cref="Lumina.Excel.GeneratedSheets.PetMirage"/> sheet.
/// This maps to the <see cref="Lumina.Excel.Sheets.PetMirage"/> sheet.
/// </summary>
public PetGlam ReturnSummonGlam => (PetGlam)this.Struct->ReturnSummonGlam;

Expand Down
22 changes: 9 additions & 13 deletions Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
using System.Numerics;

using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Data;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.ClientState.Resolvers;
using Dalamud.Game.ClientState.Statuses;
using Dalamud.Game.Text.SeStringHandling;

using Lumina.Excel.GeneratedSheets;
using Lumina.Excel;
using Lumina.Excel.Sheets;

namespace Dalamud.Game.ClientState.Objects.SubKinds;

Expand All @@ -16,14 +12,14 @@ namespace Dalamud.Game.ClientState.Objects.SubKinds;
public interface IPlayerCharacter : IBattleChara
{
/// <summary>
/// Gets the current <see cref="ExcelResolver{T}">world</see> of the character.
/// Gets the current <see cref="RowRef{T}">world</see> of the character.
/// </summary>
ExcelResolver<World> CurrentWorld { get; }
RowRef<World> CurrentWorld { get; }

/// <summary>
/// Gets the home <see cref="ExcelResolver{T}">world</see> of the character.
/// Gets the home <see cref="RowRef{T}">world</see> of the character.
/// </summary>
ExcelResolver<World> HomeWorld { get; }
RowRef<World> HomeWorld { get; }
}

/// <summary>
Expand All @@ -42,10 +38,10 @@ internal PlayerCharacter(IntPtr address)
}

/// <inheritdoc/>
public ExcelResolver<World> CurrentWorld => new(this.Struct->CurrentWorld);
public RowRef<World> CurrentWorld => LuminaUtils.CreateRef<World>(this.Struct->CurrentWorld);

/// <inheritdoc/>
public ExcelResolver<World> HomeWorld => new(this.Struct->HomeWorld);
public RowRef<World> HomeWorld => LuminaUtils.CreateRef<World>(this.Struct->HomeWorld);

/// <summary>
/// Gets the target actor ID of the PlayerCharacter.
Expand Down
Loading
Loading