Compare commits
No commits in common. "master" and "v1.0" 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
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using RetainerTrack.Handlers;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Commands;
|
|
||||||
|
|
||||||
internal sealed class WhoCommand : IDisposable
|
|
||||||
{
|
|
||||||
private readonly PersistenceContext _persistenceContext;
|
|
||||||
private readonly ICommandManager _commandManager;
|
|
||||||
private readonly IChatGui _chatGui;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly Dictionary<string, uint> _worlds;
|
|
||||||
|
|
||||||
public WhoCommand(PersistenceContext persistenceContext, ICommandManager commandManager, IChatGui chatGui,
|
|
||||||
IClientState clientState, IDataManager dataManager)
|
|
||||||
{
|
|
||||||
_persistenceContext = persistenceContext;
|
|
||||||
_commandManager = commandManager;
|
|
||||||
_chatGui = chatGui;
|
|
||||||
_clientState = clientState;
|
|
||||||
_worlds = dataManager.GetExcelSheet<World>()!.Where(x => x.IsPublic)
|
|
||||||
.ToDictionary(x => x.Name.ToString().ToUpperInvariant(), x => x.RowId);
|
|
||||||
_commandManager.AddHandler("/rwho", new CommandInfo(ProcessCommand)
|
|
||||||
{
|
|
||||||
HelpMessage =
|
|
||||||
"/rwho Character Name@World → Shows all retainers for the character (will use your current world if no world is specified)"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessCommand(string command, string arguments)
|
|
||||||
{
|
|
||||||
string[] nameParts = arguments.Split(' ');
|
|
||||||
if (nameParts.Length != 2)
|
|
||||||
{
|
|
||||||
_chatGui.Print($"USAGE: /{command} Character Name@World");
|
|
||||||
}
|
|
||||||
else if (nameParts[1].Contains('@', StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
string[] lastNameParts = nameParts[1].Split('@');
|
|
||||||
if (_worlds.TryGetValue(lastNameParts[1].ToUpperInvariant(), out uint worldId))
|
|
||||||
ProcessLookup($"{nameParts[0]} {lastNameParts[0]}", worldId);
|
|
||||||
else
|
|
||||||
_chatGui.PrintError($"Unknown world: {lastNameParts[1]}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ProcessLookup(arguments, _clientState?.LocalPlayer?.CurrentWorld?.Id ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessLookup(string name, uint world)
|
|
||||||
{
|
|
||||||
if (world == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_chatGui.Print($"Retainer names for {name}: ");
|
|
||||||
var retainers = _persistenceContext.GetRetainerNamesForCharacter(name, world);
|
|
||||||
foreach (var retainerName in retainers)
|
|
||||||
_chatGui.Print($" - {retainerName}");
|
|
||||||
if (retainers.Count == 0)
|
|
||||||
_chatGui.Print(" (No retainers found)");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_commandManager.RemoveHandler("/rwho");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
|
|
||||||
|
|
||||||
#pragma warning disable 219, 612, 618
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Compiled
|
|
||||||
{
|
|
||||||
internal partial class PlayerEntityType
|
|
||||||
{
|
|
||||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
|
||||||
{
|
|
||||||
var runtimeEntityType = model.AddEntityType(
|
|
||||||
"RetainerTrack.Database.Player",
|
|
||||||
typeof(Player),
|
|
||||||
baseEntityType);
|
|
||||||
|
|
||||||
var localContentId = runtimeEntityType.AddProperty(
|
|
||||||
"LocalContentId",
|
|
||||||
typeof(ulong),
|
|
||||||
propertyInfo: typeof(Player).GetProperty("LocalContentId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Player).GetField("<LocalContentId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
valueGenerated: ValueGenerated.OnAdd,
|
|
||||||
afterSaveBehavior: PropertySaveBehavior.Throw,
|
|
||||||
sentinel: 0ul);
|
|
||||||
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(
|
|
||||||
"Name",
|
|
||||||
typeof(string),
|
|
||||||
propertyInfo: typeof(Player).GetProperty("Name", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Player).GetField("<Name>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
maxLength: 20);
|
|
||||||
name.TypeMapping = SqliteStringTypeMapping.Default;
|
|
||||||
|
|
||||||
var key = runtimeEntityType.AddKey(
|
|
||||||
new[] { localContentId });
|
|
||||||
runtimeEntityType.SetPrimaryKey(key);
|
|
||||||
|
|
||||||
return runtimeEntityType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
|
||||||
{
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:Schema", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:TableName", "Players");
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
|
|
||||||
|
|
||||||
Customize(runtimeEntityType);
|
|
||||||
}
|
|
||||||
|
|
||||||
static partial void Customize(RuntimeEntityType runtimeEntityType);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
|
||||||
|
|
||||||
#pragma warning disable 219, 612, 618
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Compiled
|
|
||||||
{
|
|
||||||
internal partial class RetainerEntityType
|
|
||||||
{
|
|
||||||
public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null)
|
|
||||||
{
|
|
||||||
var runtimeEntityType = model.AddEntityType(
|
|
||||||
"RetainerTrack.Database.Retainer",
|
|
||||||
typeof(Retainer),
|
|
||||||
baseEntityType);
|
|
||||||
|
|
||||||
var localContentId = runtimeEntityType.AddProperty(
|
|
||||||
"LocalContentId",
|
|
||||||
typeof(ulong),
|
|
||||||
propertyInfo: typeof(Retainer).GetProperty("LocalContentId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Retainer).GetField("<LocalContentId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
valueGenerated: ValueGenerated.OnAdd,
|
|
||||||
afterSaveBehavior: PropertySaveBehavior.Throw,
|
|
||||||
sentinel: 0ul);
|
|
||||||
localContentId.TypeMapping = SqliteULongTypeMapping.Default;
|
|
||||||
|
|
||||||
var name = runtimeEntityType.AddProperty(
|
|
||||||
"Name",
|
|
||||||
typeof(string),
|
|
||||||
propertyInfo: typeof(Retainer).GetProperty("Name", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Retainer).GetField("<Name>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
maxLength: 24);
|
|
||||||
name.TypeMapping = SqliteStringTypeMapping.Default;
|
|
||||||
|
|
||||||
var ownerLocalContentId = runtimeEntityType.AddProperty(
|
|
||||||
"OwnerLocalContentId",
|
|
||||||
typeof(ulong),
|
|
||||||
propertyInfo: typeof(Retainer).GetProperty("OwnerLocalContentId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Retainer).GetField("<OwnerLocalContentId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
sentinel: 0ul);
|
|
||||||
ownerLocalContentId.TypeMapping = SqliteULongTypeMapping.Default;
|
|
||||||
|
|
||||||
var worldId = runtimeEntityType.AddProperty(
|
|
||||||
"WorldId",
|
|
||||||
typeof(ushort),
|
|
||||||
propertyInfo: typeof(Retainer).GetProperty("WorldId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
fieldInfo: typeof(Retainer).GetField("<WorldId>k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly),
|
|
||||||
sentinel: (ushort)0);
|
|
||||||
worldId.TypeMapping = UShortTypeMapping.Default.Clone(
|
|
||||||
comparer: new ValueComparer<ushort>(
|
|
||||||
(ushort v1, ushort v2) => v1 == v2,
|
|
||||||
(ushort v) => (int)v,
|
|
||||||
(ushort v) => v),
|
|
||||||
keyComparer: new ValueComparer<ushort>(
|
|
||||||
(ushort v1, ushort v2) => v1 == v2,
|
|
||||||
(ushort v) => (int)v,
|
|
||||||
(ushort v) => v),
|
|
||||||
providerValueComparer: new ValueComparer<ushort>(
|
|
||||||
(ushort v1, ushort v2) => v1 == v2,
|
|
||||||
(ushort v) => (int)v,
|
|
||||||
(ushort v) => v),
|
|
||||||
mappingInfo: new RelationalTypeMappingInfo(
|
|
||||||
storeTypeName: "INTEGER"));
|
|
||||||
|
|
||||||
var key = runtimeEntityType.AddKey(
|
|
||||||
new[] { localContentId });
|
|
||||||
runtimeEntityType.SetPrimaryKey(key);
|
|
||||||
|
|
||||||
return runtimeEntityType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CreateAnnotations(RuntimeEntityType runtimeEntityType)
|
|
||||||
{
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:FunctionName", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:Schema", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:SqlQuery", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:TableName", "Retainers");
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:ViewName", null);
|
|
||||||
runtimeEntityType.AddAnnotation("Relational:ViewSchema", null);
|
|
||||||
|
|
||||||
Customize(runtimeEntityType);
|
|
||||||
}
|
|
||||||
|
|
||||||
static partial void Customize(RuntimeEntityType runtimeEntityType);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
|
|
||||||
#pragma warning disable 219, 612, 618
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Compiled
|
|
||||||
{
|
|
||||||
[DbContext(typeof(RetainerTrackContext))]
|
|
||||||
public partial class RetainerTrackContextModel : RuntimeModel
|
|
||||||
{
|
|
||||||
private static readonly bool _useOldBehavior31751 =
|
|
||||||
System.AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31751", out var enabled31751) && enabled31751;
|
|
||||||
|
|
||||||
static RetainerTrackContextModel()
|
|
||||||
{
|
|
||||||
var model = new RetainerTrackContextModel();
|
|
||||||
|
|
||||||
if (_useOldBehavior31751)
|
|
||||||
{
|
|
||||||
model.Initialize();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var thread = new System.Threading.Thread(RunInitialization, 10 * 1024 * 1024);
|
|
||||||
thread.Start();
|
|
||||||
thread.Join();
|
|
||||||
|
|
||||||
void RunInitialization()
|
|
||||||
{
|
|
||||||
model.Initialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model.Customize();
|
|
||||||
_instance = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RetainerTrackContextModel _instance;
|
|
||||||
public static IModel Instance => _instance;
|
|
||||||
|
|
||||||
partial void Initialize();
|
|
||||||
|
|
||||||
partial void Customize();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
|
||||||
|
|
||||||
#pragma warning disable 219, 612, 618
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Compiled
|
|
||||||
{
|
|
||||||
public partial class RetainerTrackContextModel
|
|
||||||
{
|
|
||||||
partial void Initialize()
|
|
||||||
{
|
|
||||||
var player = PlayerEntityType.Create(this);
|
|
||||||
var retainer = RetainerEntityType.Create(this);
|
|
||||||
|
|
||||||
PlayerEntityType.CreateAnnotations(player);
|
|
||||||
RetainerEntityType.CreateAnnotations(retainer);
|
|
||||||
|
|
||||||
AddAnnotation("ProductVersion", "8.0.5");
|
|
||||||
AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IRelationalModel CreateRelationalModel()
|
|
||||||
{
|
|
||||||
var relationalModel = new RelationalModel(this);
|
|
||||||
|
|
||||||
var player = FindEntityType("RetainerTrack.Database.Player")!;
|
|
||||||
|
|
||||||
var defaultTableMappings = new List<TableMappingBase<ColumnMappingBase>>();
|
|
||||||
player.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings);
|
|
||||||
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);
|
|
||||||
retainerTrackDatabasePlayerTableBase.Columns.Add("LocalContentId", localContentIdColumnBase);
|
|
||||||
var nameColumnBase = new ColumnBase<ColumnMappingBase>("Name", "TEXT", retainerTrackDatabasePlayerTableBase);
|
|
||||||
retainerTrackDatabasePlayerTableBase.Columns.Add("Name", nameColumnBase);
|
|
||||||
relationalModel.DefaultTables.Add("RetainerTrack.Database.Player", retainerTrackDatabasePlayerTableBase);
|
|
||||||
var retainerTrackDatabasePlayerMappingBase = new TableMappingBase<ColumnMappingBase>(player, retainerTrackDatabasePlayerTableBase, true);
|
|
||||||
retainerTrackDatabasePlayerTableBase.AddTypeMapping(retainerTrackDatabasePlayerMappingBase, false);
|
|
||||||
defaultTableMappings.Add(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);
|
|
||||||
|
|
||||||
var tableMappings = new List<TableMapping>();
|
|
||||||
player.SetRuntimeAnnotation("Relational:TableMappings", tableMappings);
|
|
||||||
var playersTable = new Table("Players", null, relationalModel);
|
|
||||||
var localContentIdColumn = new Column("LocalContentId", "INTEGER", playersTable);
|
|
||||||
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);
|
|
||||||
playersTable.Columns.Add("Name", nameColumn);
|
|
||||||
var pK_Players = new UniqueConstraint("PK_Players", playersTable, new[] { localContentIdColumn });
|
|
||||||
playersTable.PrimaryKey = pK_Players;
|
|
||||||
var pK_PlayersUc = RelationalModel.GetKey(this,
|
|
||||||
"RetainerTrack.Database.Player",
|
|
||||||
new[] { "LocalContentId" });
|
|
||||||
pK_Players.MappedKeys.Add(pK_PlayersUc);
|
|
||||||
RelationalModel.GetOrCreateUniqueConstraints(pK_PlayersUc).Add(pK_Players);
|
|
||||||
playersTable.UniqueConstraints.Add("PK_Players", pK_Players);
|
|
||||||
relationalModel.Tables.Add(("Players", null), playersTable);
|
|
||||||
var playersTableMapping = new TableMapping(player, playersTable, true);
|
|
||||||
playersTable.AddTypeMapping(playersTableMapping, false);
|
|
||||||
tableMappings.Add(playersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(localContentIdColumn, player.FindProperty("LocalContentId")!, playersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(accountIdColumn, player.FindProperty("AccountId")!, playersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(nameColumn, player.FindProperty("Name")!, playersTableMapping);
|
|
||||||
|
|
||||||
var retainer = FindEntityType("RetainerTrack.Database.Retainer")!;
|
|
||||||
|
|
||||||
var defaultTableMappings0 = new List<TableMappingBase<ColumnMappingBase>>();
|
|
||||||
retainer.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings0);
|
|
||||||
var retainerTrackDatabaseRetainerTableBase = new TableBase("RetainerTrack.Database.Retainer", null, relationalModel);
|
|
||||||
var localContentIdColumnBase0 = new ColumnBase<ColumnMappingBase>("LocalContentId", "INTEGER", retainerTrackDatabaseRetainerTableBase);
|
|
||||||
retainerTrackDatabaseRetainerTableBase.Columns.Add("LocalContentId", localContentIdColumnBase0);
|
|
||||||
var nameColumnBase0 = new ColumnBase<ColumnMappingBase>("Name", "TEXT", retainerTrackDatabaseRetainerTableBase);
|
|
||||||
retainerTrackDatabaseRetainerTableBase.Columns.Add("Name", nameColumnBase0);
|
|
||||||
var ownerLocalContentIdColumnBase = new ColumnBase<ColumnMappingBase>("OwnerLocalContentId", "INTEGER", retainerTrackDatabaseRetainerTableBase);
|
|
||||||
retainerTrackDatabaseRetainerTableBase.Columns.Add("OwnerLocalContentId", ownerLocalContentIdColumnBase);
|
|
||||||
var worldIdColumnBase = new ColumnBase<ColumnMappingBase>("WorldId", "INTEGER", retainerTrackDatabaseRetainerTableBase);
|
|
||||||
retainerTrackDatabaseRetainerTableBase.Columns.Add("WorldId", worldIdColumnBase);
|
|
||||||
relationalModel.DefaultTables.Add("RetainerTrack.Database.Retainer", retainerTrackDatabaseRetainerTableBase);
|
|
||||||
var retainerTrackDatabaseRetainerMappingBase = new TableMappingBase<ColumnMappingBase>(retainer, retainerTrackDatabaseRetainerTableBase, true);
|
|
||||||
retainerTrackDatabaseRetainerTableBase.AddTypeMapping(retainerTrackDatabaseRetainerMappingBase, false);
|
|
||||||
defaultTableMappings0.Add(retainerTrackDatabaseRetainerMappingBase);
|
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)localContentIdColumnBase0, retainer.FindProperty("LocalContentId")!, retainerTrackDatabaseRetainerMappingBase);
|
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)nameColumnBase0, retainer.FindProperty("Name")!, retainerTrackDatabaseRetainerMappingBase);
|
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)ownerLocalContentIdColumnBase, retainer.FindProperty("OwnerLocalContentId")!, retainerTrackDatabaseRetainerMappingBase);
|
|
||||||
RelationalModel.CreateColumnMapping((ColumnBase<ColumnMappingBase>)worldIdColumnBase, retainer.FindProperty("WorldId")!, retainerTrackDatabaseRetainerMappingBase);
|
|
||||||
|
|
||||||
var tableMappings0 = new List<TableMapping>();
|
|
||||||
retainer.SetRuntimeAnnotation("Relational:TableMappings", tableMappings0);
|
|
||||||
var retainersTable = new Table("Retainers", null, relationalModel);
|
|
||||||
var localContentIdColumn0 = new Column("LocalContentId", "INTEGER", retainersTable);
|
|
||||||
retainersTable.Columns.Add("LocalContentId", localContentIdColumn0);
|
|
||||||
var nameColumn0 = new Column("Name", "TEXT", retainersTable);
|
|
||||||
retainersTable.Columns.Add("Name", nameColumn0);
|
|
||||||
var ownerLocalContentIdColumn = new Column("OwnerLocalContentId", "INTEGER", retainersTable);
|
|
||||||
retainersTable.Columns.Add("OwnerLocalContentId", ownerLocalContentIdColumn);
|
|
||||||
var worldIdColumn = new Column("WorldId", "INTEGER", retainersTable);
|
|
||||||
retainersTable.Columns.Add("WorldId", worldIdColumn);
|
|
||||||
var pK_Retainers = new UniqueConstraint("PK_Retainers", retainersTable, new[] { localContentIdColumn0 });
|
|
||||||
retainersTable.PrimaryKey = pK_Retainers;
|
|
||||||
var pK_RetainersUc = RelationalModel.GetKey(this,
|
|
||||||
"RetainerTrack.Database.Retainer",
|
|
||||||
new[] { "LocalContentId" });
|
|
||||||
pK_Retainers.MappedKeys.Add(pK_RetainersUc);
|
|
||||||
RelationalModel.GetOrCreateUniqueConstraints(pK_RetainersUc).Add(pK_Retainers);
|
|
||||||
retainersTable.UniqueConstraints.Add("PK_Retainers", pK_Retainers);
|
|
||||||
relationalModel.Tables.Add(("Retainers", null), retainersTable);
|
|
||||||
var retainersTableMapping = new TableMapping(retainer, retainersTable, true);
|
|
||||||
retainersTable.AddTypeMapping(retainersTableMapping, false);
|
|
||||||
tableMappings0.Add(retainersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(localContentIdColumn0, retainer.FindProperty("LocalContentId")!, retainersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(nameColumn0, retainer.FindProperty("Name")!, retainersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(ownerLocalContentIdColumn, retainer.FindProperty("OwnerLocalContentId")!, retainersTableMapping);
|
|
||||||
RelationalModel.CreateColumnMapping(worldIdColumn, retainer.FindProperty("WorldId")!, retainersTableMapping);
|
|
||||||
return relationalModel.MakeReadOnly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
[*.cs]
|
|
||||||
# CA1062: Validate arguments of public methods
|
|
||||||
dotnet_diagnostic.CA1062.severity = none
|
|
@ -1,62 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20240524200204_InitialCreate")]
|
|
||||||
partial class InitialCreate
|
|
||||||
{
|
|
||||||
/// <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<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,52 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class InitialCreate : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Players",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
LocalContentId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
|
||||||
Name = table.Column<string>(type: "TEXT", maxLength: 20, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Players", x => x.LocalContentId);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Retainers",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
LocalContentId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
|
||||||
Name = table.Column<string>(type: "TEXT", maxLength: 24, nullable: false),
|
|
||||||
WorldId = table.Column<ushort>(type: "INTEGER", nullable: false),
|
|
||||||
OwnerLocalContentId = table.Column<ulong>(type: "INTEGER", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Retainers", x => x.LocalContentId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Players");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Retainers");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20240524201345_ImportLegacyData")]
|
|
||||||
partial class ImportLegacyData
|
|
||||||
{
|
|
||||||
/// <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<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,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using Dalamud.Plugin;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class ImportLegacyData : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
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("20240524214606_CleanupBrokenPlayerIds")]
|
|
||||||
partial class CleanupBrokenPlayerIds
|
|
||||||
{
|
|
||||||
/// <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<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,22 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class CleanupBrokenPlayerIds : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.Sql("DELETE FROM Players WHERE LocalContentId < 18014398509481984");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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,63 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using RetainerTrack.Database;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(RetainerTrackContext))]
|
|
||||||
partial class RetainerTrackContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(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,14 +1,8 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
namespace RetainerTrack.Database
|
||||||
|
|
||||||
namespace RetainerTrack.Database;
|
|
||||||
|
|
||||||
public class Player
|
|
||||||
{
|
{
|
||||||
[Key, Required]
|
internal sealed class Player
|
||||||
public ulong LocalContentId { get; set; }
|
{
|
||||||
|
public ulong Id { get; set; }
|
||||||
[MaxLength(20), Required]
|
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
}
|
||||||
public ulong? AccountId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
namespace RetainerTrack.Database
|
||||||
|
|
||||||
namespace RetainerTrack.Database;
|
|
||||||
|
|
||||||
public class Retainer
|
|
||||||
{
|
{
|
||||||
[Key, Required]
|
internal sealed class Retainer
|
||||||
public ulong LocalContentId { get; set; }
|
{
|
||||||
|
public ulong Id { get; set; }
|
||||||
[MaxLength(24), Required]
|
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
|
|
||||||
[Required]
|
|
||||||
public ushort WorldId { get; set; }
|
public ushort WorldId { get; set; }
|
||||||
|
public ulong OwnerContentId { get; set; }
|
||||||
[Required]
|
}
|
||||||
public ulong OwnerLocalContentId { get; set; }
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database;
|
|
||||||
|
|
||||||
internal sealed class RetainerTrackContext : DbContext
|
|
||||||
{
|
|
||||||
public DbSet<Retainer> Retainers { get; set; }
|
|
||||||
public DbSet<Player> Players { get; set; }
|
|
||||||
|
|
||||||
public RetainerTrackContext(DbContextOptions<RetainerTrackContext> options)
|
|
||||||
: base(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#if EF
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Database;
|
|
||||||
|
|
||||||
internal sealed class PalClientContextFactory : IDesignTimeDbContextFactory<RetainerTrackContext>
|
|
||||||
{
|
|
||||||
public RetainerTrackContext CreateDbContext(string[] args)
|
|
||||||
{
|
|
||||||
var optionsBuilder =
|
|
||||||
new DbContextOptionsBuilder<RetainerTrackContext>().UseSqlite(
|
|
||||||
$"Data Source={Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "XIVLauncher", "pluginConfigs", "RetainerTrack", RetainerTrackPlugin.DatabaseFileName)}");
|
|
||||||
return new RetainerTrackContext(optionsBuilder.Options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
8
RetainerTrack/Handlers/ContentIdToName.cs
Normal file
8
RetainerTrack/Handlers/ContentIdToName.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
|
internal sealed class ContentIdToName
|
||||||
|
{
|
||||||
|
public ulong ContentId { get; init; }
|
||||||
|
public string PlayerName { get; init; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
@ -6,13 +6,11 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
using Dalamud.Utility.Signatures;
|
using Dalamud.Utility.Signatures;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
internal sealed unsafe class GameHooks : IDisposable
|
internal sealed unsafe class GameHooks : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ILogger<GameHooks> _logger;
|
private readonly ILogger<GameHooks> _logger;
|
||||||
@ -37,14 +35,13 @@ 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)
|
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_persistenceContext = persistenceContext;
|
_persistenceContext = persistenceContext;
|
||||||
|
|
||||||
_logger.LogDebug("Initializing game hooks");
|
_logger.LogDebug("Initializing game hooks");
|
||||||
gameInteropProvider.InitializeFromAttributes(this);
|
SignatureHelper.Initialise(this);
|
||||||
CharacterNameResultHook.Enable();
|
CharacterNameResultHook.Enable();
|
||||||
SocialListResultHook.Enable();
|
SocialListResultHook.Enable();
|
||||||
|
|
||||||
@ -55,10 +52,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 +62,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 +83,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 +135,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 +152,10 @@ 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,46 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Dalamud.Game.Network.Structures;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
|
||||||
|
|
||||||
internal sealed class MarketBoardOfferingsHandler : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IMarketBoard _marketBoard;
|
|
||||||
private readonly ILogger<MarketBoardOfferingsHandler> _logger;
|
|
||||||
private readonly IClientState _clientState;
|
|
||||||
private readonly PersistenceContext _persistenceContext;
|
|
||||||
|
|
||||||
public MarketBoardOfferingsHandler(
|
|
||||||
IMarketBoard marketBoard,
|
|
||||||
ILogger<MarketBoardOfferingsHandler> logger,
|
|
||||||
IClientState clientState,
|
|
||||||
PersistenceContext persistenceContext)
|
|
||||||
{
|
|
||||||
_marketBoard = marketBoard;
|
|
||||||
_logger = logger;
|
|
||||||
_clientState = clientState;
|
|
||||||
_persistenceContext = persistenceContext;
|
|
||||||
|
|
||||||
_marketBoard.OfferingsReceived += HandleOfferings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_marketBoard.OfferingsReceived += HandleOfferings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleOfferings(IMarketBoardCurrentOfferings currentOfferings)
|
|
||||||
{
|
|
||||||
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
|
|
||||||
if (worldId == 0)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Skipping market board handler, current world unknown");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(() => _persistenceContext.HandleMarketBoardPage(currentOfferings, worldId));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +1,64 @@
|
|||||||
using System;
|
using System;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Hooking;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
internal sealed unsafe class MarketBoardUiHandler : IDisposable
|
internal sealed unsafe class MarketBoardUiHandler : IDisposable
|
||||||
{
|
{
|
||||||
private const string AddonName = "ItemSearchResult";
|
|
||||||
|
|
||||||
private readonly ILogger<MarketBoardUiHandler> _logger;
|
private readonly ILogger<MarketBoardUiHandler> _logger;
|
||||||
|
private readonly Framework _framework;
|
||||||
|
private readonly GameGui _gameGui;
|
||||||
private readonly PersistenceContext _persistenceContext;
|
private readonly PersistenceContext _persistenceContext;
|
||||||
private readonly IAddonLifecycle _addonLifecycle;
|
|
||||||
|
private Hook<Draw>? _drawHook;
|
||||||
|
|
||||||
|
private delegate void Draw(AtkUnitBase* addon);
|
||||||
|
|
||||||
public MarketBoardUiHandler(
|
public MarketBoardUiHandler(
|
||||||
ILogger<MarketBoardUiHandler> logger,
|
ILogger<MarketBoardUiHandler> logger,
|
||||||
PersistenceContext persistenceContext,
|
Framework framework,
|
||||||
IAddonLifecycle addonLifecycle)
|
GameGui gameGui,
|
||||||
|
PersistenceContext persistenceContext)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_framework = framework;
|
||||||
|
_gameGui = gameGui;
|
||||||
_persistenceContext = persistenceContext;
|
_persistenceContext = persistenceContext;
|
||||||
_addonLifecycle = addonLifecycle;
|
|
||||||
|
|
||||||
_addonLifecycle.RegisterListener(AddonEvent.PreDraw, AddonName, PreDraw);
|
_framework.Update += FrameworkUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PreDraw(AddonEvent type, AddonArgs args)
|
private AddonItemSearchResult* ItemSearchResult => (AddonItemSearchResult*)_gameGui.GetAddonByName("ItemSearchResult");
|
||||||
|
|
||||||
|
private void FrameworkUpdate(Framework framework)
|
||||||
{
|
{
|
||||||
UpdateRetainerNames((AddonItemSearchResult*)args.Addon);
|
var addon = ItemSearchResult;
|
||||||
|
if (addon == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_drawHook ??= Hook<Draw>.FromAddress(
|
||||||
|
new nint(addon->AtkUnitBase.AtkEventListener.vfunc[42]),
|
||||||
|
AddonDraw);
|
||||||
|
_drawHook.Enable();
|
||||||
|
_framework.Update -= FrameworkUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateRetainerNames(AddonItemSearchResult* addon)
|
private void AddonDraw(AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
UpdateRetainerNames();
|
||||||
|
_drawHook!.Original(addon);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRetainerNames()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var addon = ItemSearchResult;
|
||||||
if (addon == null || !addon->AtkUnitBase.IsVisible)
|
if (addon == null || !addon->AtkUnitBase.IsVisible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -51,19 +73,13 @@ 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('('))
|
||||||
{
|
{
|
||||||
string playerName = _persistenceContext.GetCharacterNameOnCurrentWorld(retainerName);
|
string playerName = _persistenceContext.GetCharacterNameOnCurrentWorld(retainerName);
|
||||||
if (!string.IsNullOrEmpty(playerName))
|
if (!string.IsNullOrEmpty(playerName))
|
||||||
@ -79,6 +95,8 @@ internal sealed unsafe class MarketBoardUiHandler : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_addonLifecycle.UnregisterListener(AddonEvent.PreDraw, AddonName, PreDraw);
|
_drawHook?.Dispose();
|
||||||
|
_framework.Update -= FrameworkUpdate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
61
RetainerTrack/Handlers/NetworkHandler.cs
Normal file
61
RetainerTrack/Handlers/NetworkHandler.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Data;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Game.Network;
|
||||||
|
using Dalamud.Game.Network.Structures;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework;
|
||||||
|
|
||||||
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
|
internal sealed class NetworkHandler : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger<NetworkHandler> _logger;
|
||||||
|
private readonly GameNetwork _gameNetwork;
|
||||||
|
private readonly DataManager _dataManager;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
|
||||||
|
public NetworkHandler(
|
||||||
|
ILogger<NetworkHandler> logger,
|
||||||
|
GameNetwork gameNetwork,
|
||||||
|
DataManager dataManager,
|
||||||
|
ClientState clientState,
|
||||||
|
PersistenceContext persistenceContext)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_gameNetwork = gameNetwork;
|
||||||
|
_dataManager = dataManager;
|
||||||
|
_clientState = clientState;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
|
|
||||||
|
_gameNetwork.NetworkMessage += NetworkMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_gameNetwork.NetworkMessage -= NetworkMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NetworkMessage(nint dataPtr, ushort opcode, uint sourceActorId, uint targetActorId,
|
||||||
|
NetworkMessageDirection direction)
|
||||||
|
{
|
||||||
|
if (direction != NetworkMessageDirection.ZoneDown || !_dataManager.IsDataReady)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (opcode == _dataManager.ServerOpCodes["MarketBoardOfferings"])
|
||||||
|
{
|
||||||
|
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
|
||||||
|
if (worldId == 0)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Skipping market board handler, current world unknown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var listings = MarketBoardCurrentOfferings.Read(dataPtr);
|
||||||
|
Task.Run(() => _persistenceContext.HandleMarketBoardPage(listings, worldId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
70
RetainerTrack/Handlers/PartyHandler.cs
Normal file
70
RetainerTrack/Handlers/PartyHandler.cs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Memory;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||||
|
|
||||||
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
|
internal sealed class PartyHandler : IDisposable
|
||||||
|
{
|
||||||
|
private readonly Framework _framework;
|
||||||
|
private readonly ClientState _clientState;
|
||||||
|
private readonly PersistenceContext _persistenceContext;
|
||||||
|
|
||||||
|
private long _lastUpdate = 0;
|
||||||
|
|
||||||
|
public PartyHandler(Framework framework, ClientState clientState, PersistenceContext persistenceContext)
|
||||||
|
{
|
||||||
|
_framework = framework;
|
||||||
|
_clientState = clientState;
|
||||||
|
_persistenceContext = persistenceContext;
|
||||||
|
|
||||||
|
_framework.Update += FrameworkUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void FrameworkUpdate(Framework _)
|
||||||
|
{
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +1,44 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
using Dalamud.Game.Network.Structures;
|
using Dalamud.Game.Network.Structures;
|
||||||
using Dalamud.Plugin.Services;
|
using LiteDB;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using RetainerTrack.Database;
|
using RetainerTrack.Database;
|
||||||
|
|
||||||
namespace RetainerTrack.Handlers;
|
namespace RetainerTrack.Handlers
|
||||||
|
{
|
||||||
internal sealed class PersistenceContext
|
internal sealed class PersistenceContext
|
||||||
{
|
{
|
||||||
private readonly ILogger<PersistenceContext> _logger;
|
private readonly ILogger<PersistenceContext> _logger;
|
||||||
private readonly IClientState _clientState;
|
private readonly ClientState _clientState;
|
||||||
private readonly IServiceProvider _serviceProvider;
|
private readonly LiteDatabase _liteDatabase;
|
||||||
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, ClientState clientState,
|
||||||
IServiceProvider serviceProvider)
|
LiteDatabase liteDatabase)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_serviceProvider = serviceProvider;
|
_liteDatabase = liteDatabase;
|
||||||
|
|
||||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
var retainersByWorld = dbContext.Retainers.GroupBy(retainer => retainer.WorldId);
|
|
||||||
|
|
||||||
|
var retainersByWorld = _liteDatabase.GetCollection<Retainer>().FindAll()
|
||||||
|
.GroupBy(r => r.WorldId);
|
||||||
foreach (var retainers in retainersByWorld)
|
foreach (var retainers in retainersByWorld)
|
||||||
{
|
{
|
||||||
var world = _worldRetainerCache.GetOrAdd(retainers.Key, _ => new());
|
var world = _worldRetainerCache.GetOrAdd(retainers.Key, _ => new());
|
||||||
foreach (var retainer in retainers)
|
foreach (var retainer in retainers)
|
||||||
{
|
{
|
||||||
if (retainer.Name != null)
|
if (retainer.Name != null)
|
||||||
world[retainer.Name] = retainer.OwnerLocalContentId;
|
world[retainer.Name] = retainer.OwnerContentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var player in dbContext.Players)
|
foreach (var player in _liteDatabase.GetCollection<Player>().FindAll())
|
||||||
{
|
_playerNameCache[player.Id] = player.Name ?? string.Empty;
|
||||||
_playerCache[player.LocalContentId] = new CachedPlayer
|
|
||||||
{
|
|
||||||
AccountId = player.AccountId,
|
|
||||||
Name = player.Name ?? string.Empty,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetCharacterNameOnCurrentWorld(string retainerName)
|
public string GetCharacterNameOnCurrentWorld(string retainerName)
|
||||||
@ -63,97 +51,40 @@ 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 void HandleMarketBoardPage(MarketBoardCurrentOfferings listings, ushort worldId)
|
||||||
{
|
|
||||||
using var scope = _serviceProvider.CreateScope();
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
return dbContext.Players.Where(p => characterName == p.Name)
|
|
||||||
.SelectMany(player =>
|
|
||||||
dbContext.Retainers.Where(x => x.OwnerLocalContentId == player.LocalContentId && x.WorldId == world))
|
|
||||||
.Select(x => x.Name)
|
|
||||||
.Where(x => !string.IsNullOrEmpty(x))
|
|
||||||
.Cast<string>()
|
|
||||||
.ToList()
|
|
||||||
.AsReadOnly();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HandleMarketBoardPage(IMarketBoardCurrentOfferings currentOfferings, 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 =>
|
||||||
new Retainer
|
new Retainer
|
||||||
{
|
{
|
||||||
LocalContentId = l.RetainerId,
|
Id = l.RetainerId,
|
||||||
Name = l.RetainerName,
|
Name = l.RetainerName,
|
||||||
WorldId = worldId,
|
WorldId = worldId,
|
||||||
OwnerLocalContentId = l.RetainerOwnerId,
|
OwnerContentId = 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();
|
||||||
|
_liteDatabase.GetCollection<Retainer>().Upsert(updates);
|
||||||
using var scope = _serviceProvider.CreateScope();
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
|
|
||||||
foreach (var retainer in updates)
|
foreach (var retainer in updates)
|
||||||
{
|
{
|
||||||
Retainer? dbRetainer = dbContext.Retainers.Find(retainer.LocalContentId);
|
if (!_playerNameCache.TryGetValue(retainer.OwnerContentId, out string? ownerName))
|
||||||
if (dbRetainer != null)
|
ownerName = retainer.OwnerContentId.ToString();
|
||||||
{
|
_logger.LogTrace("Retainer {RetainerName} belongs to {OwnerId}", retainer.Name,
|
||||||
_logger.LogDebug("Updating retainer {RetainerName} with {LocalContentId}", retainer.Name,
|
|
||||||
retainer.LocalContentId);
|
|
||||||
dbRetainer.Name = retainer.Name;
|
|
||||||
dbRetainer.WorldId = retainer.WorldId;
|
|
||||||
dbRetainer.OwnerLocalContentId = retainer.OwnerLocalContentId;
|
|
||||||
dbContext.Retainers.Update(dbRetainer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Adding retainer {RetainerName} with {LocalContentId}", retainer.Name,
|
|
||||||
retainer.LocalContentId);
|
|
||||||
dbContext.Retainers.Add(retainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
string ownerName;
|
|
||||||
if (_playerCache.TryGetValue(retainer.OwnerLocalContentId, out CachedPlayer? cachedPlayer))
|
|
||||||
ownerName = cachedPlayer.Name;
|
|
||||||
else
|
|
||||||
ownerName = retainer.OwnerLocalContentId.ToString(CultureInfo.InvariantCulture);
|
|
||||||
_logger.LogDebug(" Retainer {RetainerName} belongs to {OwnerName}", retainer.Name,
|
|
||||||
ownerName);
|
ownerName);
|
||||||
|
|
||||||
if (retainer.Name != null)
|
if (retainer.Name != null)
|
||||||
{
|
{
|
||||||
var world = _worldRetainerCache.GetOrAdd(retainer.WorldId, _ => new());
|
var world = _worldRetainerCache.GetOrAdd(retainer.WorldId, _ => new());
|
||||||
world[retainer.Name] = retainer.OwnerLocalContentId;
|
world[retainer.Name] = retainer.OwnerContentId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int changeCount = dbContext.SaveChanges();
|
|
||||||
if (changeCount > 0)
|
|
||||||
_logger.LogDebug("Saved {Count} retainer mappings", changeCount);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -161,129 +92,37 @@ 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
|
||||||
return;
|
|
||||||
|
|
||||||
if (_playerCache.TryGetValue(mapping.ContentId, out CachedPlayer? cachedPlayer))
|
|
||||||
{
|
|
||||||
if (mapping.PlayerName == cachedPlayer.Name && mapping.AccountId == cachedPlayer.AccountId)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
Name = mapping.PlayerName,
|
|
||||||
AccountId = mapping.AccountId,
|
|
||||||
});
|
|
||||||
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 => mapping.ContentId != 0 && !string.IsNullOrEmpty(mapping.PlayerName))
|
||||||
.Where(mapping =>
|
.Where(mapping =>
|
||||||
{
|
{
|
||||||
if (_playerCache.TryGetValue(mapping.ContentId, out CachedPlayer? cachedPlayer))
|
if (_playerNameCache.TryGetValue(mapping.ContentId, out string? existingName))
|
||||||
return mapping.PlayerName != cachedPlayer.Name || mapping.AccountId != cachedPlayer.AccountId;
|
return mapping.PlayerName != existingName;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
|
.Select(mapping =>
|
||||||
|
new Player
|
||||||
|
{
|
||||||
|
Id = mapping.ContentId,
|
||||||
|
Name = mapping.PlayerName,
|
||||||
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
_liteDatabase.GetCollection<Player>().Upsert(updates);
|
||||||
if (updates.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var scope = _serviceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
foreach (var update in updates)
|
|
||||||
{
|
|
||||||
var dbPlayer = dbContext.Players.Find(update.ContentId);
|
|
||||||
if (dbPlayer == null)
|
|
||||||
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();
|
|
||||||
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.Id] = 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;
|
|
||||||
}
|
|
@ -1,20 +1,68 @@
|
|||||||
<Project Sdk="Dalamud.NET.Sdk/9.0.2">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>4.3</Version>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
|
<Version>1.0</Version>
|
||||||
|
<LangVersion>11.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="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
|
<PackageReference Include="DalamudPackager" Version="2.1.10" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5" Condition="'$(Configuration)' == 'EF'">
|
<PackageReference Include="LiteDB" Version="5.0.15" />
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Dalamud">
|
||||||
|
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ImGui.NET">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ImGuiScene">
|
||||||
|
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Lumina.Excel">
|
||||||
|
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="FFXIVClientStructs">
|
||||||
|
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
|
||||||
|
<Private>false</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
|
||||||
|
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip" />
|
||||||
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -4,6 +4,5 @@
|
|||||||
"Punchline": "Track who a retainer belongs to.",
|
"Punchline": "Track who a retainer belongs to.",
|
||||||
"Description": "Keeps track of who a retainer belongs to - if you have seen the retainer's owner locally (if you were on the same party, or if you have seen an item crafted by them).\n\nTracking is unavailable if the crafter isn't the retainer owner (to avoid resellers being attributed incorrectly), and if the tooltip says 'Obtaining Signature'.",
|
"Description": "Keeps track of who a retainer belongs to - if you have seen the retainer's owner locally (if you were on the same party, or if you have seen an item crafted by them).\n\nTracking is unavailable if the crafter isn't the retainer owner (to avoid resellers being attributed incorrectly), and if the tooltip says 'Obtaining Signature'.",
|
||||||
"RepoUrl": "https://git.carvel.li/liza/RetainerTrack",
|
"RepoUrl": "https://git.carvel.li/liza/RetainerTrack",
|
||||||
"IconUrl": "https://plugins.carvel.li/icons/RetainerTrack.png",
|
|
||||||
"Tags": ["retainer", "track"]
|
"Tags": ["retainer", "track"]
|
||||||
}
|
}
|
||||||
|
@ -1,109 +1,77 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
using Dalamud.Data;
|
||||||
using Dalamud.Extensions.MicrosoftLogging;
|
using Dalamud.Extensions.MicrosoftLogging;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Game.ClientState;
|
||||||
|
using Dalamud.Game.Gui;
|
||||||
|
using Dalamud.Game.Network;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using LiteDB;
|
||||||
using Microsoft.Data.Sqlite;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using RetainerTrack.Commands;
|
|
||||||
using RetainerTrack.Database;
|
using RetainerTrack.Database;
|
||||||
using RetainerTrack.Database.Compiled;
|
|
||||||
using RetainerTrack.Handlers;
|
using RetainerTrack.Handlers;
|
||||||
|
|
||||||
namespace RetainerTrack;
|
namespace RetainerTrack
|
||||||
|
{
|
||||||
// ReSharper disable once UnusedType.Global
|
// ReSharper disable once UnusedType.Global
|
||||||
internal sealed class RetainerTrackPlugin : IDalamudPlugin
|
internal sealed class RetainerTrackPlugin : IDalamudPlugin
|
||||||
{
|
{
|
||||||
public const string DatabaseFileName = "retainertrack.data.sqlite3";
|
|
||||||
private readonly string _sqliteConnectionString;
|
|
||||||
private readonly ServiceProvider? _serviceProvider;
|
private readonly ServiceProvider? _serviceProvider;
|
||||||
|
|
||||||
|
public string Name => "RetainerTrack";
|
||||||
|
|
||||||
public RetainerTrackPlugin(
|
public RetainerTrackPlugin(
|
||||||
IDalamudPluginInterface pluginInterface,
|
DalamudPluginInterface pluginInterface,
|
||||||
IFramework framework,
|
GameNetwork gameNetwork,
|
||||||
IClientState clientState,
|
DataManager dataManager,
|
||||||
IGameGui gameGui,
|
Framework framework,
|
||||||
IChatGui chatGui,
|
ClientState clientState,
|
||||||
IGameInteropProvider gameInteropProvider,
|
GameGui gameGui)
|
||||||
IAddonLifecycle addonLifecycle,
|
|
||||||
ICommandManager commandManager,
|
|
||||||
IDataManager dataManager,
|
|
||||||
ITargetManager targetManager,
|
|
||||||
IObjectTable objectTable,
|
|
||||||
IMarketBoard marketBoard,
|
|
||||||
IPluginLog pluginLog)
|
|
||||||
{
|
{
|
||||||
ServiceCollection serviceCollection = new();
|
ServiceCollection serviceCollection = new();
|
||||||
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace)
|
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace)
|
||||||
.ClearProviders()
|
.ClearProviders()
|
||||||
.AddDalamudLogger(pluginLog)
|
.AddDalamudLogger(this));
|
||||||
.AddFilter("Microsoft.EntityFrameworkCore", LogLevel.Warning));
|
|
||||||
serviceCollection.AddSingleton<IDalamudPlugin>(this);
|
serviceCollection.AddSingleton<IDalamudPlugin>(this);
|
||||||
serviceCollection.AddSingleton(pluginInterface);
|
serviceCollection.AddSingleton(pluginInterface);
|
||||||
|
serviceCollection.AddSingleton(gameNetwork);
|
||||||
|
serviceCollection.AddSingleton(dataManager);
|
||||||
serviceCollection.AddSingleton(framework);
|
serviceCollection.AddSingleton(framework);
|
||||||
serviceCollection.AddSingleton(clientState);
|
serviceCollection.AddSingleton(clientState);
|
||||||
serviceCollection.AddSingleton(gameGui);
|
serviceCollection.AddSingleton(gameGui);
|
||||||
serviceCollection.AddSingleton(chatGui);
|
|
||||||
serviceCollection.AddSingleton(gameInteropProvider);
|
serviceCollection.AddSingleton<LiteDatabase>(_ =>
|
||||||
serviceCollection.AddSingleton(addonLifecycle);
|
new LiteDatabase(new ConnectionString
|
||||||
serviceCollection.AddSingleton(commandManager);
|
{
|
||||||
serviceCollection.AddSingleton(dataManager);
|
Filename = Path.Join(pluginInterface.GetPluginConfigDirectory(), "retainer-data.litedb"),
|
||||||
serviceCollection.AddSingleton(targetManager);
|
Connection = ConnectionType.Direct,
|
||||||
serviceCollection.AddSingleton(objectTable);
|
Upgrade = true,
|
||||||
serviceCollection.AddSingleton(marketBoard);
|
}));
|
||||||
|
|
||||||
serviceCollection.AddSingleton<PersistenceContext>();
|
serviceCollection.AddSingleton<PersistenceContext>();
|
||||||
serviceCollection.AddSingleton<MarketBoardOfferingsHandler>();
|
serviceCollection.AddSingleton<NetworkHandler>();
|
||||||
|
serviceCollection.AddSingleton<PartyHandler>();
|
||||||
serviceCollection.AddSingleton<MarketBoardUiHandler>();
|
serviceCollection.AddSingleton<MarketBoardUiHandler>();
|
||||||
serviceCollection.AddSingleton<ObjectTableHandler>();
|
|
||||||
serviceCollection.AddSingleton<GameHooks>();
|
serviceCollection.AddSingleton<GameHooks>();
|
||||||
serviceCollection.AddSingleton<AccountIdCommand>();
|
|
||||||
serviceCollection.AddSingleton<WhoCommand>();
|
|
||||||
|
|
||||||
_sqliteConnectionString = PrepareSqliteDb(serviceCollection, pluginInterface.GetPluginConfigDirectory());
|
|
||||||
_serviceProvider = serviceCollection.BuildServiceProvider();
|
_serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
|
LiteDatabase liteDatabase = _serviceProvider.GetRequiredService<LiteDatabase>();
|
||||||
|
liteDatabase.GetCollection<Retainer>()
|
||||||
|
.EnsureIndex(x => x.Id);
|
||||||
|
liteDatabase.GetCollection<Player>()
|
||||||
|
.EnsureIndex(x => x.Id);
|
||||||
|
|
||||||
RunMigrations(_serviceProvider);
|
_serviceProvider.GetRequiredService<PartyHandler>();
|
||||||
InitializeRequiredServices(_serviceProvider);
|
_serviceProvider.GetRequiredService<NetworkHandler>();
|
||||||
}
|
_serviceProvider.GetRequiredService<MarketBoardUiHandler>();
|
||||||
|
_serviceProvider.GetRequiredService<GameHooks>();
|
||||||
private static string PrepareSqliteDb(IServiceCollection serviceCollection, string getPluginConfigDirectory)
|
|
||||||
{
|
|
||||||
string connectionString = $"Data Source={Path.Join(getPluginConfigDirectory, DatabaseFileName)}";
|
|
||||||
serviceCollection.AddDbContext<RetainerTrackContext>(o => o
|
|
||||||
.UseSqlite(connectionString)
|
|
||||||
.UseModel(RetainerTrackContextModel.Instance));
|
|
||||||
return connectionString;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunMigrations(IServiceProvider serviceProvider)
|
|
||||||
{
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
|
||||||
using var dbContext = scope.ServiceProvider.GetRequiredService<RetainerTrackContext>();
|
|
||||||
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();
|
||||||
|
}
|
||||||
// ensure we're not keeping the file open longer than the plugin is loaded
|
|
||||||
using (SqliteConnection sqliteConnection = new(_sqliteConnectionString))
|
|
||||||
SqliteConnection.ClearPool(sqliteConnection);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,290 +1,73 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"net8.0-windows7.0": {
|
"net7.0-windows7.0": {
|
||||||
"Dalamud.Extensions.MicrosoftLogging": {
|
"Dalamud.Extensions.MicrosoftLogging": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[3.0.0, )",
|
"requested": "[1.0.0, )",
|
||||||
"resolved": "3.0.0",
|
"resolved": "1.0.0",
|
||||||
"contentHash": "jWK3r/cZUXN8H9vHf78gEzeRmMk4YAbCUYzLcTqUAcega8unUiFGwYy+iOjVYJ9urnr9r+hk+vBi1y9wyv+e7Q==",
|
"contentHash": "nPjMrT9n9GJ+TYF1lyVhlvhmFyN4ajMX2ccclgyMc8MNpOGZwxrJ4VEtrUUk7UkuX2wAhtnNsjrcf5sER3/CbA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.Logging": "8.0.0"
|
"Microsoft.Extensions.Logging": "7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DalamudPackager": {
|
"DalamudPackager": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[2.1.13, )",
|
"requested": "[2.1.10, )",
|
||||||
"resolved": "2.1.13",
|
"resolved": "2.1.10",
|
||||||
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
|
"contentHash": "S6NrvvOnLgT4GDdgwuKVJjbFo+8ZEj+JsEYk9ojjOR/MMfv1dIFpT8aRJQfI24rtDcw1uF+GnSSMN4WW1yt7fw=="
|
||||||
},
|
},
|
||||||
"DotNet.ReproducibleBuilds": {
|
"LiteDB": {
|
||||||
"type": "Direct",
|
"type": "Direct",
|
||||||
"requested": "[1.1.1, )",
|
"requested": "[5.0.15, )",
|
||||||
"resolved": "1.1.1",
|
"resolved": "5.0.15",
|
||||||
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
|
"contentHash": "nucyfCOGSATH553BxplxExP3BOqEwmHt0B57426EIaQjD3CC1Odb52VVCGgTxyYaD2oe3B/cJk8jDo6XiBJqPg=="
|
||||||
"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": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[8.0.5, )",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "rBTx2TP+pa+CgXIxWmUbPdO+53WV4Nmq9Njb5Olomh4og/p5qV1jU53wPpqO92gEv+ZR6arwP5Pe11XImYTT+A==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite.Core": "8.0.5",
|
|
||||||
"SQLitePCLRaw.bundle_e_sqlite3": "2.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.Gitea": {
|
|
||||||
"type": "Direct",
|
|
||||||
"requested": "[8.0.0, )",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "KOBodmDnlWGIqZt2hT47Q69TIoGhIApDVLCyyj9TT5ct8ju16AbHYcB4XeknoHX562wO1pMS/1DfBIZK+V+sxg==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
|
||||||
"Microsoft.SourceLink.Common": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Build.Tasks.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
|
||||||
},
|
|
||||||
"Microsoft.Data.Sqlite.Core": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "JMGBNGTPsrLM14j5gDG2r5/I1nbbQd1ZdgeUnF7uca8RHYin6wZpFtQNYYqOMUpSxJak55trXE9B8/X2X+pOXw==",
|
|
||||||
"dependencies": {
|
|
||||||
"SQLitePCLRaw.core": "2.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.EntityFrameworkCore": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "sqpDZgfzmTPXy/jCekqTaPDwqRDjtdGmIL+eqFfXtVAoH4AanWjeyxQ1ej3uVnTQO6f23+m9+ggJDVcgyPJxcA==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.EntityFrameworkCore.Abstractions": "8.0.5",
|
|
||||||
"Microsoft.EntityFrameworkCore.Analyzers": "8.0.5",
|
|
||||||
"Microsoft.Extensions.Caching.Memory": "8.0.0",
|
|
||||||
"Microsoft.Extensions.Logging": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.EntityFrameworkCore.Abstractions": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "qwYdfjFKtmTXX8NIm0MuZxUkon1tcw+aF5huzR7YOVr/tR3s4fqw9DWcvc23l3Jhpo/uGHWZcNPyFlI2CD3Usg=="
|
|
||||||
},
|
|
||||||
"Microsoft.EntityFrameworkCore.Analyzers": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "LzoKedC+9A8inF5d3iIzgyv/JDXgKrtpYoGIC3EqGWuHVDm9s/IHHApeTOTbzvnr7yBVV+nmYfyT1nwtzRDp0Q=="
|
|
||||||
},
|
|
||||||
"Microsoft.EntityFrameworkCore.Relational": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "x2bdSK3eKKEQkDdYcGxxDU+S7NqhBiz/Fciz01Mafz9P71VRdP3JskKHaZvwK0/sNEAT3hS7BTsDQGUA2F9mAA==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.EntityFrameworkCore": "8.0.5",
|
|
||||||
"Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.EntityFrameworkCore.Sqlite.Core": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.5",
|
|
||||||
"contentHash": "txwDTpgWFeuTLHh4gYxzKnSWx2jtpX3qxRYkMgfLmjZAe5vYxHKPsTNCa7AKR78ZqrUM7iZ5bBiS3s1Q7oZi4g==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Data.Sqlite.Core": "8.0.5",
|
|
||||||
"Microsoft.EntityFrameworkCore.Relational": "8.0.5",
|
|
||||||
"Microsoft.Extensions.DependencyModel": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.Caching.Abstractions": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "3KuSxeHoNYdxVYfg2IRZCThcrlJ1XJqIXkAWikCsbm5C/bCjv7G0WoKDyuR98Q+T607QT2Zl5GsbGRkENcV2yQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.Caching.Memory": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "7pqivmrZDzo1ADPkRwjy+8jtRKWRCPag9qPI+p7sgu7Q4QreWhcvbiWXsbhP+yY8XSiDvZpu2/LWdBv7PnmOpQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.Caching.Abstractions": "8.0.0",
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
|
||||||
"Microsoft.Extensions.Options": "8.0.0",
|
|
||||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.Configuration.Abstractions": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.DependencyInjection": {
|
"Microsoft.Extensions.DependencyInjection": {
|
||||||
"type": "Transitive",
|
"type": "Direct",
|
||||||
"resolved": "8.0.0",
|
"requested": "[7.0.0, )",
|
||||||
"contentHash": "V8S3bsm50ig6JSyrbcJJ8bW2b9QLGouz+G1miK3UTaOWmMtFwNNNzUf4AleyDWUmTrWMLNnFSLEQtxmxgNQnNQ==",
|
"resolved": "7.0.0",
|
||||||
|
"contentHash": "elNeOmkeX3eDVG6pYVeV82p29hr+UKDaBhrZyWvWLw/EVZSYEkZlQdkp0V39k/Xehs2Qa0mvoCvkVj3eQxNQ1Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
"Microsoft.Extensions.DependencyInjection.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "7.0.0",
|
||||||
"contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
|
"contentHash": "h3j/QfmFN4S0w4C2A6X7arXij/M/OVw3uQHSOFxnND4DyAzO1F9eMX7Eti7lU/OkSthEE0WzRsfT/Dmx86jzCw=="
|
||||||
},
|
|
||||||
"Microsoft.Extensions.DependencyModel": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "NSmDw3K0ozNDgShSIpsZcbFIzBX4w28nDag+TfaQujkXGazBm+lid5onlWoCBy4VsLxqnnKjEBbGSJVWJMf43g==",
|
|
||||||
"dependencies": {
|
|
||||||
"System.Text.Encodings.Web": "8.0.0",
|
|
||||||
"System.Text.Json": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging": {
|
"Microsoft.Extensions.Logging": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "7.0.0",
|
||||||
"contentHash": "tvRkov9tAJ3xP51LCv3FJ2zINmv1P8Hi8lhhtcKGqM+ImiTCC84uOPEI4z8Cdq2C3o9e+Aa0Gw0rmrsJD77W+w==",
|
"contentHash": "Nw2muoNrOG5U5qa2ZekXwudUn2BJcD41e65zwmDHb1fQegTX66UokLWZkJRpqSSHXDOWZ5V0iqhbxOEky91atA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.DependencyInjection": "8.0.0",
|
"Microsoft.Extensions.DependencyInjection": "7.0.0",
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "8.0.0",
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||||
"Microsoft.Extensions.Options": "8.0.0"
|
"Microsoft.Extensions.Logging.Abstractions": "7.0.0",
|
||||||
|
"Microsoft.Extensions.Options": "7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Logging.Abstractions": {
|
"Microsoft.Extensions.Logging.Abstractions": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "7.0.0",
|
||||||
"contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
|
"contentHash": "kmn78+LPVMOWeITUjIlfxUPDsI0R6G0RkeAMBmQxAJ7vBJn4q2dTva7pWi65ceN5vPGjJ9q/Uae2WKgvfktJAw=="
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Options": {
|
"Microsoft.Extensions.Options": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "7.0.0",
|
||||||
"contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==",
|
"contentHash": "lP1yBnTTU42cKpMozuafbvNtQ7QcBjr/CcK3bYOGEMH55Fjt+iecXjT6chR7vbgCMqy3PG3aNQSZgo/EuY/9qQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
|
"Microsoft.Extensions.DependencyInjection.Abstractions": "7.0.0",
|
||||||
"Microsoft.Extensions.Primitives": "8.0.0"
|
"Microsoft.Extensions.Primitives": "7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.Extensions.Primitives": {
|
"Microsoft.Extensions.Primitives": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "8.0.0",
|
"resolved": "7.0.0",
|
||||||
"contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
|
"contentHash": "um1KU5kxcRp3CNuI8o/GrZtD4AIOXDk+RLsytjZ9QPok3ttLUelLKpilVPuaFT3TFjOhSibUAso0odbOaCDj3Q=="
|
||||||
},
|
|
||||||
"Microsoft.SourceLink.AzureRepos.Git": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "1.1.1",
|
|
||||||
"contentHash": "qB5urvw9LO2bG3eVAkuL+2ughxz2rR7aYgm2iyrB8Rlk9cp2ndvGRCvehk3rNIhRuNtQaeKwctOl1KvWiklv5w==",
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Build.Tasks.Git": "1.1.1",
|
|
||||||
"Microsoft.SourceLink.Common": "1.1.1"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Microsoft.SourceLink.Bitbucket.Git": {
|
"net7.0-windows7.0/win-x64": {}
|
||||||
"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": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "2.1.6",
|
|
||||||
"contentHash": "BmAf6XWt4TqtowmiWe4/5rRot6GerAeklmOPfviOvwLoF5WwgxcJHAxZtySuyW9r9w+HLILnm8VfJFLCUJYW8A==",
|
|
||||||
"dependencies": {
|
|
||||||
"SQLitePCLRaw.lib.e_sqlite3": "2.1.6",
|
|
||||||
"SQLitePCLRaw.provider.e_sqlite3": "2.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SQLitePCLRaw.core": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "2.1.6",
|
|
||||||
"contentHash": "wO6v9GeMx9CUngAet8hbO7xdm+M42p1XeJq47ogyRoYSvNSp0NGLI+MgC0bhrMk9C17MTVFlLiN6ylyExLCc5w==",
|
|
||||||
"dependencies": {
|
|
||||||
"System.Memory": "4.5.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "2.1.6",
|
|
||||||
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
|
|
||||||
},
|
|
||||||
"SQLitePCLRaw.provider.e_sqlite3": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "2.1.6",
|
|
||||||
"contentHash": "PQ2Oq3yepLY4P7ll145P3xtx2bX8xF4PzaKPRpw9jZlKvfe4LE/saAV82inND9usn1XRpmxXk7Lal3MTI+6CNg==",
|
|
||||||
"dependencies": {
|
|
||||||
"SQLitePCLRaw.core": "2.1.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Memory": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "4.5.3",
|
|
||||||
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
|
|
||||||
},
|
|
||||||
"System.Text.Encodings.Web": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
|
|
||||||
},
|
|
||||||
"System.Text.Json": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "OdrZO2WjkiEG6ajEFRABTRCi/wuXQPxeV6g8xvUJqdxMvvuCCEk86zPla8UiIQJz3durtUEbNyY/3lIhS0yZvQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"System.Text.Encodings.Web": "8.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"net8.0-windows7.0/win-x64": {
|
|
||||||
"SQLitePCLRaw.lib.e_sqlite3": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "2.1.6",
|
|
||||||
"contentHash": "2ObJJLkIUIxRpOUlZNGuD4rICpBnrBR5anjyfUFQep4hMOIeqW+XGQYzrNmHSVz5xSWZ3klSbh7sFR6UyDj68Q=="
|
|
||||||
},
|
|
||||||
"System.Text.Encodings.Web": {
|
|
||||||
"type": "Transitive",
|
|
||||||
"resolved": "8.0.0",
|
|
||||||
"contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user