Compare commits

...

13 commits

Author SHA1 Message Date
github-actions[bot]
b0a0fafb53 Merge remote-tracking branch 'origin/master' into api14-rollup
Some checks failed
Build Dalamud / Build on Windows (push) Has been cancelled
Build Dalamud / Check API Compatibility (push) Has been cancelled
Build Dalamud / Deploy dalamud-distrib staging (push) Has been cancelled
2025-12-15 23:31:02 +00:00
goat
8334836b6a
Merge pull request #2386 from KazWolfe/ipc-context
Some checks failed
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
Rollup changes to next version / check (api14) (push) Failing after 8s
Tag Build / Tag Build (push) Successful in 3s
feat: Add IPC Context
2025-12-16 00:30:06 +01:00
goat
8a742e7e59
Merge pull request #2505 from Aireil/patch-25
Remove obsolete enum values from FlyTextKind
2025-12-15 23:25:46 +01:00
Aireil
56325afa7f
Remove obsolete enum values from FlyTextKind
They have been obsolete for nearly 7 months (before 7.3).
2025-12-15 23:14:46 +01:00
goaaats
ffd99d5791 Add interface to obtain versioning info 2025-12-15 21:43:52 +01:00
goaaats
20af5b40c7 Make all versioning functions internal, move to separate class 2025-12-15 21:31:25 +01:00
goaaats
a1409096fd Redo SeStringRenderer deprecations 2025-12-15 21:20:24 +01:00
goat
fecba89710
Merge pull request #2498 from goatcorp/api14-rollup
Some checks are pending
Build Dalamud / Build on Windows (push) Waiting to run
Build Dalamud / Check API Compatibility (push) Blocked by required conditions
Build Dalamud / Deploy dalamud-distrib staging (push) Blocked by required conditions
[api14] Rollup changes from master
2025-12-15 21:12:03 +01:00
goat
b57b96b9a0
Merge pull request #2493 from Haselnussbomber/update-packages
Some checks failed
Build Dalamud / Build on Windows (push) Has been cancelled
Build Dalamud / Check API Compatibility (push) Has been cancelled
Build Dalamud / Deploy dalamud-distrib staging (push) Has been cancelled
[API14] Update packages
2025-12-13 23:47:14 +01:00
Haselnussbomber
07f9e03010
Update packages 2025-12-07 15:14:27 +01:00
Haselnussbomber
9cfa81c92d
Remove unused packages 2025-12-07 15:00:20 +01:00
Kaz Wolfe
8cced4c1d7
fix: use channel threadlocal instead of a ThreadStatic 2025-08-25 13:31:05 -07:00
Kaz Wolfe
b18b8b40e5
feat: Add IPC Context
- Demo of the ipc context idea
- Accessible via ipcPub.GetContext()
2025-08-25 13:14:13 -07:00
35 changed files with 372 additions and 267 deletions

View file

@ -84,11 +84,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Collections.Immutable" />
<PackageReference Include="System.Drawing.Common" />
<PackageReference Include="System.Reactive" />
<PackageReference Include="System.Reflection.MetadataLoadContext" />
<PackageReference Include="System.Resources.Extensions" />
<PackageReference Include="TerraFX.Interop.Windows" />
</ItemGroup>

View file

@ -192,8 +192,8 @@ public sealed class EntryPoint
var dalamud = new Dalamud(info, fs, configuration, mainThreadContinueEvent);
Log.Information("This is Dalamud - Core: {GitHash}, CS: {CsGitHash} [{CsVersion}]",
Util.GetScmVersion(),
Util.GetGitHashClientStructs(),
Versioning.GetScmVersion(),
Versioning.GetGitHashClientStructs(),
FFXIVClientStructs.ThisAssembly.Git.Commits);
dalamud.WaitForUnload();

View file

@ -104,7 +104,7 @@ internal partial class ChatHandlers : IServiceType
if (this.configuration.PrintDalamudWelcomeMsg)
{
chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud {0} loaded."), Util.GetScmVersion())
chatGui.Print(string.Format(Loc.Localize("DalamudWelcome", "Dalamud {0} loaded."), Versioning.GetScmVersion())
+ string.Format(Loc.Localize("PluginsWelcome", " {0} plugin(s) loaded."), pluginManager.InstalledPlugins.Count(x => x.IsLoaded)));
}
@ -116,7 +116,7 @@ internal partial class ChatHandlers : IServiceType
}
}
if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Util.AssemblyVersion.StartsWith(this.configuration.LastVersion))
if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Versioning.GetAssemblyVersion().StartsWith(this.configuration.LastVersion))
{
var linkPayload = chatGui.AddChatLinkHandler(
(_, _) => dalamudInterface.OpenPluginInstallerTo(PluginInstallerOpenKind.Changelogs));
@ -137,7 +137,7 @@ internal partial class ChatHandlers : IServiceType
Type = XivChatType.Notice,
});
this.configuration.LastVersion = Util.AssemblyVersion;
this.configuration.LastVersion = Versioning.GetAssemblyVersion();
this.configuration.QueueSave();
}

View file

@ -92,34 +92,16 @@ public enum FlyTextKind : int
/// </summary>
IslandExp = 15,
/// <summary>
/// Val1 in serif font next to all caps condensed font Text1 with Text2 in sans-serif as subtitle.
/// </summary>
[Obsolete("Use Dataset instead", true)]
Unknown16 = 16,
/// <summary>
/// Val1 in serif font next to all caps condensed font Text1 with Text2 in sans-serif as subtitle.
/// </summary>
Dataset = 16,
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// </summary>
[Obsolete("Use Knowledge instead", true)]
Unknown17 = 17,
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// </summary>
Knowledge = 17,
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// </summary>
[Obsolete("Use PhantomExp instead", true)]
Unknown18 = 18,
/// <summary>
/// Val1 in serif font, Text2 in sans-serif as subtitle.
/// </summary>

View file

@ -162,8 +162,7 @@ internal class SeStringRenderer : IServiceType
if (drawParams.Font.HasValue)
font = drawParams.Font.Value;
// API14: Remove commented out code
if (ThreadSafety.IsMainThread /* && drawParams.TargetDrawList is null */ && font is null)
if (ThreadSafety.IsMainThread && drawParams.TargetDrawList is null && font is null)
font = ImGui.GetFont();
if (font is null)
throw new ArgumentException("Specified font is empty.");

View file

@ -66,17 +66,10 @@ public unsafe ref struct SeStringDrawState : IDisposable
this.drawList = ssdp.TargetDrawList.Value;
this.ScreenOffset = ssdp.ScreenOffset ?? Vector2.Zero;
// API14: Remove, always throw
if (ThreadSafety.IsMainThread)
{
this.ScreenOffset = ssdp.ScreenOffset ?? ImGui.GetCursorScreenPos();
this.FontSize = ssdp.FontSize ?? ImGui.GetFontSize();
}
else
{
throw new ArgumentException(
$"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state.");
}
this.ScreenOffset = ssdp.ScreenOffset ?? throw new ArgumentException(
$"{nameof(ssdp.ScreenOffset)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state. (GetCursorScreenPos?)");
this.FontSize = ssdp.FontSize ?? throw new ArgumentException(
$"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state.");
// this.FontSize = ssdp.FontSize ?? throw new ArgumentException(
// $"{nameof(ssdp.FontSize)} must be set when specifying a target draw list, as it cannot be fetched from the ImGui state.");

View file

@ -305,12 +305,12 @@ internal class DalamudCommands : IServiceType
chatGui.Print(new SeStringBuilder()
.AddItalics("Dalamud:")
.AddText($" {Util.GetScmVersion()}")
.AddText($" {Versioning.GetScmVersion()}")
.Build());
chatGui.Print(new SeStringBuilder()
.AddItalics("FFXIVCS:")
.AddText($" {Util.GetGitHashClientStructs()}")
.AddText($" {Versioning.GetGitHashClientStructs()}")
.Build());
}

View file

@ -182,7 +182,7 @@ internal class DalamudInterface : IInternalDisposableService
() => Service<DalamudInterface>.GetNullable()?.ToggleDevMenu(),
VirtualKey.SHIFT);
if (Util.GetActiveTrack() != "release")
if (Versioning.GetActiveTrack() != "release")
{
titleScreenMenu.AddEntryCore(
Loc.Localize("TSMDalamudDevMenu", "Developer Menu"),
@ -865,7 +865,7 @@ internal class DalamudInterface : IInternalDisposableService
}
ImGui.MenuItem(this.dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown version", false, false);
ImGui.MenuItem($"D: {Util.GetScmVersion()} CS: {Util.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false, false);
ImGui.MenuItem($"D: {Versioning.GetScmVersion()} CS: {Versioning.GetGitHashClientStructs()}[{FFXIVClientStructs.ThisAssembly.Git.Commits}]", false, false);
ImGui.MenuItem($"CLR: {Environment.Version}", false, false);
ImGui.EndMenu();
@ -1076,8 +1076,8 @@ internal class DalamudInterface : IInternalDisposableService
{
ImGui.PushFont(InterfaceManager.MonoFont);
ImGui.BeginMenu($"{Util.GetActiveTrack() ?? "???"} on {Util.GetGitBranch() ?? "???"}", false);
ImGui.BeginMenu($"{Util.GetScmVersion()}", false);
ImGui.BeginMenu($"{Versioning.GetActiveTrack() ?? "???"} on {Versioning.GetGitBranch() ?? "???"}", false);
ImGui.BeginMenu($"{Versioning.GetScmVersion()}", false);
ImGui.BeginMenu(this.FrameCount.ToString("000000"), false);
ImGui.BeginMenu(ImGui.GetIO().Framerate.ToString("000"), false);
ImGui.BeginMenu($"W:{Util.FormatBytes(GC.GetTotalMemory(false))}", false);

View file

@ -47,7 +47,7 @@ public class BranchSwitcherWindow : Window
this.branches = await client.GetFromJsonAsync<Dictionary<string, VersionEntry>>(BranchInfoUrl);
Debug.Assert(this.branches != null, "this.branches != null");
var trackName = Util.GetActiveTrack();
var trackName = Versioning.GetActiveTrack();
this.selectedBranchIndex = this.branches.IndexOf(x => x.Value.Track == trackName);
if (this.selectedBranchIndex == -1)
{

View file

@ -147,7 +147,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
var pmWantsChangelog = pm?.InstalledPlugins.Any() ?? true;
return (string.IsNullOrEmpty(configuration.LastChangelogMajorMinor) ||
(!WarrantsChangelogForMajorMinor.StartsWith(configuration.LastChangelogMajorMinor) &&
Util.AssemblyVersion.StartsWith(WarrantsChangelogForMajorMinor))) && pmWantsChangelog;
Versioning.GetAssemblyVersion().StartsWith(WarrantsChangelogForMajorMinor))) && pmWantsChangelog;
}
/// <inheritdoc/>
@ -357,7 +357,7 @@ internal sealed class ChangelogWindow : Window, IDisposable
{
case State.WindowFadeIn:
case State.ExplainerIntro:
ImGui.TextWrapped($"Welcome to Dalamud v{Util.GetScmVersion()}!");
ImGui.TextWrapped($"Welcome to Dalamud v{Versioning.GetScmVersion()}!");
ImGuiHelpers.ScaledDummy(5);
ImGui.TextWrapped(ChangeLog);
ImGuiHelpers.ScaledDummy(5);

View file

@ -48,12 +48,20 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPub.RegisterAction(msg =>
{
Log.Information("Data action was called: {Msg}", msg);
Log.Information(
"Data action was called: {Msg}\n" +
" Context: {Context}",
msg,
this.ipcPub.GetContext());
});
this.ipcPub.RegisterFunc(msg =>
{
Log.Information("Data func was called: {Msg}", msg);
Log.Information(
"Data func was called: {Msg}\n" +
" Context: {Context}",
msg,
this.ipcPub.GetContext());
return Guid.NewGuid().ToString();
});
}
@ -61,14 +69,8 @@ internal class PluginIpcWidget : IDataWindowWidget
if (this.ipcSub == null)
{
this.ipcSub = new CallGatePubSub<string, string>("dataDemo1");
this.ipcSub.Subscribe(_ =>
{
Log.Information("PONG1");
});
this.ipcSub.Subscribe(_ =>
{
Log.Information("PONG2");
});
this.ipcSub.Subscribe(_ => { Log.Information("PONG1"); });
this.ipcSub.Subscribe(_ => { Log.Information("PONG2"); });
this.ipcSub.Subscribe(_ => throw new Exception("PONG3"));
}
@ -78,12 +80,21 @@ internal class PluginIpcWidget : IDataWindowWidget
this.ipcPubGo.RegisterAction(go =>
{
Log.Information("Data action was called: {Name}", go?.Name);
Log.Information(
"Data action was called: {Name}" +
"\n Context: {Context}",
go?.Name,
this.ipcPubGo.GetContext());
});
this.ipcPubGo.RegisterFunc(go =>
{
Log.Information("Data func was called: {Name}", go?.Name);
Log.Information(
"Data func was called: {Name}\n" +
" Context: {Context}",
go?.Name,
this.ipcPubGo.GetContext());
return "test";
});
}

View file

@ -144,7 +144,7 @@ internal class SeStringCreatorWidget : IDataWindowWidget
new TextEntry(TextEntryType.Macro, " <string(lstr1)>"),
];
private SeStringParameter[]? localParameters = [Util.GetScmVersion()];
private SeStringParameter[]? localParameters = [Versioning.GetScmVersion()];
private ReadOnlySeString input;
private ClientLanguage? language;
private Task? validImportSheetNamesTask;

View file

@ -302,7 +302,7 @@ internal class PluginInstallerWindow : Window, IDisposable
this.profileManagerWidget.Reset();
if (this.staleDalamudNewVersion == null && !Util.GetActiveTrack().IsNullOrEmpty())
if (this.staleDalamudNewVersion == null && !Versioning.GetActiveTrack().IsNullOrEmpty())
{
Service<DalamudReleases>.Get().GetVersionForCurrentTrack().ContinueWith(t =>
{
@ -310,7 +310,7 @@ internal class PluginInstallerWindow : Window, IDisposable
return;
var versionInfo = t.Result;
if (versionInfo.AssemblyVersion != Util.GetScmVersion())
if (versionInfo.AssemblyVersion != Versioning.GetScmVersion())
{
this.staleDalamudNewVersion = versionInfo.AssemblyVersion;
}
@ -1670,7 +1670,7 @@ internal class PluginInstallerWindow : Window, IDisposable
DrawWarningIcon();
DrawLinesCentered("A new version of Dalamud is available.\n" +
"Please restart the game to ensure compatibility with updated plugins.\n" +
$"old: {Util.GetScmVersion()} new: {this.staleDalamudNewVersion}");
$"old: {Versioning.GetScmVersion()} new: {this.staleDalamudNewVersion}");
ImGuiHelpers.ScaledDummy(10);
}
@ -2461,7 +2461,7 @@ internal class PluginInstallerWindow : Window, IDisposable
var isOutdated = effectiveApiLevel < PluginManager.DalamudApiLevel;
var isIncompatible = manifest.MinimumDalamudVersion != null &&
manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed;
manifest.MinimumDalamudVersion > Versioning.GetAssemblyVersionParsed();
var enableInstallButton = this.updateStatus != OperationStatus.InProgress &&
this.installStatus != OperationStatus.InProgress &&

View file

@ -223,7 +223,7 @@ Contribute at: https://github.com/goatcorp/Dalamud
.Select(plugin => $"{plugin.Manifest.Name} by {plugin.Manifest.Author}\n")
.Aggregate(string.Empty, (current, next) => $"{current}{next}");
this.creditsText = string.Format(CreditsTextTempl, typeof(Dalamud).Assembly.GetName().Version, pluginCredits, Util.GetGitHashClientStructs());
this.creditsText = string.Format(CreditsTextTempl, typeof(Dalamud).Assembly.GetName().Version, pluginCredits, Versioning.GetGitHashClientStructs());
var gameGui = Service<GameGui>.Get();
var playerState = PlayerState.Instance();

View file

@ -503,7 +503,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
lssb.PushEdgeColorType(701).PushColorType(539)
.Append(SeIconChar.BoxedLetterD.ToIconChar())
.PopColorType().PopEdgeColorType();
lssb.Append($" Dalamud: {Util.GetScmVersion()}");
lssb.Append($" Dalamud: {Versioning.GetScmVersion()}");
lssb.Append($" - {count} {(count != 1 ? "plugins" : "plugin")} loaded");

View file

@ -36,7 +36,7 @@ internal class HappyHttpClient : IInternalDisposableService
{
UserAgent =
{
new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion),
new ProductInfoHeaderValue("Dalamud", Versioning.GetAssemblyVersion()),
},
},
};

View file

@ -38,7 +38,7 @@ internal sealed class ClientHelloService : IInternalDisposableService
return new ClientHelloResponse
{
ApiVersion = "1.0",
DalamudVersion = Util.GetScmVersion(),
DalamudVersion = Versioning.GetScmVersion(),
GameVersion = dalamud.StartInfo.GameVersion?.ToString() ?? "Unknown",
ProcessId = Environment.ProcessId,
ProcessStartTime = new DateTimeOffset(Process.GetCurrentProcess().StartTime).ToUnixTimeSeconds(),

View file

@ -16,18 +16,15 @@ using Dalamud.Game.Text;
using Dalamud.Game.Text.Sanitizer;
using Dalamud.Interface;
using Dalamud.Interface.Internal;
using Dalamud.Interface.Internal.Windows.PluginInstaller;
using Dalamud.Interface.Internal.Windows.SelfTest;
using Dalamud.Interface.Internal.Windows.Settings;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Internal;
using Dalamud.Plugin.Internal.AutoUpdate;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Internal.Types.Manifest;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Plugin.VersionInfo;
using Dalamud.Utility;
using Serilog;
@ -204,11 +201,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
return true;
}
/// <summary>
/// Gets the plugin the given assembly is part of.
/// </summary>
/// <param name="assembly">The assembly to check.</param>
/// <returns>The plugin the given assembly is part of, or null if this is a shared assembly or if this information cannot be determined.</returns>
/// <inheritdoc/>
public IExposedPlugin? GetPlugin(Assembly assembly)
=> AssemblyLoadContext.GetLoadContext(assembly) switch
{
@ -216,11 +209,7 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
var context => this.GetPlugin(context),
};
/// <summary>
/// Gets the plugin that loads in the given context.
/// </summary>
/// <param name="context">The context to check.</param>
/// <returns>The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined.</returns>
/// <inheritdoc/>
public IExposedPlugin? GetPlugin(AssemblyLoadContext context)
=> Service<PluginManager>.Get().InstalledPlugins.FirstOrDefault(p => p.LoadsIn(context)) switch
{
@ -228,6 +217,12 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
var p => new ExposedPlugin(p),
};
/// <inheritdoc/>
public IDalamudVersionInfo GetDalamudVersion()
{
return new DalamudVersionInfo(Versioning.GetAssemblyVersionParsed(), Versioning.GetActiveTrack());
}
#region IPC
/// <inheritdoc/>
@ -248,75 +243,75 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
/// <inheritdoc/>
public ICallGateProvider<TRet> GetIpcProvider<TRet>(string name)
=> new CallGatePubSub<TRet>(name);
=> new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, TRet> GetIpcProvider<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name);
=> new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, TRet> GetIpcProvider<T1, T2, TRet>(string name)
=> new CallGatePubSub<T1, T2, TRet>(name);
=> new CallGatePubSub<T1, T2, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, TRet> GetIpcProvider<T1, T2, T3, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, T4, TRet> GetIpcProvider<T1, T2, T3, T4, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, T4, T5, TRet> GetIpcProvider<T1, T2, T3, T4, T5, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<TRet> GetIpcSubscriber<TRet>(string name)
=> new CallGatePubSub<TRet>(name);
=> new CallGatePubSub<TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, TRet> GetIpcSubscriber<T1, TRet>(string name)
=> new CallGatePubSub<T1, TRet>(name);
=> new CallGatePubSub<T1, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, TRet> GetIpcSubscriber<T1, T2, TRet>(string name)
=> new CallGatePubSub<T1, T2, TRet>(name);
=> new CallGatePubSub<T1, T2, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, TRet> GetIpcSubscriber<T1, T2, T3, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, T4, TRet> GetIpcSubscriber<T1, T2, T3, T4, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet>(name, this.plugin);
/// <inheritdoc/>
public ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet> GetIpcSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(string name)
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name);
=> new CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet>(name, this.plugin);
#endregion

View file

@ -15,7 +15,7 @@ using Dalamud.Plugin.Internal.Types.Manifest;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal;
using Dalamud.Plugin.Services;
using Dalamud.Plugin.VersionInfo;
namespace Dalamud.Plugin;
@ -194,6 +194,12 @@ public interface IDalamudPluginInterface : IServiceProvider
/// <returns>The plugin that loads in the given context, or null if this isn't a plugin's context or if this information cannot be determined.</returns>
IExposedPlugin? GetPlugin(AssemblyLoadContext context);
/// <summary>
/// Gets information about the version of Dalamud this plugin is loaded into.
/// </summary>
/// <returns>Class containing version information.</returns>
IDalamudVersionInfo GetDalamudVersion();
/// <inheritdoc cref="DataShare.GetOrCreateData{T}"/>
T GetOrCreateData<T>(string tag, Func<T> dataGenerator) where T : class;

View file

@ -1790,7 +1790,7 @@ internal class PluginManager : IInternalDisposableService
var updates = this.AvailablePlugins
.Where(remoteManifest => plugin.Manifest.InternalName == remoteManifest.InternalName)
.Where(remoteManifest => plugin.Manifest.InstalledFromUrl == remoteManifest.SourceRepo.PluginMasterUrl || !remoteManifest.SourceRepo.IsThirdParty)
.Where(remoteManifest => remoteManifest.MinimumDalamudVersion == null || Util.AssemblyVersionParsed >= remoteManifest.MinimumDalamudVersion)
.Where(remoteManifest => remoteManifest.MinimumDalamudVersion == null || Versioning.GetAssemblyVersionParsed() >= remoteManifest.MinimumDalamudVersion)
.Where(remoteManifest =>
{
var useTesting = this.UseTesting(remoteManifest);

View file

@ -315,7 +315,7 @@ internal class LocalPlugin : IAsyncDisposable
if (!this.CheckPolicy())
throw new PluginPreconditionFailedException($"Unable to load {this.Name} as a load policy forbids it");
if (this.Manifest.MinimumDalamudVersion != null && this.Manifest.MinimumDalamudVersion > Util.AssemblyVersionParsed)
if (this.Manifest.MinimumDalamudVersion != null && this.Manifest.MinimumDalamudVersion > Versioning.GetAssemblyVersionParsed())
throw new PluginPreconditionFailedException($"Unable to load {this.Name}, Dalamud version is lower than minimum required version {this.Manifest.MinimumDalamudVersion}");
this.State = PluginState.Loading;

View file

@ -59,7 +59,7 @@ internal class PluginRepository
},
UserAgent =
{
new ProductInfoHeaderValue("Dalamud", Util.AssemblyVersion),
new ProductInfoHeaderValue("Dalamud", Versioning.GetAssemblyVersion()),
},
},
};
@ -164,7 +164,7 @@ internal class PluginRepository
}
this.PluginMaster = pluginMaster.Where(this.IsValidManifest).ToList().AsReadOnly();
// API9 HACK: Force IsHide to false, we should remove that
if (!this.IsThirdParty)
{
@ -197,7 +197,7 @@ internal class PluginRepository
Log.Error("Plugin {PluginName} in {RepoLink} has an invalid Name.", manifest.InternalName, this.PluginMasterUrl);
return false;
}
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (manifest.AssemblyVersion == null)
{
@ -224,7 +224,7 @@ internal class PluginRepository
request.Headers.CacheControl = new CacheControlHeaderValue { NoCache = true };
using var requestCts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout));
return await httpClient.SendAsync(request, requestCts.Token);
}
}

View file

@ -19,6 +19,9 @@ public interface ICallGateProvider
/// <inheritdoc cref="CallGatePubSubBase.UnregisterFunc"/>
public void UnregisterFunc();
/// <inheritdoc cref="CallGatePubSubBase.GetContext"/>
public IpcContext? GetContext();
}
/// <inheritdoc cref="ICallGateProvider"/>

View file

@ -2,7 +2,9 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
using System.Threading;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Ipc.Internal.Converters;
@ -16,6 +18,8 @@ namespace Dalamud.Plugin.Ipc.Internal;
/// </summary>
internal class CallGateChannel
{
private readonly ThreadLocal<IpcContext> ipcExecutionContext = new();
/// <summary>
/// The actual storage.
/// </summary>
@ -145,6 +149,21 @@ internal class CallGateChannel
return (TRet)result;
}
internal void SetInvocationContext(IpcContext ipcContext)
{
this.ipcExecutionContext.Value = ipcContext;
}
internal IpcContext? GetInvocationContext()
{
return this.ipcExecutionContext.IsValueCreated ? this.ipcExecutionContext.Value : null;
}
internal void ClearInvocationContext()
{
this.ipcExecutionContext.Value = null;
}
private void CheckAndConvertArgs(object?[]? args, MethodInfo methodInfo)
{
var paramTypes = methodInfo.GetParameters()

View file

@ -1,3 +1,5 @@
using Dalamud.Plugin.Internal.Types;
#pragma warning disable SA1402 // File may only contain a single type
namespace Dalamud.Plugin.Ipc.Internal;
@ -5,9 +7,9 @@ namespace Dalamud.Plugin.Ipc.Internal;
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet>, ICallGateSubscriber<TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -43,9 +45,9 @@ internal class CallGatePubSub<TRet> : CallGatePubSubBase, ICallGateProvider<TRet
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<T1, TRet>, ICallGateSubscriber<T1, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -81,9 +83,9 @@ internal class CallGatePubSub<T1, TRet> : CallGatePubSubBase, ICallGateProvider<
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, TRet>, ICallGateSubscriber<T1, T2, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -119,9 +121,9 @@ internal class CallGatePubSub<T1, T2, TRet> : CallGatePubSubBase, ICallGateProvi
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, TRet>, ICallGateSubscriber<T1, T2, T3, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -157,9 +159,9 @@ internal class CallGatePubSub<T1, T2, T3, TRet> : CallGatePubSubBase, ICallGateP
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, TRet>, ICallGateSubscriber<T1, T2, T3, T4, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -195,9 +197,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, TRet> : CallGatePubSubBase, ICallG
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -233,9 +235,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, TRet> : CallGatePubSubBase, IC
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -271,9 +273,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, TRet> : CallGatePubSubBase
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}
@ -309,9 +311,9 @@ internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, TRet> : CallGatePubSub
/// <inheritdoc cref="CallGatePubSubBase"/>
internal class CallGatePubSub<T1, T2, T3, T4, T5, T6, T7, T8, TRet> : CallGatePubSubBase, ICallGateProvider<T1, T2, T3, T4, T5, T6, T7, T8, TRet>, ICallGateSubscriber<T1, T2, T3, T4, T5, T6, T7, T8, TRet>
{
/// <inheritdoc cref="CallGatePubSubBase(string)"/>
public CallGatePubSub(string name)
: base(name)
/// <inheritdoc cref="CallGatePubSubBase(string, LocalPlugin?)"/>
public CallGatePubSub(string name, LocalPlugin? owningPlugin = null)
: base(name, owningPlugin)
{
}

View file

@ -1,4 +1,11 @@
using System.Reactive.Disposables;
using System.Threading;
using Dalamud.Plugin.Internal.Types;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Utility;
using Serilog;
namespace Dalamud.Plugin.Ipc.Internal;
@ -11,9 +18,11 @@ internal abstract class CallGatePubSubBase
/// Initializes a new instance of the <see cref="CallGatePubSubBase"/> class.
/// </summary>
/// <param name="name">The name of the IPC registration.</param>
protected CallGatePubSubBase(string name)
/// <param name="owningPlugin">The plugin that owns this IPC pubsub.</param>
protected CallGatePubSubBase(string name, LocalPlugin? owningPlugin)
{
this.Channel = Service<CallGate>.Get().GetOrCreateChannel(name);
this.OwningPlugin = owningPlugin;
}
/// <summary>
@ -21,7 +30,7 @@ internal abstract class CallGatePubSubBase
/// <see cref="ICallGateSubscriber"/>s.
/// </summary>
public bool HasAction => this.Channel.Action != null;
/// <summary>
/// Gets a value indicating whether this IPC call gate has an associated Function. Only exposed to
/// <see cref="ICallGateSubscriber"/>s.
@ -33,12 +42,17 @@ internal abstract class CallGatePubSubBase
/// <see cref="ICallGateProvider"/>s, and can be used to determine if messages should be sent through the gate.
/// </summary>
public int SubscriptionCount => this.Channel.Subscriptions.Count;
/// <summary>
/// Gets the underlying channel implementation.
/// </summary>
protected CallGateChannel Channel { get; init; }
/// <summary>
/// Gets the plugin that owns this pubsub instance.
/// </summary>
protected LocalPlugin? OwningPlugin { get; init; }
/// <summary>
/// Removes the associated Action from this call gate, effectively disabling RPC calls.
/// </summary>
@ -53,6 +67,16 @@ internal abstract class CallGatePubSubBase
public void UnregisterFunc()
=> this.Channel.Func = null;
/// <summary>
/// Gets the current context for this IPC call. This will only be present when called from within an IPC action
/// or function handler, and will be null otherwise.
/// </summary>
/// <returns>Returns a potential IPC context.</returns>
public IpcContext? GetContext()
{
return this.Channel.GetInvocationContext();
}
/// <summary>
/// Registers a <see cref="Delegate"/> for use by other plugins via RPC. This Delegate must satisfy the constraints
/// of an <see cref="Action"/> type as defined by the interface, meaning they may not return a value and must have
@ -105,7 +129,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterAction"/>
/// <seealso cref="UnregisterAction"/>
private protected void InvokeAction(params object?[]? args)
=> this.Channel.InvokeAction(args);
{
using (this.BuildContext())
{
this.Channel.InvokeAction(args);
}
}
/// <summary>
/// Executes the Function registered for this IPC call gate via <see cref="RegisterFunc"/>. This method is intended
@ -120,7 +149,12 @@ internal abstract class CallGatePubSubBase
/// <seealso cref="RegisterFunc"/>
/// <seealso cref="UnregisterFunc"/>
private protected TRet InvokeFunc<TRet>(params object?[]? args)
=> this.Channel.InvokeFunc<TRet>(args);
{
using (this.BuildContext())
{
return this.Channel.InvokeFunc<TRet>(args);
}
}
/// <summary>
/// Send the given arguments to all subscribers (through <see cref="Subscribe"/>) of this IPC call gate. This method
@ -132,4 +166,14 @@ internal abstract class CallGatePubSubBase
/// <param name="args">Delegate arguments.</param>
private protected void SendMessage(params object?[]? args)
=> this.Channel.SendMessage(args);
private IDisposable BuildContext()
{
this.Channel.SetInvocationContext(new IpcContext
{
SourcePlugin = this.OwningPlugin != null ? new ExposedPlugin(this.OwningPlugin) : null,
});
return Disposable.Create(() => { this.Channel.ClearInvocationContext(); });
}
}

View file

@ -0,0 +1,15 @@
namespace Dalamud.Plugin.Ipc;
/// <summary>
/// The context associated for an IPC call. Reads from ThreadLocal.
/// </summary>
public class IpcContext
{
/// <summary>
/// Gets the plugin that initiated this IPC call.
/// </summary>
public IExposedPlugin? SourcePlugin { get; init; }
/// <inheritdoc/>
public override string ToString() => $"<IpcContext SourcePlugin={this.SourcePlugin?.Name ?? "null"}>";
}

View file

@ -0,0 +1,11 @@
namespace Dalamud.Plugin.VersionInfo;
/// <inheritdoc />
internal class DalamudVersionInfo(Version version, string? track) : IDalamudVersionInfo
{
/// <inheritdoc/>
public Version Version { get; } = version;
/// <inheritdoc/>
public string? BetaTrack { get; } = track;
}

View file

@ -0,0 +1,19 @@
namespace Dalamud.Plugin.VersionInfo;
/// <summary>
/// Interface exposing various information related to Dalamud versioning.
/// </summary>
public interface IDalamudVersionInfo
{
/// <summary>
/// Gets the Dalamud version.
/// </summary>
Version Version { get; }
/// <summary>
/// Gets the currently used beta track.
/// Please don't tell users to switch branches. They have it bad enough, fix your things instead.
/// Null if this build wasn't launched from XIVLauncher.
/// </summary>
string? BetaTrack { get; }
}

View file

@ -37,7 +37,7 @@ internal static class BugBait
Name = plugin.InternalName,
Version = isTesting ? plugin.TestingAssemblyVersion?.ToString() : plugin.AssemblyVersion.ToString(),
Platform = Util.GetHostPlatform().ToString(),
DalamudHash = Util.GetScmVersion(),
DalamudHash = Versioning.GetScmVersion(),
};
if (includeException)

View file

@ -38,7 +38,7 @@ internal class DalamudReleases : IServiceType
/// <returns>The version info for the current track.</returns>
public async Task<DalamudVersionInfo?> GetVersionForCurrentTrack()
{
var currentTrack = Util.GetActiveTrack();
var currentTrack = Versioning.GetActiveTrack();
if (currentTrack.IsNullOrEmpty())
return null;

View file

@ -69,11 +69,11 @@ public static class Troubleshooting
LoadedPlugins = pluginManager?.InstalledPlugins?.Select(x => x.Manifest as LocalPluginManifest)?.OrderByDescending(x => x.InternalName).ToArray(),
PluginStates = pluginManager?.InstalledPlugins?.Where(x => !x.IsDev).ToDictionary(x => x.Manifest.InternalName, x => x.IsBanned ? "Banned" : x.State.ToString()),
EverStartedLoadingPlugins = pluginManager?.InstalledPlugins.Where(x => x.HasEverStartedLoad).Select(x => x.InternalName).ToList(),
DalamudVersion = Util.GetScmVersion(),
DalamudGitHash = Util.GetGitHash() ?? "Unknown",
DalamudVersion = Versioning.GetScmVersion(),
DalamudGitHash = Versioning.GetGitHash() ?? "Unknown",
GameVersion = startInfo.GameVersion?.ToString() ?? "Unknown",
Language = startInfo.Language.ToString(),
BetaKey = Util.GetActiveTrack(),
BetaKey = Versioning.GetActiveTrack(),
DoPluginTest = configuration.DoPluginTest,
LoadAllApiLevels = pluginManager?.LoadAllApiLevels == true,
InterfaceLoaded = interfaceManager?.IsReady ?? false,

View file

@ -68,96 +68,10 @@ public static partial class Util
];
private static readonly Type GenericSpanType = typeof(Span<>);
private static string? scmVersionInternal;
private static string? gitHashInternal;
private static string? gitHashClientStructsInternal;
private static string? branchInternal;
private static ulong moduleStartAddr;
private static ulong moduleEndAddr;
/// <summary>
/// Gets the Dalamud version.
/// </summary>
[Api13ToDo("Remove. Make both versions here internal. Add an API somewhere.")]
public static string AssemblyVersion { get; } =
Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!.ToString();
/// <summary>
/// Gets the Dalamud version.
/// </summary>
internal static Version AssemblyVersionParsed { get; } =
Assembly.GetAssembly(typeof(ChatHandlers))!.GetName().Version!;
/// <summary>
/// Gets the SCM Version from the assembly, or null if it cannot be found. This method will generally return
/// the <c>git describe</c> output for this build, which will be a raw version if this is a stable build or an
/// appropriately-annotated version if this is *not* stable. Local builds will return a `Local Build` text string.
/// </summary>
/// <returns>The SCM version of the assembly.</returns>
public static string GetScmVersion()
{
if (scmVersionInternal != null) return scmVersionInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
return scmVersionInternal = attrs.First(a => a.Key == "SCMVersion").Value
?? asm.GetName().Version!.ToString();
}
/// <summary>
/// Gets the git commit hash value from the assembly or null if it cannot be found. Will be null for Debug builds,
/// and will be suffixed with `-dirty` if in release with pending changes.
/// </summary>
/// <returns>The git hash of the assembly.</returns>
public static string? GetGitHash()
{
if (gitHashInternal != null)
return gitHashInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
return gitHashInternal = attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value ?? "N/A";
}
/// <summary>
/// Gets the git hash value from the assembly or null if it cannot be found.
/// </summary>
/// <returns>The git hash of the assembly.</returns>
public static string? GetGitHashClientStructs()
{
if (gitHashClientStructsInternal != null)
return gitHashClientStructsInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
gitHashClientStructsInternal = attrs.First(a => a.Key == "GitHashClientStructs").Value;
return gitHashClientStructsInternal;
}
/// <summary>
/// Gets the Git branch name this version of Dalamud was built from, or null, if this is a Debug build.
/// </summary>
/// <returns>The branch name.</returns>
public static string? GetGitBranch()
{
if (branchInternal != null)
return branchInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
var gitBranch = attrs.FirstOrDefault(a => a.Key == "GitBranch")?.Value;
if (gitBranch == null)
return null;
return branchInternal = gitBranch;
}
/// <inheritdoc cref="DescribeAddress(nint)"/>
public static unsafe string DescribeAddress(void* p) => DescribeAddress((nint)p);
@ -693,16 +607,6 @@ public static partial class Util
}
}
/// <summary>
/// Gets the active Dalamud track, if this instance was launched through XIVLauncher and used a version
/// downloaded from webservices.
/// </summary>
/// <returns>The name of the track, or null.</returns>
internal static string? GetActiveTrack()
{
return Environment.GetEnvironmentVariable("DALAMUD_BRANCH");
}
/// <summary>
/// Gets a random, inoffensive, human-friendly string.
/// </summary>

View file

@ -0,0 +1,108 @@
using System.Linq;
using System.Reflection;
namespace Dalamud.Utility;
/// <summary>
/// Helpers to access Dalamud versioning information.
/// </summary>
internal static class Versioning
{
private static string? scmVersionInternal;
private static string? gitHashInternal;
private static string? gitHashClientStructsInternal;
private static string? branchInternal;
/// <summary>
/// Gets the Dalamud version.
/// </summary>
/// <returns>The raw Dalamud assembly version.</returns>
internal static string GetAssemblyVersion() =>
Assembly.GetAssembly(typeof(Versioning))!.GetName().Version!.ToString();
/// <summary>
/// Gets the Dalamud version.
/// </summary>
/// <returns>The parsed Dalamud assembly version.</returns>
internal static Version GetAssemblyVersionParsed() =>
Assembly.GetAssembly(typeof(Versioning))!.GetName().Version!;
/// <summary>
/// Gets the SCM Version from the assembly, or null if it cannot be found. This method will generally return
/// the <c>git describe</c> output for this build, which will be a raw version if this is a stable build or an
/// appropriately-annotated version if this is *not* stable. Local builds will return a `Local Build` text string.
/// </summary>
/// <returns>The SCM version of the assembly.</returns>
internal static string GetScmVersion()
{
if (scmVersionInternal != null) return scmVersionInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
return scmVersionInternal = attrs.First(a => a.Key == "SCMVersion").Value
?? asm.GetName().Version!.ToString();
}
/// <summary>
/// Gets the git commit hash value from the assembly or null if it cannot be found. Will be null for Debug builds,
/// and will be suffixed with `-dirty` if in release with pending changes.
/// </summary>
/// <returns>The git hash of the assembly.</returns>
internal static string? GetGitHash()
{
if (gitHashInternal != null)
return gitHashInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
return gitHashInternal = attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value ?? "N/A";
}
/// <summary>
/// Gets the git hash value from the assembly or null if it cannot be found.
/// </summary>
/// <returns>The git hash of the assembly.</returns>
internal static string? GetGitHashClientStructs()
{
if (gitHashClientStructsInternal != null)
return gitHashClientStructsInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
gitHashClientStructsInternal = attrs.First(a => a.Key == "GitHashClientStructs").Value;
return gitHashClientStructsInternal;
}
/// <summary>
/// Gets the Git branch name this version of Dalamud was built from, or null, if this is a Debug build.
/// </summary>
/// <returns>The branch name.</returns>
internal static string? GetGitBranch()
{
if (branchInternal != null)
return branchInternal;
var asm = typeof(Util).Assembly;
var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
var gitBranch = attrs.FirstOrDefault(a => a.Key == "GitBranch")?.Value;
if (gitBranch == null)
return null;
return branchInternal = gitBranch;
}
/// <summary>
/// Gets the active Dalamud track, if this instance was launched through XIVLauncher and used a version
/// downloaded from webservices.
/// </summary>
/// <returns>The name of the track, or null.</returns>
internal static string? GetActiveTrack()
{
return Environment.GetEnvironmentVariable("DALAMUD_BRANCH");
}
}

View file

@ -6,38 +6,35 @@
<ItemGroup>
<!-- Analyzers -->
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="4.14.0" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.2" />
<PackageVersion Include="JetBrains.Annotations" Version="2025.2.4" />
<!-- Misc Libraries -->
<PackageVersion Include="BitFaster.Caching" Version="2.4.1" />
<PackageVersion Include="BitFaster.Caching" Version="2.5.4" />
<PackageVersion Include="CheapLoc" Version="1.1.8" />
<PackageVersion Include="MinSharp" Version="1.0.4" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Lumina" Version="6.5.1" />
<PackageVersion Include="Microsoft.Extensions.ObjectPool" Version="10.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="10.0.0" />
<PackageVersion Include="System.Drawing.Common" Version="10.0.0" />
<PackageVersion Include="System.Reactive" Version="5.0.0" />
<PackageVersion Include="System.Reactive" Version="6.1.0" />
<PackageVersion Include="System.Reflection.MetadataLoadContext" Version="10.0.0" />
<PackageVersion Include="System.Resources.Extensions" Version="10.0.0" />
<PackageVersion Include="DotNet.ReproducibleBuilds" Version="1.2.39" />
<PackageVersion Include="sqlite-net-pcl" Version="1.8.116" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<!-- DirectX / Win32 -->
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.5" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.259" />
<!-- Logging -->
<PackageVersion Include="Serilog" Version="4.0.2" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="Serilog" Version="4.3.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="2.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<!-- Injector Utilities -->
<PackageVersion Include="Iced" Version="1.17.0" />
<PackageVersion Include="PeNet" Version="2.6.4" />
<PackageVersion Include="Iced" Version="1.21.0" />
<PackageVersion Include="PeNet" Version="5.1.0" />
<!-- HexaGen -->
<PackageVersion Include="HexaGen.Runtime" Version="1.1.20" />
@ -52,15 +49,15 @@
<PackageVersion Include="StreamJsonRpc" Version="2.22.23" />
<!-- Unit Testing -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageVersion Include="xunit" Version="2.4.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.abstractions" Version="2.0.3" />
<PackageVersion Include="xunit.analyzers" Version="0.10.0" />
<PackageVersion Include="xunit.assert" Version="2.4.1" />
<PackageVersion Include="xunit.core" Version="2.4.1" />
<PackageVersion Include="xunit.extensibility.core" Version="2.4.1" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageVersion Include="xunit.runner.console" Version="2.4.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.4.3" />
<PackageVersion Include="xunit.analyzers" Version="1.26.0" />
<PackageVersion Include="xunit.assert" Version="2.9.3" />
<PackageVersion Include="xunit.core" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.core" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageVersion Include="xunit.runner.console" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
</Project>