Compare commits

..

No commits in common. "master" and "v0.9" have entirely different histories.
master ... v0.9

42 changed files with 455 additions and 1672 deletions

@ -1 +1 @@
Subproject commit a63c8e7154e272374ffa03d5c801736d4229e38a
Subproject commit 2715088db81d2be34447b27b0e5e5a3e766e47c7

@ -1 +1 @@
Subproject commit 11fd2f06e1374e846e1aada06071da5fc7ef697a
Subproject commit f1c688a0599b41d70230021328a575da7351cf91

View File

@ -1,3 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Allagan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ceruleum/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using LLib;
namespace Influx.AllaganTools;
@ -17,14 +15,15 @@ internal sealed class AllaganToolsIpc : IDisposable
private readonly DalamudReflector _dalamudReflector;
private readonly IFramework _framework;
private readonly IPluginLog _pluginLog;
private readonly ICallGateSubscriber<bool, bool> _initialized;
private readonly ICallGateSubscriber<bool, bool>? _initialized;
private readonly ICallGateSubscriber<bool>? _isInitialized;
private readonly ICallGateSubscriber<Dictionary<string, string>> _getSearchFilters;
private ICharacterMonitor _characters;
private IInventoryMonitor _inventories;
private IListService _lists;
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor();
public IFilterService Filters { get; set; } = new UnavailableFilterService();
public AllaganToolsIpc(IDalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector,
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector,
IFramework framework, IPluginLog pluginLog)
{
_chatGui = chatGui;
@ -33,20 +32,14 @@ internal sealed class AllaganToolsIpc : IDisposable
_pluginLog = pluginLog;
_initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
_initialized.Subscribe(ConfigureIpc);
_getSearchFilters =
pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
_characters = new UnavailableCharacterMonitor(_pluginLog);
_inventories = new UnavailableInventoryMonitor(_pluginLog);
_lists = new UnavailableListService(_pluginLog);
_initialized.Subscribe(ConfigureIpc);
try
{
ICallGateSubscriber<bool> isInitializedFunc =
pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
bool isInitialized = isInitializedFunc.InvokeFunc();
bool isInitialized = _isInitialized.InvokeFunc();
if (isInitialized)
ConfigureIpc(true);
}
@ -65,25 +58,12 @@ internal sealed class AllaganToolsIpc : IDisposable
{
if (_dalamudReflector.TryGetDalamudPlugin("Allagan Tools", out var it, false, true))
{
var hostedPlugin = it.GetType().BaseType!;
var host = hostedPlugin.GetField("host", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(it)!;
var serviceProvider = host.GetType().GetProperty("Services")!.GetValue(host)!;
var getServiceMethod = serviceProvider.GetType().GetMethod("GetService")!;
object GetService(Type t) => getServiceMethod.Invoke(serviceProvider, [t])!;
var pluginService = it.GetType().Assembly.GetType("InventoryTools.PluginService")!;
var ccl = it.GetType()
.GetField("_service", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(it)!
.GetType()
.Assembly;
_characters =
new CharacterMonitor(GetService(ccl.GetType("CriticalCommonLib.Services.ICharacterMonitor")!));
_inventories = new InventoryMonitor(
GetService(ccl.GetType("CriticalCommonLib.Services.IInventoryMonitor")!));
_lists = new ListService(
GetService(it.GetType().Assembly.GetType("InventoryTools.Services.Interfaces.IListService")!),
GetService(it.GetType().Assembly.GetType("InventoryTools.Lists.ListFilterService")!));
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
Inventories = new InventoryMonitor(
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
Filters = new FilterService(pluginService.GetProperty("FilterService")!.GetValue(null)!);
}
else
{
@ -111,11 +91,11 @@ internal sealed class AllaganToolsIpc : IDisposable
}
}
public FilterResult? GetFilter(string keyOrName)
public Filter? GetFilter(string keyOrName)
{
try
{
return _lists.GetFilterByKeyOrName(keyOrName);
return Filters.GetFilterByKeyOrName(keyOrName);
}
catch (IpcError e)
{
@ -126,9 +106,9 @@ internal sealed class AllaganToolsIpc : IDisposable
public Dictionary<Character, Currencies> CountCurrencies()
{
_pluginLog.Verbose($"Updating characters with {_characters.GetType()} and {_inventories.GetType()}");
var characters = _characters.All.ToDictionary(x => x.CharacterId, x => x);
return _inventories.All
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}");
var characters = Characters.All.ToDictionary(x => x.CharacterId, x => x);
return Inventories.All
.Where(x => characters.ContainsKey(x.Value.CharacterId))
.ToDictionary(
x => characters[x.Value.CharacterId],
@ -144,23 +124,27 @@ internal sealed class AllaganToolsIpc : IDisposable
Ventures = inv.Sum(21072),
CeruleumTanks = inv.Sum(10155),
RepairKits = inv.Sum(10373),
FreeSlots = inv.FreeInventorySlots,
};
});
}
public void Dispose()
{
_initialized.Unsubscribe(ConfigureIpc);
_characters = new UnavailableCharacterMonitor(_pluginLog);
_inventories = new UnavailableInventoryMonitor(_pluginLog);
_lists = new UnavailableListService(_pluginLog);
_initialized?.Unsubscribe(ConfigureIpc);
Characters = new UnavailableCharacterMonitor();
Inventories = new UnavailableInventoryMonitor();
Filters = new UnavailableFilterService();
}
private sealed class InventoryWrapper(IEnumerable<InventoryItem> items)
private sealed class InventoryWrapper
{
public long Sum(int itemId) => items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
private readonly IEnumerable<InventoryItem> _items;
public int FreeInventorySlots => 140 - items.Count(x => x.Category == 1);
public InventoryWrapper(IEnumerable<InventoryItem> items)
{
_items = items;
}
public long Sum(int itemId) => _items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
}
}

View File

@ -1,4 +1,3 @@
using System;
using System.Reflection;
namespace Influx.AllaganTools;
@ -11,7 +10,6 @@ internal sealed class Character
public Character(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate;
_name = _delegate.GetType().GetField("Name")!;
_level = _delegate.GetType().GetField("Level")!;
@ -21,18 +19,13 @@ internal sealed class Character
ClassJob = (byte)_delegate.GetType().GetField("ClassJob")!.GetValue(_delegate)!;
OwnerId = (ulong)_delegate.GetType().GetField("OwnerId")!.GetValue(_delegate)!;
FreeCompanyId = (ulong)_delegate.GetType().GetField("FreeCompanyId")!.GetValue(_delegate)!;
WorldId = (uint)_delegate.GetType().GetField("WorldId")!.GetValue(_delegate)!;
}
public ulong CharacterId { get; }
public CharacterType CharacterType { get; }
public byte ClassJob { get; }
public ulong OwnerId { get; }
public ulong FreeCompanyId { get; set; }
public uint WorldId { get; }
public ulong FreeCompanyId { get; }
public string Name => (string)_name.GetValue(_delegate)!;
public uint Level => (uint)_level.GetValue(_delegate)!;
public override string ToString() =>
$"{nameof(Character)}[{CharacterId}, {(CharacterType == CharacterType.FreeCompanyChest ? "FC" : CharacterType)}, {Name}, {WorldId}]";
}

View File

@ -14,17 +14,15 @@ internal sealed class CharacterMonitor : ICharacterMonitor
public CharacterMonitor(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate;
_getPlayerCharacters =
_delegate.GetType().GetMethod("GetPlayerCharacters") ?? throw new MissingMethodException();
_allCharacters = _delegate.GetType().GetMethod("AllCharacters") ?? throw new MissingMethodException();
_getPlayerCharacters = _delegate.GetType().GetMethod("GetPlayerCharacters")!;
_allCharacters = _delegate.GetType().GetMethod("AllCharacters")!;
}
public IEnumerable<Character> PlayerCharacters => GetCharactersInternal(_getPlayerCharacters);
public IEnumerable<Character> All => GetCharactersInternal(_allCharacters);
private List<Character> GetCharactersInternal(MethodInfo methodInfo)
private IEnumerable<Character> GetCharactersInternal(MethodInfo methodInfo)
{
return ((IEnumerable)methodInfo.Invoke(_delegate, Array.Empty<object>())!)
.Cast<object>()

View File

@ -10,5 +10,4 @@ internal struct Currencies
public long Ventures { get; init; }
public long CeruleumTanks { get; init; }
public long RepairKits { get; init; }
public int FreeSlots { get; init; }
}

View File

@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Influx.AllaganTools;
internal sealed class Filter
{
private readonly object _delegate;
private readonly MethodInfo _generateFilteredList;
public Filter(object @delegate)
{
_delegate = @delegate;
_generateFilteredList = _delegate.GetType().GetMethod("GenerateFilteredList")!;
}
public IReadOnlyList<SortingResult> GenerateFilteredList()
{
Task task = (Task)_generateFilteredList.Invoke(_delegate, new object?[] { null })!;
object result = task.GetType().GetProperty("Result")!.GetValue(task)!;
return ((IEnumerable)result.GetType().GetProperty("SortedItems")!.GetValue(result)!)
.Cast<object>()
.Select(x => new SortingResult(x))
.ToList();
}
}

View File

@ -1,30 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace Influx.AllaganTools;
internal sealed class FilterResult
{
private readonly object _delegate;
private readonly PropertyInfo _sortedItems;
public FilterResult(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate;
_sortedItems =
_delegate.GetType().GetProperty("SortedItems") ?? throw new MissingMemberException();
}
public IReadOnlyList<SortingResult> GenerateFilteredList()
{
return ((IEnumerable)_sortedItems.GetValue(_delegate)!)
.Cast<object>()
.Select(x => new SortingResult(x))
.ToList();
}
}

View File

@ -0,0 +1,21 @@
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class FilterService : IFilterService
{
private readonly object _delegate;
private readonly MethodInfo _getFilterByKeyOrName;
public FilterService(object @delegate)
{
_delegate = @delegate;
_getFilterByKeyOrName = _delegate.GetType().GetMethod("GetFilterByKeyOrName")!;
}
public Filter? GetFilterByKeyOrName(string keyOrName)
{
var f = _getFilterByKeyOrName.Invoke(_delegate, new object?[] { keyOrName });
return f != null ? new Filter(f) : null;
}
}

View File

@ -0,0 +1,6 @@
namespace Influx.AllaganTools;
internal interface IFilterService
{
Filter? GetFilterByKeyOrName(string keyOrName);
}

View File

@ -1,6 +0,0 @@
namespace Influx.AllaganTools;
internal interface IListService
{
FilterResult? GetFilterByKeyOrName(string keyOrName);
}

View File

@ -13,9 +13,8 @@ internal sealed class Inventory
public Inventory(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate;
_getAllInventories = _delegate.GetType().GetMethod("GetAllInventories") ?? throw new MissingMethodException();
_getAllInventories = _delegate.GetType().GetMethod("GetAllInventories")!;
CharacterId = (ulong)_delegate.GetType().GetProperty("CharacterId")!.GetValue(_delegate)!;
}
@ -27,6 +26,5 @@ internal sealed class Inventory
.SelectMany(x => x.Cast<object?>())
.Where(x => x != null)
.Select(x => new InventoryItem(x!))
.Where(x => x.ItemId != 0)
.ToList();
}

View File

@ -1,21 +1,16 @@
using System;
using Dalamud.Logging;
namespace Influx.AllaganTools;
namespace Influx.AllaganTools;
internal sealed class InventoryItem
{
private readonly object _delegate;
public InventoryItem(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
Category = (int)@delegate.GetType().GetField("SortedCategory")!.GetValue(@delegate)!;
Container = (int)@delegate.GetType().GetField("SortedContainer")!.GetValue(@delegate)!;
ItemId = (uint)@delegate.GetType().GetProperty("ItemId")!.GetValue(@delegate)!;
Quantity = (uint)@delegate.GetType().GetField("Quantity")!.GetValue(@delegate)!;
_delegate = @delegate;
ItemId = (uint)_delegate.GetType().GetField("ItemId")!.GetValue(_delegate)!;
Quantity = (uint)_delegate.GetType().GetField("Quantity")!.GetValue(_delegate)!;
}
public int Category { get; }
public int Container { get; }
public uint ItemId { get; }
public uint Quantity { get; }
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -13,7 +12,6 @@ internal sealed class InventoryMonitor : IInventoryMonitor
public InventoryMonitor(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate;
_inventories = _delegate.GetType().GetProperty("Inventories")!;
}

View File

@ -1,28 +0,0 @@
using System;
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class ListService : IListService
{
private readonly object _listService;
private readonly object _listFilterService;
private readonly MethodInfo _getListByKeyOrName;
private readonly MethodInfo _refreshList;
public ListService(object listService, object listFilterService)
{
ArgumentNullException.ThrowIfNull(listService);
_listService = listService;
_listFilterService = listFilterService;
_getListByKeyOrName =
_listService.GetType().GetMethod("GetListByKeyOrName") ?? throw new MissingMethodException();
_refreshList = _listFilterService.GetType().GetMethod("RefreshList") ?? throw new MissingMethodException();
}
public FilterResult? GetFilterByKeyOrName(string keyOrName)
{
var f = _getListByKeyOrName.Invoke(_listService, [keyOrName]);
return f != null ? new FilterResult(_refreshList.Invoke(_listFilterService, [f])!) : null;
}
}

View File

@ -1,19 +1,15 @@
using System;
namespace Influx.AllaganTools;
namespace Influx.AllaganTools;
using ItemFlags = FFXIVClientStructs.FFXIV.Client.Game.InventoryItem.ItemFlags;
internal sealed class SortingResult
{
public SortingResult(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
LocalContentId = (ulong)@delegate.GetType().GetProperty("SourceRetainerId")!.GetValue(@delegate)!;
Quantity = (int)@delegate.GetType().GetProperty("Quantity")!.GetValue(@delegate)!;
var inventoryItem = @delegate.GetType().GetProperty("InventoryItem")!.GetValue(@delegate)!;
ItemId = (uint)inventoryItem.GetType().GetProperty("ItemId")!.GetValue(inventoryItem)!;
ItemId = (uint)inventoryItem.GetType().GetField("ItemId")!.GetValue(inventoryItem)!;
Flags = (ItemFlags)inventoryItem.GetType().GetField("Flags")!.GetValue(inventoryItem)!;
}
@ -22,7 +18,7 @@ internal sealed class SortingResult
public ItemFlags Flags { get; }
public int Quantity { get; }
public bool IsHq => Flags.HasFlag(ItemFlags.HighQuality);
public bool IsHq => Flags.HasFlag(ItemFlags.HQ);
public override string ToString()
{

View File

@ -1,26 +1,10 @@
using System;
using System.Collections.Generic;
using Dalamud.Plugin.Services;
namespace Influx.AllaganTools;
internal sealed class UnavailableCharacterMonitor(IPluginLog pluginLog) : ICharacterMonitor
internal sealed class UnavailableCharacterMonitor : ICharacterMonitor
{
public IEnumerable<Character> PlayerCharacters
{
get
{
pluginLog.Warning("Character monitor is unavailable");
return Array.Empty<Character>();
}
}
public IEnumerable<Character> All
{
get
{
pluginLog.Warning("Character monitor is unavailable");
return Array.Empty<Character>();
}
}
public IEnumerable<Character> PlayerCharacters => Array.Empty<Character>();
public IEnumerable<Character> All => Array.Empty<Character>();
}

View File

@ -0,0 +1,6 @@
namespace Influx.AllaganTools;
internal sealed class UnavailableFilterService : IFilterService
{
public Filter? GetFilterByKeyOrName(string keyOrName) => null;
}

View File

@ -1,16 +1,8 @@
using System.Collections.Generic;
using Dalamud.Plugin.Services;
namespace Influx.AllaganTools;
internal sealed class UnavailableInventoryMonitor(IPluginLog pluginLog) : IInventoryMonitor
internal sealed class UnavailableInventoryMonitor : IInventoryMonitor
{
public IReadOnlyDictionary<ulong, Inventory> All
{
get
{
pluginLog.Warning("Inventory monitor is unavailable");
return new Dictionary<ulong, Inventory>();
}
}
public IReadOnlyDictionary<ulong, Inventory> All => new Dictionary<ulong, Inventory>();
}

View File

@ -1,12 +0,0 @@
using Dalamud.Plugin.Services;
namespace Influx.AllaganTools;
internal sealed class UnavailableListService(IPluginLog pluginLog) : IListService
{
public FilterResult? GetFilterByKeyOrName(string keyOrName)
{
pluginLog.Warning("Filter Service is unavailable");
return null;
}
}

View File

@ -3,14 +3,14 @@ using Dalamud.Configuration;
namespace Influx;
internal sealed class Configuration : IPluginConfiguration
public sealed class Configuration : IPluginConfiguration
{
public int Version { get; set; } = 1;
public ServerConfiguration Server { get; set; } = new();
public IList<CharacterInfo> IncludedCharacters { get; set; } = new List<CharacterInfo>();
public IList<FilterInfo> IncludedInventoryFilters { get; set; } = new List<FilterInfo>();
public List<CharacterInfo> IncludedCharacters { get; set; } = new();
public List<FilterInfo> IncludedInventoryFilters { get; set; } = new();
public sealed class ServerConfiguration
{

20
Influx/GameStrings.cs Normal file
View File

@ -0,0 +1,20 @@
using System;
using Dalamud.Plugin.Services;
using LLib;
using Addon = Lumina.Excel.GeneratedSheets.Addon;
namespace Influx;
internal sealed class GameStrings
{
public GameStrings(IDataManager dataManager, IPluginLog pluginLog)
{
LogoutToTitleScreen = dataManager.GetString<Addon>(115, s => s.Text, pluginLog)
?? throw new Exception($"Unable to resolve {nameof(LogoutToTitleScreen)}");
LogoutAndExitGame = dataManager.GetString<Addon>(116, s => s.Text, pluginLog)
?? throw new Exception($"Unable to resolve {nameof(LogoutAndExitGame)}");
}
public string LogoutToTitleScreen { get; }
public string LogoutAndExitGame { get; }
}

View File

@ -1,15 +1,57 @@
<Project Sdk="Dalamud.NET.Sdk/10.0.0">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2</Version>
<TargetFramework>net7.0-windows</TargetFramework>
<Version>0.9</Version>
<LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>dist</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DebugType>portable</DebugType>
</PropertyGroup>
<Import Project="..\LLib\LLib.targets"/>
<Import Project="..\LLib\RenameZip.targets"/>
<PropertyGroup>
<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>
<PackageReference Include="InfluxDB.Client" Version="4.14.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0-preview.1.24081.5" />
<PackageReference Include="DalamudPackager" Version="2.1.12"/>
<PackageReference Include="InfluxDB.Client" Version="4.13.0"/>
</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="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>
<ItemGroup>
@ -17,4 +59,8 @@
<ProjectReference Include="..\ECommons\ECommons\ECommons.csproj" />
<ProjectReference Include="..\LLib\LLib.csproj" />
</ItemGroup>
<Target Name="RenameLatestZip" AfterTargets="PackagePlugin">
<Exec Command="rename $(OutDir)$(AssemblyName)\latest.zip $(AssemblyName)-$(Version).zip"/>
</Target>
</Project>

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
@ -9,6 +7,7 @@ using Influx.AllaganTools;
using Influx.LocalStatistics;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Core;
using InfluxDB.Client.Writes;
using Lumina.Excel.GeneratedSheets;
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
@ -24,9 +23,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
private readonly IPluginLog _pluginLog;
private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex;
private readonly IReadOnlyDictionary<byte, string> _classJobNames;
private readonly IReadOnlyDictionary<sbyte, ClassJobDetail> _expToJobs;
private readonly ReadOnlyDictionary<uint, PriceInfo> _prices;
private readonly ReadOnlyDictionary<uint, string> _worldNames;
private readonly IReadOnlyDictionary<sbyte, string> _expToJobs;
private readonly IReadOnlyDictionary<uint, PriceInfo> _prices;
public InfluxStatisticsClient(IChatGui chatGui, Configuration configuration, IDataManager dataManager,
IClientState clientState, IPluginLog pluginLog)
@ -41,23 +39,17 @@ internal sealed class InfluxStatisticsClient : IDisposable
.ToDictionary(x => (byte)x.RowId, x => (byte)x.ExpArrayIndex);
_classJobNames = dataManager.GetExcelSheet<ClassJob>()!.Where(x => x.RowId > 0)
.ToDictionary(x => (byte)x.RowId, x => x.Abbreviation.ToString());
_expToJobs = dataManager.GetExcelSheet<ClassJob>()!.Where(x => x.RowId > 0 && !string.IsNullOrEmpty(x.Name))
.Where(x => x.JobIndex > 0 || x.DohDolJobIndex >= 0)
_expToJobs = dataManager.GetExcelSheet<ClassJob>()!.Where(x => x.RowId > 0)
.Where(x => x.JobIndex > 0)
.Where(x => x.Abbreviation.ToString() != "SMN")
.ToDictionary(x => x.ExpArrayIndex, x => new ClassJobDetail(x.Abbreviation.ToString(), x.DohDolJobIndex >= 0));
.ToDictionary(x => x.ExpArrayIndex, x => x.Abbreviation.ToString());
_prices = dataManager.GetExcelSheet<Item>()!
.AsEnumerable()
.ToDictionary(x => x.RowId, x => new PriceInfo
{
Name = x.Name.ToString(),
Normal = x.PriceLow,
UiCategory = x.ItemUICategory.Row,
})
.AsReadOnly();
_worldNames = dataManager.GetExcelSheet<World>()!
.Where(x => x.RowId > 0 && x.IsPublic)
.ToDictionary(x => x.RowId, x => x.Name.ToString())
.AsReadOnly();
});
}
public bool Enabled => _configuration.Server.Enabled &&
@ -81,7 +73,6 @@ internal sealed class InfluxStatisticsClient : IDisposable
return;
DateTime date = DateTime.UtcNow;
date = new DateTime(date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second, DateTimeKind.Utc);
IReadOnlyDictionary<Character, Currencies> currencyStats = update.Currencies;
var validFcIds = currencyStats.Keys
@ -122,8 +113,7 @@ internal sealed class InfluxStatisticsClient : IDisposable
foreach (var sub in subs)
{
values.Add(PointData.Measurement("submersibles")
.Tag("id", fc.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("world", _worldNames[fc.WorldId])
.Tag("id", fc.CharacterId.ToString())
.Tag("fc_name", fc.Name)
.Tag("sub_id", $"{fc.CharacterId}_{sub.Id}")
.Tag("sub_name", sub.Name)
@ -132,11 +122,9 @@ internal sealed class InfluxStatisticsClient : IDisposable
.Tag("part_bow", sub.Bow)
.Tag("part_bridge", sub.Bridge)
.Tag("build", sub.Build)
.Field("enabled", sub.Enabled ? 1 : 0)
.Field("level", sub.Level)
.Field("predicted_level", sub.PredictedLevel)
.Field("state", (int)sub.State)
.Field("return_time", new DateTimeOffset(sub.ReturnTime).ToUnixTimeSeconds())
.Timestamp(date, WritePrecision.S));
}
}
@ -145,10 +133,9 @@ internal sealed class InfluxStatisticsClient : IDisposable
var writeApi = client.GetWriteApiAsync();
await writeApi.WritePointsAsync(
values,
_configuration.Server.Bucket, _configuration.Server.Organization)
.ConfigureAwait(false);
_configuration.Server.Bucket, _configuration.Server.Organization);
_pluginLog.Verbose($"Influx: Sent {values.Count} data points to server");
//_chatGui.Print($"Influx: {values.Count} points");
}
catch (Exception e)
{
@ -168,24 +155,26 @@ internal sealed class InfluxStatisticsClient : IDisposable
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("world", _worldNames[character.WorldId])
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString(CultureInfo.InvariantCulture) : null)
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Timestamp(date, WritePrecision.S);
yield return pointData("currency")
.Field("gil", localStats?.Gil ?? 0)
.Field("gil", localStats?.Gil ?? currencies.Gil)
.Field("mgp", localStats?.MGP ?? 0)
.Field("ventures", currencies.Ventures)
.Field("ceruleum_tanks", currencies.CeruleumTanks)
.Field("repair_kits", currencies.RepairKits)
.Field("free_inventory", currencies.FreeSlots);
.Field("repair_kits", currencies.RepairKits);
if (localStats != null)
{
yield return pointData("grandcompany")
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
.Field("gc", localStats.GrandCompany)
.Field("gc_rank", localStats.GcRank)
.Field("seals", (GrandCompany)localStats.GrandCompany switch
@ -214,18 +203,13 @@ internal sealed class InfluxStatisticsClient : IDisposable
if (localStats.ClassJobLevels.Count > 0)
{
foreach (var (expIndex, job) in _expToJobs)
foreach (var (expIndex, abbreviation) in _expToJobs)
{
// last update to this char was in 6.x, so we don't have PCT/VPR data
if (localStats.ClassJobLevels.Count <= expIndex)
continue;
var level = localStats.ClassJobLevels[expIndex];
if (level > 0)
{
yield return pointData("experience")
.Tag("job", job.Abbreviation)
.Tag("job_type", job.Type)
.Tag("job", abbreviation)
.Field("level", level);
}
}
@ -250,10 +234,9 @@ internal sealed class InfluxStatisticsClient : IDisposable
var owner = update.Currencies.Keys.First(x => x.CharacterId == character.OwnerId);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("id", character.CharacterId.ToString())
.Tag("player_name", owner.Name)
.Tag("player_id", character.OwnerId.ToString(CultureInfo.InvariantCulture))
.Tag("world", _worldNames[character.WorldId])
.Tag("player_id", character.OwnerId.ToString())
.Tag("type", character.CharacterType.ToString())
.Tag("retainer_name", character.Name)
.Timestamp(date, WritePrecision.S);
@ -296,14 +279,15 @@ internal sealed class InfluxStatisticsClient : IDisposable
.GroupBy(x => new { x.ItemId, x.IsHq }))
{
_prices.TryGetValue(item.Key.ItemId, out PriceInfo priceInfo);
_pluginLog.Information($"CH {localContentId} → {priceInfo.Name} x {item.Sum(x => x.Quantity)}");
bool priceHq = item.Key.IsHq || priceInfo.UiCategory == 58; // materia always uses HQ prices
yield return pointData("items")
.Tag("filter_name", filterName)
.Tag("item_id", item.Key.ItemId.ToString(CultureInfo.InvariantCulture))
.Tag("item_id", item.Key.ItemId.ToString())
.Tag("item_name", priceInfo.Name)
.Tag("hq", (item.Key.IsHq ? 1 : 0).ToString(CultureInfo.InvariantCulture))
.Tag("hq", (item.Key.IsHq ? 1 : 0).ToString())
.Field("quantity", item.Sum(x => x.Quantity))
.Field("total_gil", item.Sum(x => x.Quantity) * (priceHq ? priceInfo.Hq : priceInfo.Normal));
}
@ -316,9 +300,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("id", character.CharacterId.ToString())
.Tag("fc_name", character.Name)
.Tag("world", _worldNames[character.WorldId])
.Tag("type", character.CharacterType.ToString())
.Timestamp(date, WritePrecision.S);
@ -344,9 +327,4 @@ internal sealed class InfluxStatisticsClient : IDisposable
public uint Hq => Normal + (uint)Math.Ceiling((decimal)Normal / 10);
public uint UiCategory { get; set; }
}
private sealed record ClassJobDetail(string Abbreviation, bool IsNonCombat)
{
public string Type => IsNonCombat ? "doh_dol" : "combat";
}
}

View File

@ -2,31 +2,38 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Timers;
using Dalamud.Game.ClientState.Conditions;
using System.Runtime.InteropServices;
using System.Threading;
using AutoRetainerAPI;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Game.Command;
using Dalamud.Interface.Windowing;
using Dalamud.Memory;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using ECommons;
using ECommons.Automation;
using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Influx.AllaganTools;
using Influx.Influx;
using Influx.LocalStatistics;
using Influx.SubmarineTracker;
using Influx.Windows;
using LLib;
using Task = System.Threading.Tasks.Task;
namespace Influx;
[SuppressMessage("ReSharper", "UnusedType.Global")]
[SuppressMessage("Performance", "CA1812")]
internal sealed class InfluxPlugin : IDalamudPlugin
public class InfluxPlugin : IDalamudPlugin
{
private readonly object _lock = new();
private readonly IDalamudPluginInterface _pluginInterface;
private readonly DalamudPluginInterface _pluginInterface;
private readonly Configuration _configuration;
private readonly IClientState _clientState;
private readonly ICommandManager _commandManager;
private readonly ICondition _condition;
private readonly IPluginLog _pluginLog;
private readonly AllaganToolsIpc _allaganToolsIpc;
private readonly SubmarineTrackerIpc _submarineTrackerIpc;
@ -38,15 +45,14 @@ internal sealed class InfluxPlugin : IDalamudPlugin
private readonly ConfigurationWindow _configurationWindow;
private readonly Timer _timer;
public InfluxPlugin(IDalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog,
public InfluxPlugin(DalamudPluginInterface pluginInterface, IClientState clientState, IPluginLog pluginLog,
ICommandManager commandManager, IChatGui chatGui, IDataManager dataManager, IFramework framework,
IAddonLifecycle addonLifecycle, IGameGui gameGui, ICondition condition)
IAddonLifecycle addonLifecycle, IGameGui gameGui)
{
_pluginInterface = pluginInterface;
_configuration = LoadConfig();
_clientState = clientState;
_commandManager = commandManager;
_condition = condition;
_pluginLog = pluginLog;
DalamudReflector dalamudReflector = new DalamudReflector(pluginInterface, framework, pluginLog);
_allaganToolsIpc = new AllaganToolsIpc(pluginInterface, chatGui, dalamudReflector, framework, _pluginLog);
@ -66,15 +72,9 @@ internal sealed class InfluxPlugin : IDalamudPlugin
_windowSystem.AddWindow(_configurationWindow);
_commandManager.AddHandler("/influx", new CommandInfo(ProcessCommand));
_timer = new Timer(TimeSpan.FromMinutes(1));
_timer.Elapsed += (_, _) => UpdateStatistics();
_timer.AutoReset = true;
_timer.Enabled = true;
_timer = new Timer(_ => UpdateStatistics(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
_pluginInterface.UiBuilder.Draw += _windowSystem.Draw;
_pluginInterface.UiBuilder.OpenConfigUi += _configurationWindow.Toggle;
_condition.ConditionChange += UpdateOnLogout;
}
private Configuration LoadConfig()
@ -100,123 +100,64 @@ internal sealed class InfluxPlugin : IDalamudPlugin
private void UpdateStatistics()
{
lock (_lock)
if (!_clientState.IsLoggedIn ||
_configuration.IncludedCharacters.All(x => x.LocalContentId != _clientState.LocalContentId))
return;
try
{
if (!_clientState.IsLoggedIn ||
_configuration.IncludedCharacters.All(x => x.LocalContentId != _clientState.LocalContentId))
{
_pluginLog.Verbose("Influx: not logged in or not enabled for this character");
var currencies = _allaganToolsIpc.CountCurrencies();
var characters = currencies.Keys.ToList();
if (characters.Count == 0)
return;
}
try
{
var currencies = _allaganToolsIpc.CountCurrencies();
var characters = currencies.Keys.ToList();
if (characters.Count == 0)
{
_pluginLog.Warning("Found 0 AllaganTools characters");
return;
}
foreach (Character character in characters)
{
if (character.CharacterType == CharacterType.Character && character.FreeCompanyId != default)
Dictionary<string, IReadOnlyList<SortingResult>> inventoryItems =
_configuration.IncludedInventoryFilters.Select(c => c.Name)
.Distinct()
.ToDictionary(c => c, c =>
{
bool isFcEnabled = _configuration.IncludedCharacters
.FirstOrDefault(x => x.LocalContentId == character.CharacterId)?.IncludeFreeCompany ?? true;
if (!isFcEnabled)
character.FreeCompanyId = default;
}
}
var filter = _allaganToolsIpc.GetFilter(c);
if (filter == null)
return new List<SortingResult>();
Dictionary<string, IReadOnlyList<SortingResult>> inventoryItems =
_configuration.IncludedInventoryFilters.Select(c => c.Name)
.Distinct()
.ToDictionary(c => c, c =>
{
var filter = _allaganToolsIpc.GetFilter(c);
if (filter == null)
return new List<SortingResult>();
return filter.GenerateFilteredList();
});
return filter.GenerateFilteredList();
});
var update = new StatisticsUpdate
{
Currencies = currencies
.Where(x => _configuration.IncludedCharacters.Any(y =>
y.LocalContentId == x.Key.CharacterId ||
y.LocalContentId == x.Key.OwnerId ||
characters.Any(z =>
y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
.ToDictionary(x => x.Key, x => x.Value),
InventoryItems = inventoryItems,
Submarines = UpdateEnabledSubs(_submarineTrackerIpc.GetSubmarineStats(characters), characters),
LocalStats = _localStatsCalculator.GetAllCharacterStats()
.Where(x => characters.Any(y => y.CharacterId == x.Key))
.ToDictionary(x => characters.First(y => y.CharacterId == x.Key), x => x.Value)
.Where(x => _configuration.IncludedCharacters.Any(y =>
y.LocalContentId == x.Key.CharacterId ||
y.LocalContentId == x.Key.OwnerId ||
characters.Any(z =>
y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
.ToDictionary(x => x.Key, x => x.Value),
FcStats = _fcStatsCalculator.GetAllFcStats()
.Where(x => characters.Any(y => y.FreeCompanyId == x.Key))
.ToDictionary(x => x.Key, x => x.Value),
};
_statisticsWindow.OnStatisticsUpdate(update);
_influxStatisticsClient.OnStatisticsUpdate(update);
}
catch (Exception e)
var update = new StatisticsUpdate
{
_pluginLog.Error(e, "failed to update statistics");
}
Currencies = currencies
.Where(x => _configuration.IncludedCharacters.Any(y =>
y.LocalContentId == x.Key.CharacterId ||
y.LocalContentId == x.Key.OwnerId ||
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
.ToDictionary(x => x.Key, x => x.Value),
InventoryItems = inventoryItems,
Submarines = _submarineTrackerIpc.GetSubmarineStats(characters),
LocalStats = _localStatsCalculator.GetAllCharacterStats()
.Where(x => characters.Any(y => y.CharacterId == x.Key))
.ToDictionary(x => characters.First(y => y.CharacterId == x.Key), x => x.Value)
.Where(x => _configuration.IncludedCharacters.Any(y =>
y.LocalContentId == x.Key.CharacterId ||
y.LocalContentId == x.Key.OwnerId ||
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
.ToDictionary(x => x.Key, x => x.Value),
FcStats = _fcStatsCalculator.GetAllFcStats()
.Where(x => characters.Any(y => y.FreeCompanyId == x.Key))
.ToDictionary(x => x.Key, x => x.Value),
};
_statisticsWindow.OnStatisticsUpdate(update);
_influxStatisticsClient.OnStatisticsUpdate(update);
}
}
private IReadOnlyDictionary<Character, List<SubmarineStats>> UpdateEnabledSubs(
IReadOnlyDictionary<Character, List<SubmarineStats>> allSubs, List<Character> characters)
{
foreach (var (character, subs) in allSubs)
catch (Exception e)
{
var owner = characters.FirstOrDefault(x => x.FreeCompanyId == character.CharacterId);
if (owner == null)
continue;
var enabledSubs = _fcStatsCalculator.GetEnabledSubs(owner.CharacterId);
foreach (var sub in subs)
sub.Enabled = enabledSubs.Contains(sub.Name);
}
return allSubs;
}
private void UpdateOnLogout(ConditionFlag flag, bool value)
{
if (flag == ConditionFlag.LoggingOut && value)
{
try
{
_timer.Enabled = false;
_localStatsCalculator.UpdateStatisticsLogout();
UpdateStatistics();
}
finally
{
_timer.Enabled = true;
}
_pluginLog.Error(e, "failed to update statistics");
}
}
public void Dispose()
{
_condition.ConditionChange -= UpdateOnLogout;
_pluginInterface.UiBuilder.OpenConfigUi -= _configurationWindow.Toggle;
_pluginInterface.UiBuilder.Draw -= _windowSystem.Draw;
_timer.Stop();
_timer.Dispose();
_windowSystem.RemoveAllWindows();
_commandManager.RemoveHandler("/influx");

View File

@ -13,12 +13,13 @@ using FFXIVClientStructs.FFXIV.Client.System.Framework;
using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Newtonsoft.Json;
using Task = System.Threading.Tasks.Task;
namespace Influx.LocalStatistics;
internal sealed class FcStatsCalculator : IDisposable
public class FcStatsCalculator : IDisposable
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly IAddonLifecycle _addonLifecycle;
private readonly IGameGui _gameGui;
@ -29,11 +30,11 @@ internal sealed class FcStatsCalculator : IDisposable
private readonly Dictionary<ulong, FcStats> _cache = new();
private Status? _status;
private bool closeFcWindow = false;
public FcStatsCalculator(
IDalamudPlugin plugin,
IDalamudPluginInterface pluginInterface,
DalamudPluginInterface pluginInterface,
IClientState clientState,
IAddonLifecycle addonLifecycle,
IGameGui gameGui,
@ -53,9 +54,7 @@ internal sealed class FcStatsCalculator : IDisposable
_autoRetainerApi = new();
_autoRetainerApi.OnCharacterPostprocessStep += CheckCharacterPostProcess;
_autoRetainerApi.OnCharacterReadyToPostProcess += DoCharacterPostProcess;
_addonLifecycle.RegisterListener(AddonEvent.PostReceiveEvent, "FreeCompany", FcPostReceiveEvent);
_framework.Update += FrameworkUpdate;
_clientState.Logout += Logout;
_addonLifecycle.RegisterListener(AddonEvent.PostReceiveEvent, "FreeCompany", CloseFcWindow);
foreach (var file in _pluginInterface.ConfigDirectory.GetFiles("f.*.json"))
{
@ -86,9 +85,9 @@ internal sealed class FcStatsCalculator : IDisposable
if (infoProxy != null)
{
var fcProxy = (InfoProxyFreeCompany*)infoProxy;
if (fcProxy->Id != 0)
if (fcProxy->ID != 0)
{
_pluginLog.Information($"Requesting post-process, FC is {fcProxy->Id}");
_pluginLog.Information($"Requesting post-process, FC is {fcProxy->ID}");
_autoRetainerApi.RequestCharacterPostprocess();
}
else
@ -100,51 +99,45 @@ internal sealed class FcStatsCalculator : IDisposable
private void DoCharacterPostProcess()
{
_status = new();
closeFcWindow = true;
unsafe
{
AtkUnitBase* addon = (AtkUnitBase*)_gameGui.GetAddonByName("FreeCompany");
if (addon != null && addon->IsVisible)
FcPostReceiveEvent(AddonEvent.PostReceiveEvent);
CloseFcWindow(AddonEvent.PostReceiveEvent);
else
Chat.Instance.SendMessage("/freecompanycmd");
}
}
private void FcPostReceiveEvent(AddonEvent type, AddonArgs? args = null)
private void CloseFcWindow(AddonEvent type, AddonArgs? args = null)
{
if (_status != null)
{
_pluginLog.Verbose("FC window received event...");
_status.WindowOpened = true;
}
else
_pluginLog.Verbose("Not tracking status for FC window");
_framework.RunOnTick(() => UpdateOnTick(0), TimeSpan.FromMilliseconds(100));
}
private void FrameworkUpdate(IFramework framework)
private void UpdateOnTick(int counter)
{
if (_status == null)
bool finalAttempt = ++counter >= 10;
if (UpdateFcCredits() || finalAttempt)
{
if (closeFcWindow)
{
unsafe
{
AtkUnitBase* addon = (AtkUnitBase*)_gameGui.GetAddonByName("FreeCompany");
if (addon != null && addon->IsVisible)
addon->FireCallbackInt(-1);
}
closeFcWindow = false;
_autoRetainerApi.FinishCharacterPostProcess();
}
return;
if (_status.FallbackFinishPostProcessing < DateTime.Now)
{
_status = null;
_autoRetainerApi.FinishCharacterPostProcess();
}
else if (_status.WindowOpened && UpdateFcCredits())
{
_status = null;
_autoRetainerApi.FinishCharacterPostProcess();
}
}
private void Logout()
{
if (_status != null)
_autoRetainerApi.FinishCharacterPostProcess();
_status = null;
_framework.RunOnTick(() => UpdateOnTick(counter + 1), TimeSpan.FromMilliseconds(100));
}
// ideally we'd hook the update to the number array, but #effort
@ -157,14 +150,14 @@ internal sealed class FcStatsCalculator : IDisposable
if (infoProxy != null)
{
var fcProxy = (InfoProxyFreeCompany*)infoProxy;
ulong localContentId = fcProxy->Id;
ulong localContentId = fcProxy->ID;
if (localContentId != 0)
{
var atkArrays = Framework.Instance()->GetUIModule()->GetRaptureAtkModule()->AtkModule
var atkArrays = Framework.Instance()->GetUiModule()->GetRaptureAtkModule()->AtkModule
.AtkArrayDataHolder;
if (atkArrays.NumberArrayCount > 51)
if (atkArrays.NumberArrayCount > 50)
{
var fcArrayData = atkArrays.GetNumberArrayData(51);
var fcArrayData = atkArrays.GetNumberArrayData(50);
FcStats fcStats = new FcStats
{
ContentId = localContentId,
@ -215,29 +208,12 @@ internal sealed class FcStatsCalculator : IDisposable
public IReadOnlyDictionary<ulong, FcStats> GetAllFcStats() => _cache.AsReadOnly();
public HashSet<string> GetEnabledSubs(ulong characterId)
{
var offlineCharacterData = _autoRetainerApi.GetOfflineCharacterData(characterId);
if (offlineCharacterData == null || !offlineCharacterData.WorkshopEnabled)
return [];
return offlineCharacterData.EnabledSubs;
}
public void Dispose()
{
_clientState.Logout -= Logout;
_framework.Update -= FrameworkUpdate;
_addonLifecycle.UnregisterListener(AddonEvent.PostReceiveEvent, "FreeCompany", FcPostReceiveEvent);
_addonLifecycle.UnregisterListener(AddonEvent.PostReceiveEvent, "FreeCompany", CloseFcWindow);
_autoRetainerApi.OnCharacterPostprocessStep -= CheckCharacterPostProcess;
_autoRetainerApi.OnCharacterReadyToPostProcess -= DoCharacterPostProcess;
_autoRetainerApi.Dispose();
ECommonsMain.Dispose();
}
private sealed class Status
{
public bool WindowOpened { get; set; }
public DateTime FallbackFinishPostProcessing { get; set; } = DateTime.Now.AddSeconds(10);
}
}

View File

@ -9,11 +9,11 @@ public sealed record LocalStats
public byte GcRank { get; init; }
public bool SquadronUnlocked { get; init; }
public byte MaxLevel { get; init; } = 90;
public IList<short> ClassJobLevels { get; init; } = new List<short>();
public List<short> ClassJobLevels { get; set; } = new();
public byte StartingTown { get; init; }
public int MsqCount { get; set; } = -1;
public string? MsqName { get; set; }
public uint MsqGenre { get; set; }
public int Gil { get; set; }
public int? Gil { get; set; }
public int MGP { get; set; }
}

View File

@ -1,15 +1,17 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Memory;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Client.UI;
using Lumina.Excel.GeneratedSheets;
using Newtonsoft.Json;
@ -27,10 +29,11 @@ internal sealed class LocalStatsCalculator : IDisposable
private const uint JointQuest = 65781;
private readonly IDalamudPluginInterface _pluginInterface;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly IAddonLifecycle _addonLifecycle;
private readonly IPluginLog _pluginLog;
private readonly GameStrings _gameStrings;
private readonly Dictionary<ulong, LocalStats> _cache = new();
private IReadOnlyList<QuestInfo>? _gridaniaStart;
@ -40,7 +43,7 @@ internal sealed class LocalStatsCalculator : IDisposable
public LocalStatsCalculator(
IDalamudPluginInterface pluginInterface,
DalamudPluginInterface pluginInterface,
IClientState clientState,
IAddonLifecycle addonLifecycle,
IPluginLog pluginLog,
@ -50,17 +53,21 @@ internal sealed class LocalStatsCalculator : IDisposable
_clientState = clientState;
_addonLifecycle = addonLifecycle;
_pluginLog = pluginLog;
_gameStrings = new GameStrings(dataManager, pluginLog);
_clientState.Login += UpdateStatistics;
_clientState.TerritoryChanged += UpdateStatistics;
_addonLifecycle.RegisterListener(AddonEvent.PostSetup, "SelectYesno", UpdateStatisticsLogout);
_addonLifecycle.RegisterListener(AddonEvent.PreSetup, "JournalAccept", UpdateStatistics);
Task.Run(() =>
{
List<QuestInfo> msq = new();
foreach (var quest in dataManager.GetExcelSheet<Quest>()!.Where(x => x.JournalGenre.Row is >= 1 and <= 13))
foreach (var quest in dataManager.GetExcelSheet<Quest>()!.Where(x => x.JournalGenre.Row is >= 1 and <= 12))
{
var previousQuests = quest.PreviousQuest?.Select(x => x.Row).Where(x => x != 0).ToList();
if (previousQuests != null && quest.Unknown12 != 0)
previousQuests.Add(quest.Unknown12);
msq.Add(new QuestInfo
{
@ -110,7 +117,7 @@ internal sealed class LocalStatsCalculator : IDisposable
UpdateStatistics();
}
private static ReadOnlyCollection<QuestInfo> PopulateStartingCities(List<QuestInfo> quests, uint envoyQuestId,
private IReadOnlyList<QuestInfo> PopulateStartingCities(List<QuestInfo> quests, uint envoyQuestId,
uint startingQuestId, bool popCallOfTheSea)
{
QuestInfo callOfTheSea = quests.First(x => x.PreviousQuestIds.Contains(envoyQuestId));
@ -118,10 +125,12 @@ internal sealed class LocalStatsCalculator : IDisposable
quests.Remove(callOfTheSea);
List<QuestInfo> startingCityQuests = new List<QuestInfo> { callOfTheSea };
uint questId = envoyQuestId;
uint? questId = envoyQuestId;
QuestInfo? quest;
do
{
QuestInfo quest = quests.First(x => x.RowId == questId);
quest = quests.First(x => x.RowId == questId);
quests.Remove(quest);
if (quest.Name == "Close to Home")
@ -130,14 +139,14 @@ internal sealed class LocalStatsCalculator : IDisposable
{
RowId = startingQuestId,
Name = "Coming to ...",
PreviousQuestIds = new List<uint>(),
PreviousQuestIds = new(),
Genre = quest.Genre,
};
}
startingCityQuests.Add(quest);
questId = quest.PreviousQuestIds.FirstOrDefault();
} while (questId != 0);
} while (questId != null && questId != 0);
return Enumerable.Reverse(startingCityQuests).ToList().AsReadOnly();
}
@ -145,12 +154,20 @@ internal sealed class LocalStatsCalculator : IDisposable
public void Dispose()
{
_addonLifecycle.UnregisterListener(AddonEvent.PreSetup, "JournalAccept", UpdateStatistics);
_addonLifecycle.UnregisterListener(AddonEvent.PostSetup, "SelectYesno", UpdateStatisticsLogout);
_clientState.Login -= UpdateStatistics;
}
private void UpdateStatistics(ushort territoryType) => UpdateStatistics();
public void UpdateStatisticsLogout() => UpdateStatistics();
private unsafe void UpdateStatisticsLogout(AddonEvent type, AddonArgs args)
{
AddonSelectYesno* addonSelectYesNo = (AddonSelectYesno*)args.Addon;
string? text = MemoryHelper.ReadSeString(&addonSelectYesNo->PromptText->NodeText)?.ToString();
text = text?.Replace("\n", "").Replace("\r", "");
if (text == _gameStrings.LogoutToTitleScreen || text == _gameStrings.LogoutAndExitGame)
UpdateStatistics();
}
private void UpdateStatistics(AddonEvent type, AddonArgs args) => UpdateStatistics();
@ -246,8 +263,8 @@ internal sealed class LocalStatsCalculator : IDisposable
private unsafe List<short> ExtractClassJobLevels(PlayerState* playerState)
{
List<short> levels = new();
for (int i = 0; i < 32; ++i)
levels.Add(playerState->ClassJobLevels[i]);
for (int i = 0; i < 30; ++i)
levels.Add(playerState->ClassJobLevelArray[i]);
return levels;
}

View File

@ -2,10 +2,10 @@
namespace Influx.LocalStatistics;
public sealed class QuestInfo
public class QuestInfo
{
public required uint RowId { get; init; }
public required string Name { get; init; }
public required IList<uint> PreviousQuestIds { get; init; }
public required List<uint> PreviousQuestIds { get; init; }
public required uint Genre { get; init; }
}

View File

@ -9,7 +9,7 @@ internal sealed class StatisticsUpdate
{
public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; }
public required IReadOnlyDictionary<string, IReadOnlyList<SortingResult>> InventoryItems { get; init; }
public required IReadOnlyDictionary<Character, List<SubmarineStats>> Submarines { get; init; }
public required IReadOnlyDictionary<Character, LocalStats> LocalStats { get; init; }
public required IReadOnlyDictionary<ulong, FcStats> FcStats { get; init; }
public required Dictionary<Character, List<SubmarineStats>> Submarines { get; init; }
public required Dictionary<Character, LocalStats> LocalStats { get; init; }
public required Dictionary<ulong, FcStats> FcStats { get; init; }
}

View File

@ -2,22 +2,21 @@
namespace Influx.SubmarineTracker;
internal sealed class Build
public class Build
{
public Build(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
Type type = @delegate.GetType();
HullIdentifier =
(string)type.GetProperty("HullIdentifier")!.GetValue(@delegate)!;
(string)@delegate.GetType().GetProperty("HullIdentifier")!.GetValue(@delegate)!;
SternIdentifier =
(string)type.GetProperty("SternIdentifier")!.GetValue(@delegate)!;
(string)@delegate.GetType().GetProperty("SternIdentifier")!.GetValue(@delegate)!;
BowIdentifier =
(string)type.GetProperty("BowIdentifier")!.GetValue(@delegate)!;
(string)@delegate.GetType().GetProperty("BowIdentifier")!.GetValue(@delegate)!;
BridgeIdentifier =
(string)type.GetProperty("BridgeIdentifier")!.GetValue(@delegate)!;
(string)@delegate.GetType().GetProperty("BridgeIdentifier")!.GetValue(@delegate)!;
FullIdentifier =
(string)type.GetMethod("FullIdentifier")!.Invoke(@delegate, Array.Empty<object>())!;
(string)@delegate.GetType().GetMethod("FullIdentifier")!.Invoke(@delegate, Array.Empty<object>())!;
}
public string HullIdentifier { get; }

View File

@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Influx.SubmarineTracker;
public sealed class FcSubmarines
{
private readonly object _delegate;
public FcSubmarines(object @delegate)
{
_delegate = @delegate;
Submarines = ((IEnumerable)_delegate.GetType().GetField("Submarines")!.GetValue(_delegate)!)
.Cast<object>()
.Select(x => new Submarine(x))
.ToList();
}
public List<Submarine> Submarines { get; }
}

View File

@ -2,34 +2,25 @@
namespace Influx.SubmarineTracker;
internal sealed class Submarine
public sealed class Submarine
{
public Submarine(object @delegate)
{
ArgumentNullException.ThrowIfNull(@delegate);
Type type = @delegate.GetType();
FreeCompanyId = (ulong)type.GetField("FreeCompanyId")!.GetValue(@delegate)!;
Name = (string)type.GetField("Name")!.GetValue(@delegate)!;
Level = (ushort)type.GetField("Rank")!.GetValue(@delegate)!;
Build = new Build(type.GetProperty("Build")!.GetValue(@delegate)!);
ReturnTime = (DateTime)type.GetField("ReturnTime")!.GetValue(@delegate)!;
Name = (string)@delegate.GetType().GetProperty("Name")!.GetValue(@delegate)!;
Level = (ushort)@delegate.GetType().GetProperty("Rank")!.GetValue(@delegate)!;
Build = new Build(@delegate.GetType().GetProperty("Build")!.GetValue(@delegate)!);
try
{
bool onVoyage = (bool)type.GetMethod("IsOnVoyage")!.Invoke(@delegate, Array.Empty<object>())!;
bool returned = (bool)type.GetMethod("IsDone")!.Invoke(@delegate, Array.Empty<object>())!;
(uint predictedLevel, double _) = ((uint, double))@delegate.GetType().GetMethod("PredictExpGrowth")!.Invoke(@delegate, Array.Empty<object?>())!;
PredictedLevel = (ushort)predictedLevel;
bool onVoyage = (bool)@delegate.GetType().GetMethod("IsOnVoyage")!.Invoke(@delegate, Array.Empty<object>())!;
bool returned = (bool)@delegate.GetType().GetMethod("IsDone")!.Invoke(@delegate, Array.Empty<object>())!;
if (onVoyage)
State = returned ? EState.Returned : EState.Voyage;
else
State = EState.NoVoyage;
if (State == EState.NoVoyage)
PredictedLevel = Level;
else
{
(uint predictedLevel, double _) = ((uint, double))type.GetMethod("PredictExpGrowth")!.Invoke(@delegate, Array.Empty<object?>())!;
PredictedLevel = (ushort)predictedLevel;
}
}
catch (Exception)
{
@ -37,11 +28,9 @@ internal sealed class Submarine
}
}
public ulong FreeCompanyId { get; }
public string Name { get; }
public ushort Level { get; }
public ushort PredictedLevel { get; }
public Build Build { get; }
public DateTime ReturnTime { get; }
public EState State { get; }
}

View File

@ -1,13 +1,9 @@
using System;
namespace Influx.SubmarineTracker;
namespace Influx.SubmarineTracker;
internal sealed class SubmarineStats
public sealed class SubmarineStats
{
public required string Name { get; init; }
public required int Id { get; init; }
public required uint WorldId { get; init; }
public bool Enabled { get; set; } = true;
public required ushort Level { get; init; }
public required ushort PredictedLevel { get; init; }
@ -17,5 +13,4 @@ internal sealed class SubmarineStats
public required string Bridge { get; init; }
public required string Build { get; init; }
public required EState State { get; init; }
public required DateTime ReturnTime { get; init; }
}

View File

@ -1,10 +1,6 @@
using System;
using System.Collections;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Dalamud.Plugin;
using Influx.AllaganTools;
using LLib;
@ -19,56 +15,47 @@ internal sealed class SubmarineTrackerIpc
_dalamudReflector = dalamudReflector;
}
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public IReadOnlyDictionary<Character, List<SubmarineStats>> GetSubmarineStats(List<Character> characters)
public Dictionary<Character, List<SubmarineStats>> GetSubmarineStats(List<Character> characters)
{
if (_dalamudReflector.TryGetDalamudPlugin("Submarine Tracker", out IDalamudPlugin? it, false, true))
if (_dalamudReflector.TryGetDalamudPlugin("Submarine Tracker", out var it, false, true))
{
var databaseCache = it.GetType()
.GetField("DatabaseCache", BindingFlags.Static | BindingFlags.Public)!
.GetValue(null)!;
var getSubmarines = databaseCache.GetType()
.GetMethod("GetSubmarines", [])!;
var knownSubmarineData = ((IEnumerable)getSubmarines.Invoke(databaseCache, [])!).Cast<object>();
return knownSubmarineData
.Select(x => new Submarine(x))
.GroupBy(x => x.FreeCompanyId)
.Select(x => new SubmarineInfo(
characters.SingleOrDefault(y =>
y.CharacterType == CharacterType.FreeCompanyChest && y.CharacterId == x.Key),
x.ToList()
))
var submarineData = it.GetType().Assembly.GetType("SubmarineTracker.Data.Submarines");
var knownSubmarineData = submarineData!.GetField("KnownSubmarines")!;
return ((IEnumerable)knownSubmarineData.GetValue(null)!).Cast<object>()
.Select(x => new
{
OwnerId = (ulong)x.GetType().GetProperty("Key")!.GetValue(x)!,
FcWrapper = x.GetType().GetProperty("Value")!.GetValue(x)!
})
.Select(x => new
{
Owner = characters.FirstOrDefault(y => y.CharacterId == x.OwnerId),
Subs = new FcSubmarines(x.FcWrapper).Submarines,
})
.Where(x => x.Owner != null)
.Select(x => new
{
x.Subs,
Fc = characters.FirstOrDefault(y => y.CharacterId == x.Owner!.FreeCompanyId)
})
.Where(x => x.Fc != null)
.ToDictionary(x => x.Fc!, x => x.Subs);
.ToDictionary(
x => x.Fc!,
x => x.Subs.Select(y => new SubmarineStats
{
Id = x.Subs.IndexOf(y),
Name = y.Name,
Level = y.Level,
PredictedLevel = y.PredictedLevel,
Hull = y.Build.HullIdentifier,
Stern = y.Build.SternIdentifier,
Bow = y.Build.BowIdentifier,
Bridge = y.Build.BridgeIdentifier,
Build = y.Build.FullIdentifier,
State = y.State,
}).ToList());
}
else
return new Dictionary<Character, List<SubmarineStats>>();
}
private sealed record SubmarineInfo(Character? Fc, List<SubmarineStats> Subs)
{
public SubmarineInfo(Character? fc, List<Submarine> subs)
: this(fc, subs.Select(x => Convert(fc, subs.IndexOf(x), x)).ToList())
{
}
private static SubmarineStats Convert(Character? fc, int index, Submarine y)
{
return new SubmarineStats
{
Id = index,
Name = y.Name,
WorldId = fc?.WorldId ?? 0,
Level = y.Level,
PredictedLevel = y.PredictedLevel,
Hull = y.Build.HullIdentifier,
Stern = y.Build.SternIdentifier,
Bow = y.Build.BowIdentifier,
Bridge = y.Build.BridgeIdentifier,
Build = y.Build.FullIdentifier,
State = y.State,
ReturnTime = y.ReturnTime,
};
}
}
}

View File

@ -14,14 +14,14 @@ namespace Influx.Windows;
internal sealed class ConfigurationWindow : Window
{
private readonly IDalamudPluginInterface _pluginInterface;
private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState;
private readonly Configuration _configuration;
private readonly AllaganToolsIpc _allaganToolsIpc;
private string[] _filterNames = [];
private int _filterIndexToAdd;
private string[] _filterNames = Array.Empty<string>();
private int _filterIndexToAdd = 0;
public ConfigurationWindow(IDalamudPluginInterface pluginInterface, IClientState clientState,
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
Configuration configuration, AllaganToolsIpc allaganToolsIpc)
: base("Configuration###InfluxConfiguration")
{
@ -44,9 +44,7 @@ internal sealed class ConfigurationWindow : Window
}
}
public override void OnOpen() => RefreshFilters();
private void RefreshFilters()
public override void OnOpen()
{
_filterNames = _allaganToolsIpc.GetSearchFilters()
.Select(x => x.Value)
@ -103,7 +101,7 @@ internal sealed class ConfigurationWindow : Window
if (!tabItem)
return;
if (_clientState is { IsLoggedIn: true, LocalContentId: > 0, LocalPlayer.HomeWorld: not null })
if (_clientState is { IsLoggedIn: true, LocalContentId: > 0 })
{
string worldName = _clientState.LocalPlayer?.HomeWorld.GameData?.Name ?? "??";
ImGui.TextWrapped(
@ -127,9 +125,8 @@ internal sealed class ConfigurationWindow : Window
if (ImGui.Button("Remove inclusion"))
{
var characterInfo =
_configuration.IncludedCharacters.First(c => c.LocalContentId == _clientState.LocalContentId);
_configuration.IncludedCharacters.Remove(characterInfo);
_configuration.IncludedCharacters.RemoveAll(
c => c.LocalContentId == _clientState.LocalContentId);
Save();
}
}
@ -224,8 +221,7 @@ internal sealed class ConfigurationWindow : Window
{
ImGui.Combo("Add Search Filter", ref _filterIndexToAdd, _filterNames, _filterNames.Length);
ImGui.BeginDisabled(
_configuration.IncludedInventoryFilters.Any(x => x.Name == _filterNames[_filterIndexToAdd]));
ImGui.BeginDisabled(_configuration.IncludedInventoryFilters.Any(x => x.Name == _filterNames[_filterIndexToAdd]));
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Track Filter"))
{
_configuration.IncludedInventoryFilters.Add(new Configuration.FilterInfo
@ -242,11 +238,6 @@ internal sealed class ConfigurationWindow : Window
ImGui.TextColored(ImGuiColors.DalamudRed,
"You don't have any search filters, or the AllaganTools integration doesn't work.");
}
ImGui.Separator();
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Sync, "Refresh Filters"))
RefreshFilters();
}
private void Save(bool sendEvent = false)

View File

@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Windowing;
@ -24,10 +23,10 @@ internal sealed class StatisticsWindow : Window
public override void Draw()
{
if (ImGui.BeginTable("Currencies###InfluxStatisticsCurrencies", 2))
if (ImGui.BeginTable("Currencies###InfluxStatisticsCurrencies", 4))
{
ImGui.TableSetupColumn("Name");
ImGui.TableSetupColumn($"Gil ({_rows.Sum(x => x.Gil):N0})##Gil");
ImGui.TableSetupColumn("Gil");
ImGui.TableHeadersRow();
foreach (var row in _rows)
@ -37,7 +36,7 @@ internal sealed class StatisticsWindow : Window
ImGui.Text(row.Name);
if (ImGui.TableNextColumn())
ImGui.Text(row.Gil.ToString("N0", CultureInfo.InvariantCulture));
ImGui.Text(row.Gil.ToString("N0"));
}
ImGui.EndTable();

View File

@ -1,56 +1,28 @@
{
"version": 1,
"dependencies": {
"net8.0-windows7.0": {
"net7.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[2.1.13, )",
"resolved": "2.1.13",
"contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.1.1, )",
"resolved": "1.1.1",
"contentHash": "+H2t/t34h6mhEoUvHi8yGXyuZ2GjSovcGYehJrS2MDm2XgmPfZL2Sdxg+uL2lKgZ4M6tTwKHIlxOob2bgh0NRQ==",
"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"
}
"requested": "[2.1.12, )",
"resolved": "2.1.12",
"contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
},
"InfluxDB.Client": {
"type": "Direct",
"requested": "[4.14.0, )",
"resolved": "4.14.0",
"contentHash": "X42YEtBon4thG3qsbRWxO6VAadnX4CBn1xLhNYZGNizUMUboyut6VlRqa5BGiU1pHe4MCP+cds9rD4nWGB7vsQ==",
"requested": "[4.13.0, )",
"resolved": "4.13.0",
"contentHash": "2kDPC//sbSjm6R8lxqJrufNfF1AMhmeVw7dBnrOsMQjjxvaPrWFkITWIlL8AVtg5TdTqah9APp1laaJye6ebFg==",
"dependencies": {
"InfluxDB.Client.Core": "4.14.0",
"InfluxDB.Client.Core": "4.13.0",
"JsonSubTypes": "2.0.1",
"Microsoft.Extensions.ObjectPool": "7.0.13",
"Microsoft.Extensions.ObjectPool": "7.0.9",
"Microsoft.Net.Http.Headers": "2.2.8",
"System.Collections.Immutable": "6.0.0",
"System.Configuration.ConfigurationManager": "6.0.1",
"System.Reactive": "6.0.0"
}
},
"Microsoft.Extensions.ObjectPool": {
"type": "Direct",
"requested": "[9.0.0-preview.1.24081.5, )",
"resolved": "9.0.0-preview.1.24081.5",
"contentHash": "aAR7YW+pUUdvHk3vj7GtAi71dWGDIuY9270lsmQ6lKw23zzY+r8pLP3cGNbJdlnA9VWl+S+gnIVkBCqj2ROlEg=="
},
"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"
}
},
"CsvHelper": {
"type": "Transitive",
"resolved": "30.0.1",
@ -58,13 +30,13 @@
},
"InfluxDB.Client.Core": {
"type": "Transitive",
"resolved": "4.14.0",
"contentHash": "PtySdJE39Tv1T8p9SSpJve0Vm2gBzLdsHRL/weA97fEkeMip6Iw6zdgvb4ANWJ0MbYbprQQkSaZj2e4pTJpO6Q==",
"resolved": "4.13.0",
"contentHash": "SS6kVhUuQTjpbCBn6ULgXz/tLSttZhMEhmr0Vxk3vnvtlce4mYjX48NLk8QHWdUEyTL195AGrSyZTqYaDTZrgA==",
"dependencies": {
"CsvHelper": "30.0.1",
"Newtonsoft.Json": "13.0.3",
"NodaTime": "3.1.9",
"NodaTime.Serialization.JsonNet": "3.1.0",
"NodaTime.Serialization.JsonNet": "3.0.1",
"RestSharp": "110.1.0"
}
},
@ -76,10 +48,10 @@
"Newtonsoft.Json": "13.0.1"
}
},
"Microsoft.Build.Tasks.Git": {
"Microsoft.Extensions.ObjectPool": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
"resolved": "7.0.9",
"contentHash": "fFYlvFV8gle6IJ7Z0C3IydLfrzruL82pbBmn7oGsPK0zPtyN4pk2uX242ATOKbodZlRqLMAH6RE5wkRMCbkxug=="
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
@ -99,47 +71,6 @@
"System.Buffers": "4.5.0"
}
},
"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": {
"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"
}
},
"Microsoft.Win32.SystemEvents": {
"type": "Transitive",
"resolved": "6.0.0",
@ -160,10 +91,10 @@
},
"NodaTime.Serialization.JsonNet": {
"type": "Transitive",
"resolved": "3.1.0",
"contentHash": "eEr9lXUz50TYr4rpeJG4TDAABkpxjIKr5mDSi/Zav8d6Njy6fH7x4ZtNwWFj0Vd+vIvEZNrHFQ4Gfy8j4BqRGg==",
"resolved": "3.0.1",
"contentHash": "bmw+ElaOr21HZQQVYWZKgqs0QpSb/rJcJxem3Ok6YWAdenqOi0PS2hIkvlh+QJfsl2X3VJcla9Rhya/N3JnweA==",
"dependencies": {
"Newtonsoft.Json": "13.0.3",
"Newtonsoft.Json": "13.0.1",
"NodaTime": "[3.0.0, 4.0.0)"
}
},
@ -263,17 +194,14 @@
"autoretainerapi": {
"type": "Project",
"dependencies": {
"ECommons": "[2.2.0.2, )"
"ECommons": "[2.1.0, )"
}
},
"ecommons": {
"type": "Project"
},
"llib": {
"type": "Project",
"dependencies": {
"DalamudPackager": "[2.1.13, )"
}
"type": "Project"
}
}
}

2
LLib

@ -1 +1 @@
Subproject commit 8d947be6784804a7cab120d596dd54e88e548efc
Subproject commit 2f6ef354c401a9f1bb9b807327acad7e98662a2e

View File

@ -1,7 +0,0 @@
{
"sdk": {
"version": "8.0.0",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}