Compare commits
No commits in common. "master" and "v3.1" have entirely different histories.
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
|||||||
[submodule "LLib"]
|
|
||||||
path = LLib
|
|
||||||
url = https://git.carvel.li/liza/LLib
|
|
1
LLib
1
LLib
@ -1 +0,0 @@
|
|||||||
Subproject commit 7027d291efbbff6a55944dd521d3907210ddecbe
|
|
@ -2,8 +2,6 @@
|
|||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RetainerTrack", "RetainerTrack\RetainerTrack.csproj", "{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RetainerTrack", "RetainerTrack\RetainerTrack.csproj", "{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{3095F0BF-355A-4D13-BDDE-CCB4CA48D3C3}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -14,9 +12,5 @@ Global
|
|||||||
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Release|Any CPU.Build.0 = Release|Any CPU
|
{5FA75994-45B8-4E1A-A9D6-F28CD4C52342}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{3095F0BF-355A-4D13-BDDE-CCB4CA48D3C3}.Debug|Any CPU.ActiveCfg = Debug|x64
|
|
||||||
{3095F0BF-355A-4D13-BDDE-CCB4CA48D3C3}.Debug|Any CPU.Build.0 = Debug|x64
|
|
||||||
{3095F0BF-355A-4D13-BDDE-CCB4CA48D3C3}.Release|Any CPU.ActiveCfg = Debug|x64
|
|
||||||
{3095F0BF-355A-4D13-BDDE-CCB4CA48D3C3}.Release|Any CPU.Build.0 = Debug|x64
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
@ -990,7 +990,7 @@ csharp_space_around_binary_operators = before_and_after
|
|||||||
csharp_using_directive_placement = outside_namespace:silent
|
csharp_using_directive_placement = outside_namespace:silent
|
||||||
csharp_prefer_simple_using_statement = true:suggestion
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
csharp_prefer_braces = true:silent
|
csharp_prefer_braces = true:silent
|
||||||
csharp_style_namespace_declarations = file_scoped:suggestion
|
csharp_style_namespace_declarations = block_scoped:silent
|
||||||
csharp_style_prefer_method_group_conversion = true:silent
|
csharp_style_prefer_method_group_conversion = true:silent
|
||||||
csharp_style_prefer_top_level_statements = true:silent
|
csharp_style_prefer_top_level_statements = true:silent
|
||||||
csharp_style_prefer_primary_constructors = true:suggestion
|
csharp_style_prefer_primary_constructors = true:suggestion
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Dalamud.Game.ClientState.Objects;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using RetainerTrack.Handlers;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Commands
|
|
||||||
{
|
|
||||||
internal sealed class AccountIdCommand : IDisposable
|
|
||||||
{
|
|
||||||
private readonly ICommandManager _commandManager;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly ITargetManager _targetManager;
|
|
||||||
private readonly IChatGui _chatGui;
|
|
||||||
private readonly PersistenceContext _persistenceContext;
|
|
||||||
|
|
||||||
public AccountIdCommand(ICommandManager commandManager, IClientState clientState, ITargetManager targetManager,
|
|
||||||
IChatGui chatGui, PersistenceContext persistenceContext)
|
|
||||||
{
|
|
||||||
_commandManager = commandManager;
|
|
||||||
_clientState = clientState;
|
|
||||||
_targetManager = targetManager;
|
|
||||||
_chatGui = chatGui;
|
|
||||||
_persistenceContext = persistenceContext;
|
|
||||||
|
|
||||||
_commandManager.AddHandler("/accountid", new CommandInfo(ProcessCommand)
|
|
||||||
{
|
|
||||||
HelpMessage = "Shows the accountid of your target (or if no target, yourself)"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessCommand(string command, string arguments)
|
|
||||||
{
|
|
||||||
IGameObject? character = _targetManager.Target ?? _clientState.LocalPlayer;
|
|
||||||
if (character == null || character.ObjectKind != ObjectKind.Player)
|
|
||||||
return;
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var bc = (BattleChara*)character.Address;
|
|
||||||
_chatGui.Print($"{character.Name} has Account Id: {bc->AccountId}, Content Id: {bc->ContentId}");
|
|
||||||
|
|
||||||
_persistenceContext.HandleContentIdMapping([
|
|
||||||
new PlayerMapping
|
|
||||||
{
|
|
||||||
ContentId = bc->ContentId,
|
|
||||||
AccountId = bc->AccountId,
|
|
||||||
PlayerName = bc->NameString,
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_commandManager.RemoveHandler("/accountid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,14 +28,6 @@ namespace RetainerTrack.Database.Compiled
|
|||||||
sentinel: 0ul);
|
sentinel: 0ul);
|
||||||
localContentId.TypeMapping = SqliteULongTypeMapping.Default;
|
localContentId.TypeMapping = SqliteULongTypeMapping.Default;
|
||||||
|
|
||||||
var accountId = runtimeEntityType.AddProperty(
|
|
||||||
"AccountId",
|
|
||||||
typeof(ulong?),
|
|
||||||
propertyInfo: typeof(Player).GetProperty("AccountId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Player).GetField("<AccountId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
nullable: true);
|
|
||||||
accountId.TypeMapping = SqliteULongTypeMapping.Default;
|
|
||||||
|
|
||||||
var name = runtimeEntityType.AddProperty(
|
var name = runtimeEntityType.AddProperty(
|
||||||
"Name",
|
"Name",
|
||||||
typeof(string),
|
typeof(string),
|
||||||
|
@ -34,11 +34,6 @@ namespace RetainerTrack.Database.Compiled
|
|||||||
var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>();
|
var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>();
|
||||||
player.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings);
|
player.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings);
|
||||||
var retainerTrackDatabasePlayerTableBase = new TableBase("RetainerTrack.Database.Player", null, relationalModel);
|
var retainerTrackDatabasePlayerTableBase = new TableBase("RetainerTrack.Database.Player", null, relationalModel);
|
||||||
var accountIdColumnBase = new ColumnBase<ColumnMappingBase>("AccountId", "INTEGER", retainerTrackDatabasePlayerTableBase)
|
|
||||||
{
|
|
||||||
IsNullable = true
|
|
||||||
};
|
|
||||||
retainerTrackDatabasePlayerTableBase.Columns.Add("AccountId", accountIdColumnBase);
|
|
||||||
var localContentIdColumnBase = new ColumnBase<ColumnMappingBase>("LocalContentId", "INTEGER", retainerTrackDatabasePlayerTableBase);
|
var localContentIdColumnBase = new ColumnBase<ColumnMappingBase>("LocalContentId", "INTEGER", retainerTrackDatabasePlayerTableBase);
|
||||||
retainerTrackDatabasePlayerTableBase.Columns.Add("LocalContentId", localContentIdColumnBase);
|
retainerTrackDatabasePlayerTableBase.Columns.Add("LocalContentId", localContentIdColumnBase);
|
||||||
var nameColumnBase = new ColumnBase<ColumnMappingBase>("Name", "TEXT", retainerTrackDatabasePlayerTableBase);
|
var nameColumnBase = new ColumnBase<ColumnMappingBase>("Name", "TEXT", retainerTrackDatabasePlayerTableBase);
|
||||||
@ -48,7 +43,6 @@ namespace RetainerTrack.Database.Compiled
|
|||||||
retainerTrackDatabasePlayerTableBase.AddTypeMapping(retainerTrackDatabasePlayerMappingBase, false);
|
retainerTrackDatabasePlayerTableBase.AddTypeMapping(retainerTrackDatabasePlayerMappingBase, false);
|
||||||
defaultTableMappings.Add(retainerTrackDatabasePlayerMappingBase);
|
defaultTableMappings.Add(retainerTrackDatabasePlayerMappingBase);
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)localContentIdColumnBase, player.FindProperty("LocalContentId")!, retainerTrackDatabasePlayerMappingBase);
|
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)localContentIdColumnBase, player.FindProperty("LocalContentId")!, retainerTrackDatabasePlayerMappingBase);
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)accountIdColumnBase, player.FindProperty("AccountId")!, retainerTrackDatabasePlayerMappingBase);
|
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)nameColumnBase, player.FindProperty("Name")!, retainerTrackDatabasePlayerMappingBase);
|
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)nameColumnBase, player.FindProperty("Name")!, retainerTrackDatabasePlayerMappingBase);
|
||||||
|
|
||||||
var tableMappings = new List<TableMapping>();
|
var tableMappings = new List<TableMapping>();
|
||||||
@ -56,11 +50,6 @@ namespace RetainerTrack.Database.Compiled
|
|||||||
var playersTable = new Table("Players", null, relationalModel);
|
var playersTable = new Table("Players", null, relationalModel);
|
||||||
var localContentIdColumn = new Column("LocalContentId", "INTEGER", playersTable);
|
var localContentIdColumn = new Column("LocalContentId", "INTEGER", playersTable);
|
||||||
playersTable.Columns.Add("LocalContentId", localContentIdColumn);
|
playersTable.Columns.Add("LocalContentId", localContentIdColumn);
|
||||||
var accountIdColumn = new Column("AccountId", "INTEGER", playersTable)
|
|
||||||
{
|
|
||||||
IsNullable = true
|
|
||||||
};
|
|
||||||
playersTable.Columns.Add("AccountId", accountIdColumn);
|
|
||||||
var nameColumn = new Column("Name", "TEXT", playersTable);
|
var nameColumn = new Column("Name", "TEXT", playersTable);
|
||||||
playersTable.Columns.Add("Name", nameColumn);
|
playersTable.Columns.Add("Name", nameColumn);
|
||||||
var pK_Players = new UniqueConstraint("PK_Players", playersTable, new[] { localContentIdColumn });
|
var pK_Players = new UniqueConstraint("PK_Players", playersTable, new[] { localContentIdColumn });
|
||||||
@ -76,7 +65,6 @@ namespace RetainerTrack.Database.Compiled
|
|||||||
playersTable.AddTypeMapping(playersTableMapping, false);
|
playersTable.AddTypeMapping(playersTableMapping, false);
|
||||||
tableMappings.Add(playersTableMapping);
|
tableMappings.Add(playersTableMapping);
|
||||||
RelationalModel.CreateColumnMapping(localContentIdColumn, player.FindProperty("LocalContentId")!, playersTableMapping);
|
RelationalModel.CreateColumnMapping(localContentIdColumn, player.FindProperty("LocalContentId")!, playersTableMapping);
|
||||||
RelationalModel.CreateColumnMapping(accountIdColumn, player.FindProperty("AccountId")!, playersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(nameColumn, player.FindProperty("Name")!, playersTableMapping);
|
RelationalModel.CreateColumnMapping(nameColumn, player.FindProperty("Name")!, playersTableMapping);
|
||||||
|
|
||||||
var retainer = FindEntityType("RetainerTrack.Database.Retainer")!;
|
var retainer = FindEntityType("RetainerTrack.Database.Retainer")!;
|
||||||
|
@ -4,23 +4,97 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
|
using LiteDB;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using RetainerTrack.LegacyDb;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
#pragma warning disable 0612
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
namespace RetainerTrack.Database.Migrations
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class ImportLegacyData : Migration
|
public partial class ImportLegacyData : Migration
|
||||||
{
|
{
|
||||||
|
public static DalamudPluginInterface PluginInterface { get; set; }
|
||||||
|
|
||||||
|
private static readonly string[] PlayerColumns = new[] { "LocalContentId", "Name" };
|
||||||
|
private static readonly string[] RetainerColumns = new []{ "LocalContentId", "Name", "WorldId", "OwnerLocalContentId" };
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
|
if (PluginInterface == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string legacyDatabaseFileName = Path.Join(PluginInterface.GetPluginConfigDirectory(), "retainer-data.litedb");
|
||||||
|
if (!File.Exists(legacyDatabaseFileName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
using var liteDatabase = new LiteDatabase(new ConnectionString
|
||||||
|
{
|
||||||
|
Filename = Path.Join(PluginInterface.GetPluginConfigDirectory(), "retainer-data.litedb"),
|
||||||
|
Connection = ConnectionType.Direct,
|
||||||
|
Upgrade = true,
|
||||||
|
},
|
||||||
|
new BsonMapper
|
||||||
|
{
|
||||||
|
ResolveCollectionName = (type) =>
|
||||||
|
{
|
||||||
|
if (type == typeof(LegacyPlayer))
|
||||||
|
return LegacyPlayer.CollectionName;
|
||||||
|
|
||||||
|
if (type == typeof(LegacyRetainer))
|
||||||
|
return LegacyRetainer.CollectionName;
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(type));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
liteDatabase.GetCollection<LegacyRetainer>()
|
||||||
|
.EnsureIndex(x => x.Id);
|
||||||
|
liteDatabase.GetCollection<LegacyPlayer>()
|
||||||
|
.EnsureIndex(x => x.Id);
|
||||||
|
|
||||||
|
List<LegacyPlayer> allPlayers = liteDatabase.GetCollection<LegacyPlayer>().FindAll().ToList();
|
||||||
|
object[,] playersToInsert = To2DArray(
|
||||||
|
allPlayers.Select(player => new object[] { player.Id, player.Name }).ToList(),
|
||||||
|
PlayerColumns.Length);
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table:"Players",
|
||||||
|
columns: PlayerColumns,
|
||||||
|
values: playersToInsert);
|
||||||
|
|
||||||
|
List<LegacyRetainer> allRetainers = liteDatabase.GetCollection<LegacyRetainer>().FindAll().ToList();
|
||||||
|
object[,] retainersToInsert = To2DArray(
|
||||||
|
allRetainers.Select(retainer => new object[] { retainer.Id, retainer.Name, retainer.WorldId, retainer.OwnerContentId }).ToList(),
|
||||||
|
RetainerColumns.Length);
|
||||||
|
|
||||||
|
migrationBuilder.InsertData(
|
||||||
|
table: "Retainers",
|
||||||
|
columns: RetainerColumns, values: retainersToInsert);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("Performance", "CA1814")]
|
||||||
|
private static object[,] To2DArray(IReadOnlyList<object[]> data, int columnCount)
|
||||||
|
{
|
||||||
|
object[,] result = new object[data.Count, columnCount];
|
||||||
|
for (int i = 0; i < data.Count; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < columnCount; j++)
|
||||||
|
{
|
||||||
|
result[i, j] = data[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
|
migrationBuilder.Sql("DELETE FROM Players");
|
||||||
|
migrationBuilder.Sql("DELETE FROM Retainers");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning restore 0612
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using RetainerTrack.Database;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(RetainerTrackContext))]
|
|
||||||
[Migration("20240629073047_AddAccountIdToPlayer")]
|
|
||||||
partial class AddAccountIdToPlayer
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.5");
|
|
||||||
|
|
||||||
modelBuilder.Entity("RetainerTrack.Database.Player", b =>
|
|
||||||
{
|
|
||||||
b.Property<ulong>("LocalContentId")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<ulong?>("AccountId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(20)
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.HasKey("LocalContentId");
|
|
||||||
|
|
||||||
b.ToTable("Players");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("RetainerTrack.Database.Retainer", b =>
|
|
||||||
{
|
|
||||||
b.Property<ulong>("LocalContentId")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(24)
|
|
||||||
.HasColumnType("TEXT");
|
|
||||||
|
|
||||||
b.Property<ulong>("OwnerLocalContentId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<ushort>("WorldId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.HasKey("LocalContentId");
|
|
||||||
|
|
||||||
b.ToTable("Retainers");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddAccountIdToPlayer : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<ulong>(
|
|
||||||
name: "AccountId",
|
|
||||||
table: "Players",
|
|
||||||
type: "INTEGER",
|
|
||||||
nullable: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "AccountId",
|
|
||||||
table: "Players");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,4 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
@ -23,9 +22,6 @@ namespace RetainerTrack.Database.Migrations
|
|||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<ulong?>("AccountId")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(20)
|
.HasMaxLength(20)
|
||||||
|
@ -9,6 +9,4 @@ public class Player
|
|||||||
|
|
||||||
[MaxLength(20), Required]
|
[MaxLength(20), Required]
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
public ulong? AccountId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
7
RetainerTrack/Handlers/ContentIdToName.cs
Normal file
7
RetainerTrack/Handlers/ContentIdToName.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace RetainerTrack.Handlers;
|
||||||
|
|
||||||
|
internal sealed class ContentIdToName
|
||||||
|
{
|
||||||
|
public ulong ContentId { get; init; }
|
||||||
|
public string PlayerName { get; init; } = string.Empty;
|
||||||
|
}
|
@ -7,7 +7,6 @@ using System.Threading.Tasks;
|
|||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@ -37,8 +36,7 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
|
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
|
|
||||||
public GameHooks(ILogger<GameHooks> logger, PersistenceContext persistenceContext,
|
public GameHooks(ILogger<GameHooks> logger, PersistenceContext persistenceContext, IGameInteropProvider gameInteropProvider)
|
||||||
IGameInteropProvider gameInteropProvider)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_persistenceContext = persistenceContext;
|
_persistenceContext = persistenceContext;
|
||||||
@ -55,10 +53,9 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var mapping = new PlayerMapping
|
var mapping = new ContentIdToName
|
||||||
{
|
{
|
||||||
ContentId = contentId,
|
ContentId = contentId,
|
||||||
AccountId = null,
|
|
||||||
PlayerName = MemoryHelper.ReadString(new nint(playerName), Encoding.ASCII, 32),
|
PlayerName = MemoryHelper.ReadString(new nint(playerName), Encoding.ASCII, 32),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,8 +63,7 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
{
|
{
|
||||||
_logger.LogTrace("Content id {ContentId} belongs to '{Name}'", mapping.ContentId,
|
_logger.LogTrace("Content id {ContentId} belongs to '{Name}'", mapping.ContentId,
|
||||||
mapping.PlayerName);
|
mapping.PlayerName);
|
||||||
if (mapping.PlayerName.IsValidCharacterName())
|
Task.Run(() => _persistenceContext.HandleContentIdMapping(mapping));
|
||||||
Task.Run(() => _persistenceContext.HandleContentIdMapping(new List<PlayerMapping> { mapping }));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -88,23 +84,19 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = Marshal.PtrToStructure<SocialListResultPage>(dataPtr);
|
var result = Marshal.PtrToStructure<SocialListResultPage>(dataPtr);
|
||||||
List<PlayerMapping> mappings = new();
|
List<ContentIdToName> mappings = new();
|
||||||
foreach (SocialListPlayer player in result.PlayerSpan)
|
foreach (SocialListPlayer player in result.PlayerSpan)
|
||||||
{
|
{
|
||||||
if (player.ContentId == 0)
|
var mapping = new ContentIdToName
|
||||||
continue;
|
|
||||||
|
|
||||||
var mapping = new PlayerMapping
|
|
||||||
{
|
{
|
||||||
ContentId = player.ContentId,
|
ContentId = player.ContentId,
|
||||||
AccountId = player.AccountId != 0 ? player.AccountId : null,
|
|
||||||
PlayerName = MemoryHelper.ReadString(new nint(player.CharacterName), Encoding.ASCII, 32),
|
PlayerName = MemoryHelper.ReadString(new nint(player.CharacterName), Encoding.ASCII, 32),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(mapping.PlayerName))
|
if (!string.IsNullOrEmpty(mapping.PlayerName))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Content id {ContentId} belongs to '{Name}' ({AccountId})", mapping.ContentId,
|
_logger.LogTrace("Content id {ContentId} belongs to '{Name}'", mapping.ContentId,
|
||||||
mapping.PlayerName, mapping.AccountId);
|
mapping.PlayerName);
|
||||||
mappings.Add(mapping);
|
mappings.Add(mapping);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -144,15 +136,15 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
///
|
///
|
||||||
/// Both 1 and 2 are sent to you on login, unprompted.
|
/// Both 1 and 2 are sent to you on login, unprompted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x420)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x380)]
|
||||||
internal struct SocialListResultPage
|
internal struct SocialListResultPage
|
||||||
{
|
{
|
||||||
[FieldOffset(0x10)] private fixed byte Players[10 * 0x70];
|
[FieldOffset(0x10)] private fixed byte Players[10 * 0x58];
|
||||||
|
|
||||||
public Span<SocialListPlayer> PlayerSpan => new(Unsafe.AsPointer(ref Players[0]), 10);
|
public Span<SocialListPlayer> PlayerSpan => new(Unsafe.AsPointer(ref Players[0]), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 0x70, Pack = 1)]
|
[StructLayout(LayoutKind.Explicit, Size = 0x58)]
|
||||||
internal struct SocialListPlayer
|
internal struct SocialListPlayer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -161,14 +153,9 @@ internal sealed unsafe class GameHooks : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[FieldOffset(0x00)] public readonly ulong ContentId;
|
[FieldOffset(0x00)] public readonly ulong ContentId;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Only seems to be set for certain kind of social lists, e.g. friend list/FC members doesn't include any.
|
|
||||||
/// </summary>
|
|
||||||
[FieldOffset(0x18)] public readonly ulong AccountId;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This *can* be empty, e.g. if you're querying your friend list, the names are ONLY set for characters on the same world.
|
/// This *can* be empty, e.g. if you're querying your friend list, the names are ONLY set for characters on the same world.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FieldOffset(0x44)] public fixed byte CharacterName[32];
|
[FieldOffset(0x31)] public fixed byte CharacterName[32];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,64 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Game.Network.Structures;
|
using Dalamud.Game.Network.Structures;
|
||||||
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
namespace RetainerTrack.Handlers;
|
||||||
|
|
||||||
internal sealed class MarketBoardOfferingsHandler : IDisposable
|
internal sealed class MarketBoardOfferingsHandler : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IMarketBoard _marketBoard;
|
private unsafe delegate void* MarketBoardOfferings(InfoProxyItemSearch* a1, nint packetData);
|
||||||
|
|
||||||
private readonly ILogger<MarketBoardOfferingsHandler> _logger;
|
private readonly ILogger<MarketBoardOfferingsHandler> _logger;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly PersistenceContext _persistenceContext;
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
private readonly Hook<MarketBoardOfferings> _marketBoardOfferingsHook;
|
||||||
|
|
||||||
public MarketBoardOfferingsHandler(
|
public unsafe MarketBoardOfferingsHandler(
|
||||||
IMarketBoard marketBoard,
|
|
||||||
ILogger<MarketBoardOfferingsHandler> logger,
|
ILogger<MarketBoardOfferingsHandler> logger,
|
||||||
IClientState clientState,
|
IClientState clientState,
|
||||||
|
IGameInteropProvider gameInteropProvider,
|
||||||
PersistenceContext persistenceContext)
|
PersistenceContext persistenceContext)
|
||||||
{
|
{
|
||||||
_marketBoard = marketBoard;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_persistenceContext = persistenceContext;
|
_persistenceContext = persistenceContext;
|
||||||
|
|
||||||
_marketBoard.OfferingsReceived += HandleOfferings;
|
_logger.LogDebug("Setting up offerings hook");
|
||||||
|
_marketBoardOfferingsHook =
|
||||||
|
gameInteropProvider.HookFromSignature<MarketBoardOfferings>("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 0F B6 82 ?? ?? ?? ?? 48 8B FA 48 8B D9 38 41 19 74 54",
|
||||||
|
MarketBoardOfferingsDetour);
|
||||||
|
_marketBoardOfferingsHook.Enable();
|
||||||
|
_logger.LogDebug("Offerings hook enabled successfully");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_marketBoard.OfferingsReceived += HandleOfferings;
|
_marketBoardOfferingsHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleOfferings(IMarketBoardCurrentOfferings currentOfferings)
|
// adapted from https://github.com/tesu/PennyPincher/commit/0f9b3963fd4a6e9b87f585ee491d4de59a93f7a3
|
||||||
|
private unsafe void* MarketBoardOfferingsDetour(InfoProxyItemSearch* a1, nint packetData)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (packetData != nint.Zero)
|
||||||
|
{
|
||||||
|
ParseOfferings(packetData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Could not parse marketboard offerings.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return _marketBoardOfferingsHook.Original(a1, packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseOfferings(nint dataPtr)
|
||||||
{
|
{
|
||||||
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
|
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
|
||||||
if (worldId == 0)
|
if (worldId == 0)
|
||||||
@ -41,6 +67,7 @@ internal sealed class MarketBoardOfferingsHandler : IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task.Run(() => _persistenceContext.HandleMarketBoardPage(currentOfferings, worldId));
|
var listings = MarketBoardCurrentOfferings.Read(dataPtr);
|
||||||
|
Task.Run(() => _persistenceContext.HandleMarketBoardPage(listings, worldId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,17 +51,11 @@ internal sealed unsafe class MarketBoardUiHandler : IDisposable
|
|||||||
for (int i = 0; i < length; ++i)
|
for (int i = 0; i < length; ++i)
|
||||||
{
|
{
|
||||||
var listItem = results->ItemRendererList[i].AtkComponentListItemRenderer;
|
var listItem = results->ItemRendererList[i].AtkComponentListItemRenderer;
|
||||||
if (listItem == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var uldManager = listItem->AtkComponentButton.AtkComponentBase.UldManager;
|
var uldManager = listItem->AtkComponentButton.AtkComponentBase.UldManager;
|
||||||
if (uldManager.NodeListCount < 14)
|
if (uldManager.NodeListCount < 14)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var retainerNameNode = (AtkTextNode*)uldManager.NodeList[5];
|
var retainerNameNode = (AtkTextNode*)uldManager.NodeList[5];
|
||||||
if (retainerNameNode == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
string retainerName = retainerNameNode->NodeText.ToString();
|
string retainerName = retainerNameNode->NodeText.ToString();
|
||||||
if (!retainerName.Contains('(', StringComparison.Ordinal))
|
if (!retainerName.Contains('(', StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
|
||||||
using Dalamud.Memory;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
|
||||||
|
|
||||||
internal sealed class ObjectTableHandler : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IObjectTable _objectTable;
|
|
||||||
private readonly IFramework _framework;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly ILogger<ObjectTableHandler> _logger;
|
|
||||||
private readonly PersistenceContext _persistenceContext;
|
|
||||||
|
|
||||||
private long _lastUpdate;
|
|
||||||
|
|
||||||
public ObjectTableHandler(IObjectTable objectTable, IFramework framework, IClientState clientState, ILogger<ObjectTableHandler> logger, PersistenceContext persistenceContext)
|
|
||||||
{
|
|
||||||
_objectTable = objectTable;
|
|
||||||
_framework = framework;
|
|
||||||
_clientState = clientState;
|
|
||||||
_logger = logger;
|
|
||||||
_persistenceContext = persistenceContext;
|
|
||||||
|
|
||||||
_framework.Update += FrameworkUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe void FrameworkUpdate(IFramework framework)
|
|
||||||
{
|
|
||||||
long now = Environment.TickCount64;
|
|
||||||
if (!_clientState.IsLoggedIn || _clientState.IsPvPExcludingDen || now - _lastUpdate < 30_000)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastUpdate = now;
|
|
||||||
|
|
||||||
List<PlayerMapping> playerMappings = new();
|
|
||||||
foreach (var obj in _objectTable)
|
|
||||||
{
|
|
||||||
if (obj.ObjectKind == ObjectKind.Player)
|
|
||||||
{
|
|
||||||
var bc = (BattleChara*)obj.Address;
|
|
||||||
if (bc->ContentId == 0 || bc->AccountId == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
playerMappings.Add(new PlayerMapping
|
|
||||||
{
|
|
||||||
ContentId = bc->ContentId,
|
|
||||||
AccountId = bc->AccountId,
|
|
||||||
PlayerName = bc->NameString,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playerMappings.Count > 0)
|
|
||||||
Task.Run(() => _persistenceContext.HandleContentIdMapping(playerMappings));
|
|
||||||
|
|
||||||
_logger.LogTrace("ObjectTable handling for {Count} players took {TimeMs}", playerMappings.Count, TimeSpan.FromMilliseconds(Environment.TickCount64 - now));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_framework.Update -= FrameworkUpdate;
|
|
||||||
}
|
|
||||||
}
|
|
68
RetainerTrack/Handlers/PartyHandler.cs
Normal file
68
RetainerTrack/Handlers/PartyHandler.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||||
|
|
||||||
|
namespace RetainerTrack.Handlers;
|
||||||
|
|
||||||
|
internal sealed class PartyHandler : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IFramework _framework;
|
||||||
|
private readonly IClientState _clientState;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
|
||||||
|
private long _lastUpdate;
|
||||||
|
|
||||||
|
public PartyHandler(IFramework framework, IClientState clientState, PersistenceContext persistenceContext)
|
||||||
|
{
|
||||||
|
_framework = framework;
|
||||||
|
_clientState = clientState;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
|
|
||||||
|
_framework.Update += FrameworkUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void FrameworkUpdate(IFramework _)
|
||||||
|
{
|
||||||
|
long now = Environment.TickCount64;
|
||||||
|
if (!_clientState.IsLoggedIn || _clientState.IsPvPExcludingDen || now - _lastUpdate < 180_000)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_lastUpdate = now;
|
||||||
|
|
||||||
|
// skip if we're not in an alliance, party members are handled via social list updates
|
||||||
|
var groupManager = GroupManager.Instance();
|
||||||
|
if (groupManager->AllianceFlags == 0x0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<ContentIdToName> mappings = new();
|
||||||
|
foreach (var allianceMember in groupManager->AllianceMembersSpan)
|
||||||
|
HandlePartyMember(allianceMember, mappings);
|
||||||
|
|
||||||
|
if (mappings.Count > 0)
|
||||||
|
Task.Run(() => _persistenceContext.HandleContentIdMapping(mappings));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static unsafe void HandlePartyMember(PartyMember partyMember, List<ContentIdToName> contentIdToNames)
|
||||||
|
{
|
||||||
|
if (partyMember.ContentID == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string name = MemoryHelper.ReadStringNullTerminated((nint)partyMember.Name);
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return;
|
||||||
|
|
||||||
|
contentIdToNames.Add(new ContentIdToName
|
||||||
|
{
|
||||||
|
ContentId = (ulong)partyMember.ContentID,
|
||||||
|
PlayerName = name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_framework.Update -= FrameworkUpdate;
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,6 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Dalamud.Game.Network.Structures;
|
using Dalamud.Game.Network.Structures;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using RetainerTrack.Database;
|
using RetainerTrack.Database;
|
||||||
@ -18,7 +17,7 @@ internal sealed class PersistenceContext
|
|||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly IServiceProvider _serviceProvider;
|
||||||
private readonly ConcurrentDictionary<uint, ConcurrentDictionary<string, ulong>> _worldRetainerCache = new();
|
private readonly ConcurrentDictionary<uint, ConcurrentDictionary<string, ulong>> _worldRetainerCache = new();
|
||||||
private readonly ConcurrentDictionary<ulong, CachedPlayer> _playerCache = new();
|
private readonly ConcurrentDictionary<ulong, string> _playerNameCache = new();
|
||||||
|
|
||||||
public PersistenceContext(ILogger<PersistenceContext> logger, IClientState clientState,
|
public PersistenceContext(ILogger<PersistenceContext> logger, IClientState clientState,
|
||||||
IServiceProvider serviceProvider)
|
IServiceProvider serviceProvider)
|
||||||
@ -43,13 +42,7 @@ internal sealed class PersistenceContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (var player in dbContext.Players)
|
foreach (var player in dbContext.Players)
|
||||||
{
|
_playerNameCache[player.LocalContentId] = player.Name ?? string.Empty;
|
||||||
_playerCache[player.LocalContentId] = new CachedPlayer
|
|
||||||
{
|
|
||||||
AccountId = player.AccountId,
|
|
||||||
Name = player.Name ?? string.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +56,7 @@ internal sealed class PersistenceContext
|
|||||||
if (!currentWorldCache.TryGetValue(retainerName, out ulong playerContentId))
|
if (!currentWorldCache.TryGetValue(retainerName, out ulong playerContentId))
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
|
||||||
return _playerCache.TryGetValue(playerContentId, out CachedPlayer? cachedPlayer)
|
return _playerNameCache.TryGetValue(playerContentId, out string? playerName) ? playerName : string.Empty;
|
||||||
? cachedPlayer.Name
|
|
||||||
: string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IReadOnlyList<string> GetRetainerNamesForCharacter(string characterName, uint world)
|
public IReadOnlyList<string> GetRetainerNamesForCharacter(string characterName, uint world)
|
||||||
@ -82,14 +73,12 @@ internal sealed class PersistenceContext
|
|||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleMarketBoardPage(IMarketBoardCurrentOfferings currentOfferings, ushort worldId)
|
public void HandleMarketBoardPage(MarketBoardCurrentOfferings listings, ushort worldId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var updates =
|
var updates =
|
||||||
currentOfferings.ItemListings
|
listings.ItemListings.DistinctBy(o => o.RetainerId)
|
||||||
.Cast<MarketBoardCurrentOfferings.MarketBoardItemListing>()
|
|
||||||
.DistinctBy(o => o.RetainerId)
|
|
||||||
.Where(l => l.RetainerId != 0)
|
.Where(l => l.RetainerId != 0)
|
||||||
.Where(l => l.RetainerOwnerId != 0)
|
.Where(l => l.RetainerOwnerId != 0)
|
||||||
.Select(l =>
|
.Select(l =>
|
||||||
@ -100,48 +89,29 @@ internal sealed class PersistenceContext
|
|||||||
WorldId = worldId,
|
WorldId = worldId,
|
||||||
OwnerLocalContentId = l.RetainerOwnerId,
|
OwnerLocalContentId = l.RetainerOwnerId,
|
||||||
})
|
})
|
||||||
.Where(mapping =>
|
|
||||||
{
|
|
||||||
if (mapping.Name == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var currentWorldCache = _worldRetainerCache.GetOrAdd(mapping.WorldId, _ => new());
|
|
||||||
if (currentWorldCache.TryGetValue(mapping.Name, out ulong playerContentId))
|
|
||||||
return mapping.OwnerLocalContentId != playerContentId;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.DistinctBy(x => x.LocalContentId)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
||||||
|
|
||||||
|
var ids = updates.Select(x => x.LocalContentId).ToList();
|
||||||
|
var dbRetainers = dbContext.Retainers.Where(x => ids.Contains(x.LocalContentId))
|
||||||
|
.ToDictionary(x => x.LocalContentId, x => x);
|
||||||
foreach (var retainer in updates)
|
foreach (var retainer in updates)
|
||||||
{
|
{
|
||||||
Retainer? dbRetainer = dbContext.Retainers.Find(retainer.LocalContentId);
|
if (dbRetainers.TryGetValue(retainer.LocalContentId, out var dbRetainer))
|
||||||
if (dbRetainer != null)
|
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Updating retainer {RetainerName} with {LocalContentId}", retainer.Name,
|
|
||||||
retainer.LocalContentId);
|
|
||||||
dbRetainer.Name = retainer.Name;
|
dbRetainer.Name = retainer.Name;
|
||||||
dbRetainer.WorldId = retainer.WorldId;
|
dbRetainer.WorldId = retainer.WorldId;
|
||||||
dbRetainer.OwnerLocalContentId = retainer.OwnerLocalContentId;
|
dbRetainer.OwnerLocalContentId = retainer.OwnerLocalContentId;
|
||||||
dbContext.Retainers.Update(dbRetainer);
|
dbContext.Retainers.Update(dbRetainer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
_logger.LogDebug("Adding retainer {RetainerName} with {LocalContentId}", retainer.Name,
|
|
||||||
retainer.LocalContentId);
|
|
||||||
dbContext.Retainers.Add(retainer);
|
dbContext.Retainers.Add(retainer);
|
||||||
}
|
|
||||||
|
|
||||||
string ownerName;
|
if (!_playerNameCache.TryGetValue(retainer.OwnerLocalContentId, out string? ownerName))
|
||||||
if (_playerCache.TryGetValue(retainer.OwnerLocalContentId, out CachedPlayer? cachedPlayer))
|
|
||||||
ownerName = cachedPlayer.Name;
|
|
||||||
else
|
|
||||||
ownerName = retainer.OwnerLocalContentId.ToString(CultureInfo.InvariantCulture);
|
ownerName = retainer.OwnerLocalContentId.ToString(CultureInfo.InvariantCulture);
|
||||||
_logger.LogDebug(" Retainer {RetainerName} belongs to {OwnerName}", retainer.Name,
|
_logger.LogTrace("Retainer {RetainerName} belongs to {OwnerId}", retainer.Name,
|
||||||
ownerName);
|
ownerName);
|
||||||
|
|
||||||
if (retainer.Name != null)
|
if (retainer.Name != null)
|
||||||
@ -151,9 +121,7 @@ internal sealed class PersistenceContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int changeCount = dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
if (changeCount > 0)
|
|
||||||
_logger.LogDebug("Saved {Count} retainer mappings", changeCount);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -161,129 +129,49 @@ internal sealed class PersistenceContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleContentIdMappingFallback(PlayerMapping mapping)
|
public void HandleContentIdMapping(ContentIdToName mapping)
|
||||||
|
=> HandleContentIdMapping(new List<ContentIdToName> { mapping });
|
||||||
|
|
||||||
|
public void HandleContentIdMapping(IReadOnlyList<ContentIdToName> mappings)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (mapping.ContentId == 0 || string.IsNullOrEmpty(mapping.PlayerName))
|
var updates = mappings.DistinctBy(x => x.ContentId)
|
||||||
return;
|
.Where(mapping => mapping.ContentId != 0 && !string.IsNullOrEmpty(mapping.PlayerName))
|
||||||
|
.Where(mapping =>
|
||||||
|
{
|
||||||
|
if (_playerNameCache.TryGetValue(mapping.ContentId, out string? existingName))
|
||||||
|
return mapping.PlayerName != existingName;
|
||||||
|
|
||||||
if (_playerCache.TryGetValue(mapping.ContentId, out CachedPlayer? cachedPlayer))
|
return true;
|
||||||
{
|
})
|
||||||
if (mapping.PlayerName == cachedPlayer.Name && mapping.AccountId == cachedPlayer.AccountId)
|
.Select(mapping =>
|
||||||
return;
|
new Player
|
||||||
}
|
|
||||||
|
|
||||||
using (var scope = _serviceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
var dbPlayer = dbContext.Players.Find(mapping.ContentId);
|
|
||||||
if (dbPlayer == null)
|
|
||||||
dbContext.Players.Add(new Player
|
|
||||||
{
|
{
|
||||||
LocalContentId = mapping.ContentId,
|
LocalContentId = mapping.ContentId,
|
||||||
Name = mapping.PlayerName,
|
Name = mapping.PlayerName,
|
||||||
AccountId = mapping.AccountId,
|
})
|
||||||
});
|
.ToList();
|
||||||
else
|
|
||||||
{
|
|
||||||
dbPlayer.Name = mapping.PlayerName;
|
|
||||||
dbPlayer.AccountId ??= mapping.AccountId;
|
|
||||||
dbContext.Entry(dbPlayer).State = EntityState.Modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
int changeCount = dbContext.SaveChanges();
|
|
||||||
if (changeCount > 0)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Saved fallback player mappings for {ContentId} / {Name} / {AccountId}",
|
|
||||||
mapping.ContentId, mapping.PlayerName, mapping.AccountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
_playerCache[mapping.ContentId] = new CachedPlayer
|
|
||||||
{
|
|
||||||
AccountId = mapping.AccountId,
|
|
||||||
Name = mapping.PlayerName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(e, "Could not persist singular mapping for {ContentId} / {Name} / {AccountId}",
|
|
||||||
mapping.ContentId, mapping.PlayerName, mapping.AccountId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleContentIdMapping(IReadOnlyList<PlayerMapping> mappings)
|
|
||||||
{
|
|
||||||
var updates = mappings.DistinctBy(x => x.ContentId)
|
|
||||||
.Where(mapping => mapping.ContentId != 0 && !string.IsNullOrEmpty(mapping.PlayerName))
|
|
||||||
.Where(mapping =>
|
|
||||||
{
|
|
||||||
if (_playerCache.TryGetValue(mapping.ContentId, out CachedPlayer? cachedPlayer))
|
|
||||||
return mapping.PlayerName != cachedPlayer.Name || mapping.AccountId != cachedPlayer.AccountId;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (updates.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var scope = _serviceProvider.CreateScope())
|
using (var scope = _serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
||||||
foreach (var update in updates)
|
foreach (var update in updates)
|
||||||
{
|
{
|
||||||
var dbPlayer = dbContext.Players.Find(update.ContentId);
|
if (!dbContext.Players.Any(x => x.LocalContentId == update.LocalContentId))
|
||||||
if (dbPlayer == null)
|
dbContext.Players.AddRange(updates);
|
||||||
dbContext.Players.Add(new Player
|
|
||||||
{
|
|
||||||
LocalContentId = update.ContentId,
|
|
||||||
Name = update.PlayerName,
|
|
||||||
AccountId = update.AccountId,
|
|
||||||
});
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dbPlayer.Name = update.PlayerName;
|
|
||||||
dbPlayer.AccountId ??= update.AccountId;
|
|
||||||
dbContext.Entry(dbPlayer).State = EntityState.Modified;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int changeCount = dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
if (changeCount > 0)
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Saved {Count} player mappings", changeCount);
|
|
||||||
foreach (var update in updates)
|
|
||||||
_logger.LogTrace(" {ContentId} = {Name} ({AccountId})", update.ContentId, update.PlayerName,
|
|
||||||
update.AccountId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var player in updates)
|
foreach (var player in updates)
|
||||||
{
|
_playerNameCache[player.LocalContentId] = player.Name ?? string.Empty;
|
||||||
_playerCache[player.ContentId] = new CachedPlayer
|
|
||||||
{
|
|
||||||
AccountId = player.AccountId,
|
|
||||||
Name = player.PlayerName,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(e, "Could not persist multiple mappings, attempting non-batch update");
|
_logger.LogError(e, "Could not persist multiple mappings");
|
||||||
foreach (var update in updates)
|
|
||||||
{
|
|
||||||
HandleContentIdMappingFallback(update);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CachedPlayer
|
|
||||||
{
|
|
||||||
public required ulong? AccountId { get; init; }
|
|
||||||
public required string Name { get; init; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
namespace RetainerTrack.Handlers;
|
|
||||||
|
|
||||||
internal sealed class PlayerMapping
|
|
||||||
{
|
|
||||||
public required ulong? AccountId { get; init; }
|
|
||||||
public required ulong ContentId { get; init; }
|
|
||||||
public required string PlayerName { get; init; } = string.Empty;
|
|
||||||
}
|
|
12
RetainerTrack/LegacyDb/LegacyPlayer.cs
Normal file
12
RetainerTrack/LegacyDb/LegacyPlayer.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RetainerTrack.LegacyDb;
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
|
internal sealed class LegacyPlayer
|
||||||
|
{
|
||||||
|
public static string CollectionName => "Player";
|
||||||
|
|
||||||
|
public ulong Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
}
|
14
RetainerTrack/LegacyDb/LegacyRetainer.cs
Normal file
14
RetainerTrack/LegacyDb/LegacyRetainer.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace RetainerTrack.LegacyDb;
|
||||||
|
|
||||||
|
[Obsolete]
|
||||||
|
internal sealed class LegacyRetainer
|
||||||
|
{
|
||||||
|
public static string CollectionName => "Retainer";
|
||||||
|
|
||||||
|
public ulong Id { get; set; }
|
||||||
|
public string? Name { get; set; }
|
||||||
|
public ushort WorldId { get; set; }
|
||||||
|
public ulong OwnerContentId { get; set; }
|
||||||
|
}
|
@ -1,20 +1,73 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>4.3</Version>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
|
<Version>3.1</Version>
|
||||||
|
<LangVersion>12.0</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
<OutputPath Condition="'$(Configuration)' != 'EF'">dist</OutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
|
<OutputPath>dist</OutputPath>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
|
<DebugType>portable</DebugType>
|
||||||
|
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Import Project="..\LLib\LLib.targets"/>
|
<PropertyGroup>
|
||||||
<Import Project="..\LLib\RenameZip.targets"/>
|
<DalamudLibPath>$(appdata)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
|
||||||
|
<DalamudLibPath>$(DALAMUD_HOME)/</DalamudLibPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="3.0.0" />
|
<PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="3.0.0" />
|
||||||
|
<PackageReference Include="DalamudPackager" Version="2.1.12" />
|
||||||
|
<PackageReference Include="LiteDB" Version="5.0.17" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5" Condition="'$(Configuration)' == 'EF'">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Dalamud">
|
||||||
|
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ImGui.NET">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ImGuiScene">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina.Excel">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FFXIVClientStructs">
|
||||||
|
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||||
|
<Private Condition="'$(Configuration)' != 'EF'">false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin" Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Dalamud.Extensions.MicrosoftLogging;
|
using Dalamud.Extensions.MicrosoftLogging;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.Data.Sqlite;
|
using Microsoft.Data.Sqlite;
|
||||||
@ -11,6 +10,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using RetainerTrack.Commands;
|
using RetainerTrack.Commands;
|
||||||
using RetainerTrack.Database;
|
using RetainerTrack.Database;
|
||||||
using RetainerTrack.Database.Compiled;
|
using RetainerTrack.Database.Compiled;
|
||||||
|
using RetainerTrack.Database.Migrations;
|
||||||
using RetainerTrack.Handlers;
|
using RetainerTrack.Handlers;
|
||||||
|
|
||||||
namespace RetainerTrack;
|
namespace RetainerTrack;
|
||||||
@ -23,7 +23,7 @@ internal sealed class RetainerTrackPlugin : IDalamudPlugin
|
|||||||
private readonly ServiceProvider? _serviceProvider;
|
private readonly ServiceProvider? _serviceProvider;
|
||||||
|
|
||||||
public RetainerTrackPlugin(
|
public RetainerTrackPlugin(
|
||||||
IDalamudPluginInterface pluginInterface,
|
DalamudPluginInterface pluginInterface,
|
||||||
IFramework framework,
|
IFramework framework,
|
||||||
IClientState clientState,
|
IClientState clientState,
|
||||||
IGameGui gameGui,
|
IGameGui gameGui,
|
||||||
@ -32,9 +32,6 @@ internal sealed class RetainerTrackPlugin : IDalamudPlugin
|
|||||||
IAddonLifecycle addonLifecycle,
|
IAddonLifecycle addonLifecycle,
|
||||||
ICommandManager commandManager,
|
ICommandManager commandManager,
|
||||||
IDataManager dataManager,
|
IDataManager dataManager,
|
||||||
ITargetManager targetManager,
|
|
||||||
IObjectTable objectTable,
|
|
||||||
IMarketBoard marketBoard,
|
|
||||||
IPluginLog pluginLog)
|
IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
ServiceCollection serviceCollection = new();
|
ServiceCollection serviceCollection = new();
|
||||||
@ -52,52 +49,41 @@ internal sealed class RetainerTrackPlugin : IDalamudPlugin
|
|||||||
serviceCollection.AddSingleton(addonLifecycle);
|
serviceCollection.AddSingleton(addonLifecycle);
|
||||||
serviceCollection.AddSingleton(commandManager);
|
serviceCollection.AddSingleton(commandManager);
|
||||||
serviceCollection.AddSingleton(dataManager);
|
serviceCollection.AddSingleton(dataManager);
|
||||||
serviceCollection.AddSingleton(targetManager);
|
|
||||||
serviceCollection.AddSingleton(objectTable);
|
|
||||||
serviceCollection.AddSingleton(marketBoard);
|
|
||||||
|
|
||||||
serviceCollection.AddSingleton<PersistenceContext>();
|
serviceCollection.AddSingleton<PersistenceContext>();
|
||||||
serviceCollection.AddSingleton<MarketBoardOfferingsHandler>();
|
serviceCollection.AddSingleton<MarketBoardOfferingsHandler>();
|
||||||
|
serviceCollection.AddSingleton<PartyHandler>();
|
||||||
serviceCollection.AddSingleton<MarketBoardUiHandler>();
|
serviceCollection.AddSingleton<MarketBoardUiHandler>();
|
||||||
serviceCollection.AddSingleton<ObjectTableHandler>();
|
|
||||||
serviceCollection.AddSingleton<GameHooks>();
|
serviceCollection.AddSingleton<GameHooks>();
|
||||||
serviceCollection.AddSingleton<AccountIdCommand>();
|
|
||||||
serviceCollection.AddSingleton<WhoCommand>();
|
serviceCollection.AddSingleton<WhoCommand>();
|
||||||
|
|
||||||
_sqliteConnectionString = PrepareSqliteDb(serviceCollection, pluginInterface.GetPluginConfigDirectory());
|
_sqliteConnectionString =
|
||||||
|
$"Data Source={Path.Join(pluginInterface.GetPluginConfigDirectory(), DatabaseFileName)}";
|
||||||
|
serviceCollection.AddDbContext<RetainerTrackContext>(o => o
|
||||||
|
.UseSqlite(_sqliteConnectionString)
|
||||||
|
.UseModel(RetainerTrackContextModel.Instance));
|
||||||
|
|
||||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
|
|
||||||
RunMigrations(_serviceProvider);
|
RunMigrations(_serviceProvider);
|
||||||
InitializeRequiredServices(_serviceProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string PrepareSqliteDb(IServiceCollection serviceCollection, string getPluginConfigDirectory)
|
_serviceProvider.GetRequiredService<PartyHandler>();
|
||||||
{
|
_serviceProvider.GetRequiredService<MarketBoardOfferingsHandler>();
|
||||||
string connectionString = $"Data Source={Path.Join(getPluginConfigDirectory, DatabaseFileName)}";
|
_serviceProvider.GetRequiredService<MarketBoardUiHandler>();
|
||||||
serviceCollection.AddDbContext<RetainerTrackContext>(o => o
|
_serviceProvider.GetRequiredService<GameHooks>();
|
||||||
.UseSqlite(connectionString)
|
_serviceProvider.GetRequiredService<WhoCommand>();
|
||||||
.UseModel(RetainerTrackContextModel.Instance));
|
|
||||||
return connectionString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunMigrations(IServiceProvider serviceProvider)
|
private static void RunMigrations(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
|
ImportLegacyData.PluginInterface = serviceProvider.GetRequiredService<DalamudPluginInterface>();
|
||||||
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
using var scope = serviceProvider.CreateScope();
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
||||||
dbContext.Database.Migrate();
|
dbContext.Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeRequiredServices(ServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
serviceProvider.GetRequiredService<MarketBoardOfferingsHandler>();
|
|
||||||
serviceProvider.GetRequiredService<MarketBoardUiHandler>();
|
|
||||||
serviceProvider.GetRequiredService<ObjectTableHandler>();
|
|
||||||
serviceProvider.GetRequiredService<GameHooks>();
|
|
||||||
serviceProvider.GetRequiredService<AccountIdCommand>();
|
|
||||||
serviceProvider.GetRequiredService<WhoCommand>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_serviceProvider?.Dispose();
|
_serviceProvider?.Dispose();
|
||||||
|
@ -13,21 +13,15 @@
|
|||||||
},
|
},
|
||||||
"DalamudPackager": {
|
"DalamudPackager": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.1.13, )",
|
"requested": "[2.1.12, )",
|
||||||
"resolved": "2.1.13",
|
"resolved": "2.1.12",
|
||||||
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
|
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
|
||||||
},
|
},
|
||||||
"DotNet.ReproducibleBuilds": {
|
"LiteDB": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.1.1, )",
|
"requested": "[5.0.17, )",
|
||||||
"resolved": "1.1.1",
|
"resolved": "5.0.17",
|
||||||
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
|
"contentHash": "cKPvkdlzIts3ZKu/BzoIc/Y71e4VFKlij4LyioPFATZMot+wB7EAm1FFbZSJez6coJmQUoIg/3yHE1MMU+zOdg=="
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.SourceLink.AzureRepos.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Bitbucket.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.GitHub": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.GitLab": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite": {
|
"Microsoft.EntityFrameworkCore.Sqlite": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
@ -39,20 +33,82 @@
|
|||||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
|
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.SourceLink.Gitea": {
|
"Microsoft.EntityFrameworkCore.Tools": {
|
||||||
|
"type": "Direct",
|
||||||
|
"requested": "[8.0.5, )",
|
||||||
|
"resolved": "8.0.5",
|
||||||
|
"contentHash": "ZG5X2uznVmw+Mk0HVv3YHiTaGcCANDmZg81/9GLvE5zU4B11oxuM1+tndkYCFoM9CSN0/+XfB89TVYViKXYiRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.EntityFrameworkCore.Design": "8.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.Extensions.DependencyInjection": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[8.0.0, )",
|
"requested": "[8.0.0, )",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
|
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
||||||
"Microsoft.SourceLink.Common": "8.0.0"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Build.Tasks.Git": {
|
"Humanizer.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "2.14.1",
|
||||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
"contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
|
||||||
|
},
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
|
||||||
|
},
|
||||||
|
"Microsoft.CodeAnalysis.Analyzers": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "3.3.3",
|
||||||
|
"contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
|
||||||
|
},
|
||||||
|
"Microsoft.CodeAnalysis.Common": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.5.0",
|
||||||
|
"contentHash": "lwAbIZNdnY0SUNoDmZHkVUwLO8UyNnyyh1t/4XsbFxi4Ounb3xszIYZaWhyj5ZjyfcwqwmtMbE7fUTVCqQEIdQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.CodeAnalysis.Analyzers": "3.3.3",
|
||||||
|
"System.Collections.Immutable": "6.0.0",
|
||||||
|
"System.Reflection.Metadata": "6.0.1",
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
|
||||||
|
"System.Text.Encoding.CodePages": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.CodeAnalysis.CSharp": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.5.0",
|
||||||
|
"contentHash": "cM59oMKAOxvdv76bdmaKPy5hfj+oR+zxikWoueEB7CwTko7mt9sVKZI8Qxlov0C/LuKEG+WQwifepqL3vuTiBQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Microsoft.CodeAnalysis.Common": "[4.5.0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.CodeAnalysis.CSharp.Workspaces": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.5.0",
|
||||||
|
"contentHash": "h74wTpmGOp4yS4hj+EvNzEiPgg/KVs2wmSfTZ81upJZOtPkJsVkgfsgtxxqmAeapjT/vLKfmYV0bS8n5MNVP+g==",
|
||||||
|
"dependencies": {
|
||||||
|
"Humanizer.Core": "2.14.1",
|
||||||
|
"Microsoft.CodeAnalysis.CSharp": "[4.5.0]",
|
||||||
|
"Microsoft.CodeAnalysis.Common": "[4.5.0]",
|
||||||
|
"Microsoft.CodeAnalysis.Workspaces.Common": "[4.5.0]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Microsoft.CodeAnalysis.Workspaces.Common": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.5.0",
|
||||||
|
"contentHash": "l4dDRmGELXG72XZaonnOeORyD/T5RpEu5LGHOUIhnv+MmUWDY/m1kWXGwtcgQ5CJ5ynkFiRnIYzTKXYjUs7rbw==",
|
||||||
|
"dependencies": {
|
||||||
|
"Humanizer.Core": "2.14.1",
|
||||||
|
"Microsoft.Bcl.AsyncInterfaces": "6.0.0",
|
||||||
|
"Microsoft.CodeAnalysis.Common": "[4.5.0]",
|
||||||
|
"System.Composition": "6.0.0",
|
||||||
|
"System.IO.Pipelines": "6.0.3",
|
||||||
|
"System.Threading.Channels": "6.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Data.Sqlite.Core": {
|
"Microsoft.Data.Sqlite.Core": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
@ -83,6 +139,18 @@
|
|||||||
"resolved": "8.0.5",
|
"resolved": "8.0.5",
|
||||||
"contentHash": "LzoKedC+9A8inF5d3iIzgyv/JDXgKrtpYoGIC3EqGWuHVDm9s/IHHApeTOTbzvnr7yBVV+nmYfyT1nwtzRDp0Q=="
|
"contentHash": "LzoKedC+9A8inF5d3iIzgyv/JDXgKrtpYoGIC3EqGWuHVDm9s/IHHApeTOTbzvnr7yBVV+nmYfyT1nwtzRDp0Q=="
|
||||||
},
|
},
|
||||||
|
"Microsoft.EntityFrameworkCore.Design": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "8.0.5",
|
||||||
|
"contentHash": "HWYnbuMwllSCsZjfKj3Vz+HDGOCyGlTMYjI7tZH5pK7AuiGNHOdshCnWlEFEuDV6oAadWfXGTDmkmV53gwTqSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"Humanizer.Core": "2.14.1",
|
||||||
|
"Microsoft.CodeAnalysis.CSharp.Workspaces": "4.5.0",
|
||||||
|
"Microsoft.EntityFrameworkCore.Relational": "8.0.5",
|
||||||
|
"Microsoft.Extensions.DependencyModel": "8.0.0",
|
||||||
|
"Mono.TextTemplating": "2.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Microsoft.EntityFrameworkCore.Relational": {
|
"Microsoft.EntityFrameworkCore.Relational": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.5",
|
"resolved": "8.0.5",
|
||||||
@ -130,14 +198,6 @@
|
|||||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
"Microsoft.Extensions.Primitives": "8.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.DependencyInjection": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
@ -184,45 +244,12 @@
|
|||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
|
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
|
||||||
},
|
},
|
||||||
"Microsoft.SourceLink.AzureRepos.Git": {
|
"Mono.TextTemplating": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.1.1",
|
"resolved": "2.2.1",
|
||||||
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
|
"contentHash": "KZYeKBET/2Z0gY1WlTAK7+RHTl7GSbtvTLDXEZZojUdAPqpQNDL6tHv7VUpqfX5VEOh+uRGKaZXkuD253nEOBQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
"System.CodeDom": "4.4.0"
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Bitbucket.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "cDzxXwlyWpLWaH0em4Idj0H3AmVo3L/6xRXKssYemx+7W52iNskj/SQ4FOmfCb8YQt39otTDNMveCZzYtMoucQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Common": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitHub": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "IaJGnOv/M7UQjRJks7B6p7pbPnOwisYGOIzqCz5ilGFTApZ3ktOR+6zJ12ZRPInulBmdAf1SrGdDG2MU8g6XTw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.GitLab": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "tvsg47DDLqqedlPeYVE2lmiTpND8F0hkrealQ5hYltSmvruy/Gr5nHAKSsjyw5L3NeM/HLMI5ORv7on/M4qyZw==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SQLitePCLRaw.bundle_e_sqlite3": {
|
"SQLitePCLRaw.bundle_e_sqlite3": {
|
||||||
@ -255,11 +282,98 @@
|
|||||||
"SQLitePCLRaw.core": "2.1.6"
|
"SQLitePCLRaw.core": "2.1.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"System.CodeDom": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.4.0",
|
||||||
|
"contentHash": "2sCCb7doXEwtYAbqzbF/8UAeDRMNmPaQbU2q50Psg1J9KzumyVVCgKQY8s53WIPTufNT0DpSe9QRvVjOzfDWBA=="
|
||||||
|
},
|
||||||
|
"System.Collections.Immutable": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Composition": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "d7wMuKQtfsxUa7S13tITC8n1cQzewuhD5iDjZtK2prwFfKVzdYtgrTHgjaV03Zq7feGQ5gkP85tJJntXwInsJA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Composition.AttributedModel": "6.0.0",
|
||||||
|
"System.Composition.Convention": "6.0.0",
|
||||||
|
"System.Composition.Hosting": "6.0.0",
|
||||||
|
"System.Composition.Runtime": "6.0.0",
|
||||||
|
"System.Composition.TypedParts": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Composition.AttributedModel": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "WK1nSDLByK/4VoC7fkNiFuTVEiperuCN/Hyn+VN30R+W2ijO1d0Z2Qm0ScEl9xkSn1G2MyapJi8xpf4R8WRa/w=="
|
||||||
|
},
|
||||||
|
"System.Composition.Convention": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "XYi4lPRdu5bM4JVJ3/UIHAiG6V6lWWUlkhB9ab4IOq0FrRsp0F4wTyV4Dj+Ds+efoXJ3qbLqlvaUozDO7OLeXA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Composition.AttributedModel": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Composition.Hosting": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "w/wXjj7kvxuHPLdzZ0PAUt++qJl03t7lENmb2Oev0n3zbxyNULbWBlnd5J5WUMMv15kg5o+/TCZFb6lSwfaUUQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Composition.Runtime": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Composition.Runtime": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "qkRH/YBaMPTnzxrS5RDk1juvqed4A6HOD/CwRcDGyPpYps1J27waBddiiq1y93jk2ZZ9wuA/kynM+NO0kb3PKg=="
|
||||||
|
},
|
||||||
|
"System.Composition.TypedParts": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "iUR1eHrL8Cwd82neQCJ00MpwNIBs4NZgXzrPqx8NJf/k4+mwBO0XCRmHYJT4OLSwDDqh5nBLJWkz5cROnrGhRA==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Composition.AttributedModel": "6.0.0",
|
||||||
|
"System.Composition.Hosting": "6.0.0",
|
||||||
|
"System.Composition.Runtime": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.IO.Pipelines": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.3",
|
||||||
|
"contentHash": "ryTgF+iFkpGZY1vRQhfCzX0xTdlV3pyaTTqRu2ETbEv+HlV7O6y7hyQURnghNIXvctl5DuZ//Dpks6HdL/Txgw=="
|
||||||
|
},
|
||||||
"System.Memory": {
|
"System.Memory": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.5.3",
|
"resolved": "4.5.3",
|
||||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
||||||
},
|
},
|
||||||
|
"System.Reflection.Metadata": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.1",
|
||||||
|
"contentHash": "III/lNMSn0ZRBuM9m5Cgbiho5j81u0FAEagFX5ta2DKbljZ3T0IpD8j+BIiHQPeKqJppWS9bGEp6JnKnWKze0g==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Collections.Immutable": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
|
||||||
|
},
|
||||||
|
"System.Text.Encoding.CodePages": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Text.Encodings.Web": {
|
"System.Text.Encodings.Web": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
@ -272,6 +386,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"System.Text.Encodings.Web": "8.0.0"
|
"System.Text.Encodings.Web": "8.0.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"System.Threading.Channels": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "TY8/9+tI0mNaUMgntOxxaq2ndTkdXqLSxvPmas7XEqOlv9lQtB7wLjYGd756lOaO7Dvb5r/WXhluM+0Xe87v5Q=="
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"net8.0-windows7.0/win-x64": {
|
"net8.0-windows7.0/win-x64": {
|
||||||
@ -280,6 +399,14 @@
|
|||||||
"resolved": "2.1.6",
|
"resolved": "2.1.6",
|
||||||
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
|
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
|
||||||
},
|
},
|
||||||
|
"System.Text.Encoding.CodePages": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "6.0.0",
|
||||||
|
"contentHash": "ZFCILZuOvtKPauZ/j/swhvw68ZRi9ATCfvGbk1QfydmcXBkIWecWKn/250UH7rahZ5OoDBaiAudJtPvLwzw85A==",
|
||||||
|
"dependencies": {
|
||||||
|
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.Text.Encodings.Web": {
|
"System.Text.Encodings.Web": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "8.0.0",
|
||||||
|
Loading…
Reference in New Issue
Block a user