master
Liza 2024-03-21 11:32:39 +01:00
parent 959a66bd24
commit 1174413b9a
Signed by: liza
GPG Key ID: 7199F8D727D55F67
34 changed files with 1201 additions and 110 deletions

@ -1 +1 @@
Subproject commit 2715088db81d2be34447b27b0e5e5a3e766e47c7 Subproject commit 6f0aaa55bce6ec79fd4d72f84f21597b39e5445d

@ -1 +1 @@
Subproject commit f1c688a0599b41d70230021328a575da7351cf91 Subproject commit d238d4188e8b47b11252d75cb5e4b678b8da2756

2
Influx.sln.DotSettings Normal file
View File

@ -0,0 +1,2 @@
<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></wpf:ResourceDictionary>

1017
Influx/.editorconfig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,13 +15,12 @@ internal sealed class AllaganToolsIpc : IDisposable
private readonly DalamudReflector _dalamudReflector; private readonly DalamudReflector _dalamudReflector;
private readonly IFramework _framework; private readonly IFramework _framework;
private readonly IPluginLog _pluginLog; 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 readonly ICallGateSubscriber<Dictionary<string, string>> _getSearchFilters;
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor(); private ICharacterMonitor _characters;
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor(); private IInventoryMonitor _inventories;
public IFilterService Filters { get; set; } = new UnavailableFilterService(); private IFilterService _filters;
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector, public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector,
IFramework framework, IPluginLog pluginLog) IFramework framework, IPluginLog pluginLog)
@ -32,14 +31,20 @@ internal sealed class AllaganToolsIpc : IDisposable
_pluginLog = pluginLog; _pluginLog = pluginLog;
_initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized"); _initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
_initialized.Subscribe(ConfigureIpc);
_getSearchFilters = _getSearchFilters =
pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters"); pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
_characters = new UnavailableCharacterMonitor(_pluginLog);
_inventories = new UnavailableInventoryMonitor(_pluginLog);
_filters = new UnavailableFilterService(_pluginLog);
_initialized.Subscribe(ConfigureIpc);
try try
{ {
bool isInitialized = _isInitialized.InvokeFunc(); ICallGateSubscriber<bool> isInitializedFunc =
pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
bool isInitialized = isInitializedFunc.InvokeFunc();
if (isInitialized) if (isInitialized)
ConfigureIpc(true); ConfigureIpc(true);
} }
@ -60,10 +65,10 @@ internal sealed class AllaganToolsIpc : IDisposable
{ {
var pluginService = it.GetType().Assembly.GetType("InventoryTools.PluginService")!; var pluginService = it.GetType().Assembly.GetType("InventoryTools.PluginService")!;
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!); _characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
Inventories = new InventoryMonitor( _inventories = new InventoryMonitor(
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!); pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
Filters = new FilterService(pluginService.GetProperty("FilterService")!.GetValue(null)!); _filters = new FilterService(pluginService.GetProperty("FilterService")!.GetValue(null)!);
} }
else else
{ {
@ -95,7 +100,7 @@ internal sealed class AllaganToolsIpc : IDisposable
{ {
try try
{ {
return Filters.GetFilterByKeyOrName(keyOrName); return _filters.GetFilterByKeyOrName(keyOrName);
} }
catch (IpcError e) catch (IpcError e)
{ {
@ -106,9 +111,9 @@ internal sealed class AllaganToolsIpc : IDisposable
public Dictionary<Character, Currencies> CountCurrencies() public Dictionary<Character, Currencies> CountCurrencies()
{ {
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}"); _pluginLog.Debug($"{_characters.GetType()}, {_inventories.GetType()}");
var characters = Characters.All.ToDictionary(x => x.CharacterId, x => x); var characters = _characters.All.ToDictionary(x => x.CharacterId, x => x);
return Inventories.All return _inventories.All
.Where(x => characters.ContainsKey(x.Value.CharacterId)) .Where(x => characters.ContainsKey(x.Value.CharacterId))
.ToDictionary( .ToDictionary(
x => characters[x.Value.CharacterId], x => characters[x.Value.CharacterId],
@ -130,21 +135,14 @@ internal sealed class AllaganToolsIpc : IDisposable
public void Dispose() public void Dispose()
{ {
_initialized?.Unsubscribe(ConfigureIpc); _initialized.Unsubscribe(ConfigureIpc);
Characters = new UnavailableCharacterMonitor(); _characters = new UnavailableCharacterMonitor(_pluginLog);
Inventories = new UnavailableInventoryMonitor(); _inventories = new UnavailableInventoryMonitor(_pluginLog);
Filters = new UnavailableFilterService(); _filters = new UnavailableFilterService(_pluginLog);
} }
private sealed class InventoryWrapper private sealed class InventoryWrapper(IEnumerable<InventoryItem> items)
{ {
private readonly IEnumerable<InventoryItem> _items; public long Sum(int itemId) => items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
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,3 +1,4 @@
using System;
using System.Reflection; using System.Reflection;
namespace Influx.AllaganTools; namespace Influx.AllaganTools;
@ -10,6 +11,7 @@ internal sealed class Character
public Character(object @delegate) public Character(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate; _delegate = @delegate;
_name = _delegate.GetType().GetField("Name")!; _name = _delegate.GetType().GetField("Name")!;
_level = _delegate.GetType().GetField("Level")!; _level = _delegate.GetType().GetField("Level")!;

View File

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

View File

@ -1,4 +1,5 @@
using System.Collections; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -13,6 +14,7 @@ internal sealed class Filter
public Filter(object @delegate) public Filter(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate; _delegate = @delegate;
_generateFilteredList = _delegate.GetType().GetMethod("GenerateFilteredList")!; _generateFilteredList = _delegate.GetType().GetMethod("GenerateFilteredList")!;
} }

View File

@ -1,4 +1,5 @@
using System.Reflection; using System;
using System.Reflection;
namespace Influx.AllaganTools; namespace Influx.AllaganTools;
@ -9,13 +10,14 @@ internal sealed class FilterService : IFilterService
public FilterService(object @delegate) public FilterService(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate; _delegate = @delegate;
_getFilterByKeyOrName = _delegate.GetType().GetMethod("GetFilterByKeyOrName")!; _getFilterByKeyOrName = _delegate.GetType().GetMethod("GetFilterByKeyOrName")!;
} }
public Filter? GetFilterByKeyOrName(string keyOrName) public Filter? GetFilterByKeyOrName(string keyOrName)
{ {
var f = _getFilterByKeyOrName.Invoke(_delegate, new object?[] { keyOrName }); var f = _getFilterByKeyOrName.Invoke(_delegate, [keyOrName]);
return f != null ? new Filter(f) : null; return f != null ? new Filter(f) : null;
} }
} }

View File

@ -13,6 +13,7 @@ internal sealed class Inventory
public Inventory(object @delegate) public Inventory(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
_delegate = @delegate; _delegate = @delegate;
_getAllInventories = _delegate.GetType().GetMethod("GetAllInventories")!; _getAllInventories = _delegate.GetType().GetMethod("GetAllInventories")!;
CharacterId = (ulong)_delegate.GetType().GetProperty("CharacterId")!.GetValue(_delegate)!; CharacterId = (ulong)_delegate.GetType().GetProperty("CharacterId")!.GetValue(_delegate)!;

View File

@ -1,14 +1,14 @@
namespace Influx.AllaganTools; using System;
namespace Influx.AllaganTools;
internal sealed class InventoryItem internal sealed class InventoryItem
{ {
private readonly object _delegate;
public InventoryItem(object @delegate) public InventoryItem(object @delegate)
{ {
_delegate = @delegate; ArgumentNullException.ThrowIfNull(@delegate);
ItemId = (uint)_delegate.GetType().GetField("ItemId")!.GetValue(_delegate)!; ItemId = (uint)@delegate.GetType().GetField("ItemId")!.GetValue(@delegate)!;
Quantity = (uint)_delegate.GetType().GetField("Quantity")!.GetValue(_delegate)!; Quantity = (uint)@delegate.GetType().GetField("Quantity")!.GetValue(@delegate)!;
} }
public uint ItemId { get; } public uint ItemId { get; }

View File

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

View File

@ -1,10 +1,14 @@
namespace Influx.AllaganTools; using System;
namespace Influx.AllaganTools;
using ItemFlags = FFXIVClientStructs.FFXIV.Client.Game.InventoryItem.ItemFlags; using ItemFlags = FFXIVClientStructs.FFXIV.Client.Game.InventoryItem.ItemFlags;
internal sealed class SortingResult internal sealed class SortingResult
{ {
public SortingResult(object @delegate) public SortingResult(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
LocalContentId = (ulong)@delegate.GetType().GetProperty("SourceRetainerId")!.GetValue(@delegate)!; LocalContentId = (ulong)@delegate.GetType().GetProperty("SourceRetainerId")!.GetValue(@delegate)!;
Quantity = (int)@delegate.GetType().GetProperty("Quantity")!.GetValue(@delegate)!; Quantity = (int)@delegate.GetType().GetProperty("Quantity")!.GetValue(@delegate)!;

View File

@ -1,10 +1,26 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Dalamud.Plugin.Services;
namespace Influx.AllaganTools; namespace Influx.AllaganTools;
internal sealed class UnavailableCharacterMonitor : ICharacterMonitor internal sealed class UnavailableCharacterMonitor(IPluginLog pluginLog) : ICharacterMonitor
{ {
public IEnumerable<Character> PlayerCharacters => Array.Empty<Character>(); public IEnumerable<Character> PlayerCharacters
public IEnumerable<Character> All => Array.Empty<Character>(); {
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>();
}
}
} }

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net8.0-windows</TargetFramework>
<Version>0.11</Version> <Version>0.12</Version>
<LangVersion>11.0</LangVersion> <LangVersion>12</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
@ -25,7 +25,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.12"/> <PackageReference Include="DalamudPackager" Version="2.1.12"/>
<PackageReference Include="InfluxDB.Client" Version="4.14.0" /> <PackageReference Include="InfluxDB.Client" Version="4.14.0" />
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,5 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
@ -23,7 +25,7 @@ internal sealed class InfluxStatisticsClient : IDisposable
private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex; private readonly IReadOnlyDictionary<byte, byte> _classJobToArrayIndex;
private readonly IReadOnlyDictionary<byte, string> _classJobNames; private readonly IReadOnlyDictionary<byte, string> _classJobNames;
private readonly IReadOnlyDictionary<sbyte, string> _expToJobs; private readonly IReadOnlyDictionary<sbyte, string> _expToJobs;
private readonly IReadOnlyDictionary<uint, PriceInfo> _prices; private readonly ReadOnlyDictionary<uint, PriceInfo> _prices;
public InfluxStatisticsClient(IChatGui chatGui, Configuration configuration, IDataManager dataManager, public InfluxStatisticsClient(IChatGui chatGui, Configuration configuration, IDataManager dataManager,
IClientState clientState, IPluginLog pluginLog) IClientState clientState, IPluginLog pluginLog)
@ -43,12 +45,14 @@ internal sealed class InfluxStatisticsClient : IDisposable
.Where(x => x.Abbreviation.ToString() != "SMN") .Where(x => x.Abbreviation.ToString() != "SMN")
.ToDictionary(x => x.ExpArrayIndex, x => x.Abbreviation.ToString()); .ToDictionary(x => x.ExpArrayIndex, x => x.Abbreviation.ToString());
_prices = dataManager.GetExcelSheet<Item>()! _prices = dataManager.GetExcelSheet<Item>()!
.AsEnumerable()
.ToDictionary(x => x.RowId, x => new PriceInfo .ToDictionary(x => x.RowId, x => new PriceInfo
{ {
Name = x.Name.ToString(), Name = x.Name.ToString(),
Normal = x.PriceLow, Normal = x.PriceLow,
UiCategory = x.ItemUICategory.Row, UiCategory = x.ItemUICategory.Row,
}); })
.AsReadOnly();
} }
public bool Enabled => _configuration.Server.Enabled && public bool Enabled => _configuration.Server.Enabled &&
@ -113,7 +117,7 @@ internal sealed class InfluxStatisticsClient : IDisposable
foreach (var sub in subs) foreach (var sub in subs)
{ {
values.Add(PointData.Measurement("submersibles") values.Add(PointData.Measurement("submersibles")
.Tag("id", fc.CharacterId.ToString()) .Tag("id", fc.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("fc_name", fc.Name) .Tag("fc_name", fc.Name)
.Tag("sub_id", $"{fc.CharacterId}_{sub.Id}") .Tag("sub_id", $"{fc.CharacterId}_{sub.Id}")
.Tag("sub_name", sub.Name) .Tag("sub_name", sub.Name)
@ -133,7 +137,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
var writeApi = client.GetWriteApiAsync(); var writeApi = client.GetWriteApiAsync();
await writeApi.WritePointsAsync( await writeApi.WritePointsAsync(
values, values,
_configuration.Server.Bucket, _configuration.Server.Organization); _configuration.Server.Bucket, _configuration.Server.Organization)
.ConfigureAwait(false);
_pluginLog.Verbose($"Influx: Sent {values.Count} data points to server"); _pluginLog.Verbose($"Influx: Sent {values.Count} data points to server");
} }
@ -155,10 +160,10 @@ internal sealed class InfluxStatisticsClient : IDisposable
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany); x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
Func<string, PointData> pointData = s => PointData.Measurement(s) Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString()) .Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("player_name", character.Name) .Tag("player_name", character.Name)
.Tag("type", character.CharacterType.ToString()) .Tag("type", character.CharacterType.ToString())
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null) .Tag("fc_id", includeFc ? character.FreeCompanyId.ToString(CultureInfo.InvariantCulture) : null)
.Timestamp(date, WritePrecision.S); .Timestamp(date, WritePrecision.S);
yield return pointData("currency") yield return pointData("currency")
@ -230,9 +235,9 @@ internal sealed class InfluxStatisticsClient : IDisposable
var owner = update.Currencies.Keys.First(x => x.CharacterId == character.OwnerId); var owner = update.Currencies.Keys.First(x => x.CharacterId == character.OwnerId);
Func<string, PointData> pointData = s => PointData.Measurement(s) Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString()) .Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("player_name", owner.Name) .Tag("player_name", owner.Name)
.Tag("player_id", character.OwnerId.ToString()) .Tag("player_id", character.OwnerId.ToString(CultureInfo.InvariantCulture))
.Tag("type", character.CharacterType.ToString()) .Tag("type", character.CharacterType.ToString())
.Tag("retainer_name", character.Name) .Tag("retainer_name", character.Name)
.Timestamp(date, WritePrecision.S); .Timestamp(date, WritePrecision.S);
@ -280,9 +285,9 @@ internal sealed class InfluxStatisticsClient : IDisposable
yield return pointData("items") yield return pointData("items")
.Tag("filter_name", filterName) .Tag("filter_name", filterName)
.Tag("item_id", item.Key.ItemId.ToString()) .Tag("item_id", item.Key.ItemId.ToString(CultureInfo.InvariantCulture))
.Tag("item_name", priceInfo.Name) .Tag("item_name", priceInfo.Name)
.Tag("hq", (item.Key.IsHq ? 1 : 0).ToString()) .Tag("hq", (item.Key.IsHq ? 1 : 0).ToString(CultureInfo.InvariantCulture))
.Field("quantity", item.Sum(x => x.Quantity)) .Field("quantity", item.Sum(x => x.Quantity))
.Field("total_gil", item.Sum(x => x.Quantity) * (priceHq ? priceInfo.Hq : priceInfo.Normal)); .Field("total_gil", item.Sum(x => x.Quantity) * (priceHq ? priceInfo.Hq : priceInfo.Normal));
} }
@ -295,7 +300,7 @@ internal sealed class InfluxStatisticsClient : IDisposable
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats); update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
Func<string, PointData> pointData = s => PointData.Measurement(s) Func<string, PointData> pointData = s => PointData.Measurement(s)
.Tag("id", character.CharacterId.ToString()) .Tag("id", character.CharacterId.ToString(CultureInfo.InvariantCulture))
.Tag("fc_name", character.Name) .Tag("fc_name", character.Name)
.Tag("type", character.CharacterType.ToString()) .Tag("type", character.CharacterType.ToString())
.Timestamp(date, WritePrecision.S); .Timestamp(date, WritePrecision.S);

View File

@ -18,7 +18,8 @@ using LLib;
namespace Influx; namespace Influx;
[SuppressMessage("ReSharper", "UnusedType.Global")] [SuppressMessage("ReSharper", "UnusedType.Global")]
public sealed class InfluxPlugin : IDalamudPlugin [SuppressMessage("Performance", "CA1812")]
internal sealed class InfluxPlugin : IDalamudPlugin
{ {
private readonly object _lock = new(); private readonly object _lock = new();
private readonly DalamudPluginInterface _pluginInterface; private readonly DalamudPluginInterface _pluginInterface;

View File

@ -16,7 +16,7 @@ using Newtonsoft.Json;
namespace Influx.LocalStatistics; namespace Influx.LocalStatistics;
public class FcStatsCalculator : IDisposable internal sealed class FcStatsCalculator : IDisposable
{ {
private readonly DalamudPluginInterface _pluginInterface; private readonly DalamudPluginInterface _pluginInterface;
private readonly IClientState _clientState; private readonly IClientState _clientState;
@ -29,7 +29,7 @@ public class FcStatsCalculator : IDisposable
private readonly Dictionary<ulong, FcStats> _cache = new(); private readonly Dictionary<ulong, FcStats> _cache = new();
private Status? _status = null; private Status? _status;
public FcStatsCalculator( public FcStatsCalculator(
IDalamudPlugin plugin, IDalamudPlugin plugin,

View File

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

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -111,7 +112,7 @@ internal sealed class LocalStatsCalculator : IDisposable
UpdateStatistics(); UpdateStatistics();
} }
private IReadOnlyList<QuestInfo> PopulateStartingCities(List<QuestInfo> quests, uint envoyQuestId, private static ReadOnlyCollection<QuestInfo> PopulateStartingCities(List<QuestInfo> quests, uint envoyQuestId,
uint startingQuestId, bool popCallOfTheSea) uint startingQuestId, bool popCallOfTheSea)
{ {
QuestInfo callOfTheSea = quests.First(x => x.PreviousQuestIds.Contains(envoyQuestId)); QuestInfo callOfTheSea = quests.First(x => x.PreviousQuestIds.Contains(envoyQuestId));
@ -119,12 +120,10 @@ internal sealed class LocalStatsCalculator : IDisposable
quests.Remove(callOfTheSea); quests.Remove(callOfTheSea);
List<QuestInfo> startingCityQuests = new List<QuestInfo> { callOfTheSea }; List<QuestInfo> startingCityQuests = new List<QuestInfo> { callOfTheSea };
uint? questId = envoyQuestId; uint questId = envoyQuestId;
QuestInfo? quest;
do do
{ {
quest = quests.First(x => x.RowId == questId); QuestInfo quest = quests.First(x => x.RowId == questId);
quests.Remove(quest); quests.Remove(quest);
if (quest.Name == "Close to Home") if (quest.Name == "Close to Home")
@ -133,14 +132,14 @@ internal sealed class LocalStatsCalculator : IDisposable
{ {
RowId = startingQuestId, RowId = startingQuestId,
Name = "Coming to ...", Name = "Coming to ...",
PreviousQuestIds = new(), PreviousQuestIds = new List<uint>(),
Genre = quest.Genre, Genre = quest.Genre,
}; };
} }
startingCityQuests.Add(quest); startingCityQuests.Add(quest);
questId = quest.PreviousQuestIds.FirstOrDefault(); questId = quest.PreviousQuestIds.FirstOrDefault();
} while (questId != null && questId != 0); } while (questId != 0);
return Enumerable.Reverse(startingCityQuests).ToList().AsReadOnly(); return Enumerable.Reverse(startingCityQuests).ToList().AsReadOnly();
} }

View File

@ -6,6 +6,6 @@ public sealed class QuestInfo
{ {
public required uint RowId { get; init; } public required uint RowId { get; init; }
public required string Name { get; init; } public required string Name { get; init; }
public required List<uint> PreviousQuestIds { get; init; } public required IList<uint> PreviousQuestIds { get; init; }
public required uint Genre { get; init; } public required uint Genre { get; init; }
} }

View File

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

View File

@ -1,13 +1,15 @@
using System.Collections; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace Influx.SubmarineTracker; namespace Influx.SubmarineTracker;
public sealed class FcSubmarines internal sealed class FcSubmarines
{ {
public FcSubmarines(object @delegate) public FcSubmarines(object @delegate)
{ {
ArgumentNullException.ThrowIfNull(@delegate);
Submarines = ((IEnumerable)@delegate.GetType().GetField("Submarines")!.GetValue(@delegate)!) Submarines = ((IEnumerable)@delegate.GetType().GetField("Submarines")!.GetValue(@delegate)!)
.Cast<object>() .Cast<object>()
.Select(x => new Submarine(x)) .Select(x => new Submarine(x))

View File

@ -2,21 +2,23 @@
namespace Influx.SubmarineTracker; namespace Influx.SubmarineTracker;
public sealed class Submarine internal sealed class Submarine
{ {
public Submarine(object @delegate) public Submarine(object @delegate)
{ {
Name = (string)@delegate.GetType().GetProperty("Name")!.GetValue(@delegate)!; ArgumentNullException.ThrowIfNull(@delegate);
Level = (ushort)@delegate.GetType().GetProperty("Rank")!.GetValue(@delegate)!; Type type = @delegate.GetType();
Build = new Build(@delegate.GetType().GetProperty("Build")!.GetValue(@delegate)!); Name = (string)type.GetProperty("Name")!.GetValue(@delegate)!;
Level = (ushort)type.GetProperty("Rank")!.GetValue(@delegate)!;
Build = new Build(type.GetProperty("Build")!.GetValue(@delegate)!);
try try
{ {
(uint predictedLevel, double _) = ((uint, double))@delegate.GetType().GetMethod("PredictExpGrowth")!.Invoke(@delegate, Array.Empty<object?>())!; (uint predictedLevel, double _) = ((uint, double))type.GetMethod("PredictExpGrowth")!.Invoke(@delegate, Array.Empty<object?>())!;
PredictedLevel = (ushort)predictedLevel; PredictedLevel = (ushort)predictedLevel;
bool onVoyage = (bool)@delegate.GetType().GetMethod("IsOnVoyage")!.Invoke(@delegate, Array.Empty<object>())!; bool onVoyage = (bool)type.GetMethod("IsOnVoyage")!.Invoke(@delegate, Array.Empty<object>())!;
bool returned = (bool)@delegate.GetType().GetMethod("IsDone")!.Invoke(@delegate, Array.Empty<object>())!; bool returned = (bool)type.GetMethod("IsDone")!.Invoke(@delegate, Array.Empty<object>())!;
if (onVoyage) if (onVoyage)
State = returned ? EState.Returned : EState.Voyage; State = returned ? EState.Returned : EState.Voyage;
else else

View File

@ -1,6 +1,6 @@
namespace Influx.SubmarineTracker; namespace Influx.SubmarineTracker;
public sealed class SubmarineStats internal sealed class SubmarineStats
{ {
public required string Name { get; init; } public required string Name { get; init; }
public required int Id { get; init; } public required int Id { get; init; }

View File

@ -1,6 +1,8 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Dalamud.Plugin;
using Influx.AllaganTools; using Influx.AllaganTools;
using LLib; using LLib;
@ -15,9 +17,10 @@ internal sealed class SubmarineTrackerIpc
_dalamudReflector = dalamudReflector; _dalamudReflector = dalamudReflector;
} }
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public Dictionary<Character, List<SubmarineStats>> GetSubmarineStats(List<Character> characters) public Dictionary<Character, List<SubmarineStats>> GetSubmarineStats(List<Character> characters)
{ {
if (_dalamudReflector.TryGetDalamudPlugin("Submarine Tracker", out var it, false, true)) if (_dalamudReflector.TryGetDalamudPlugin("Submarine Tracker", out IDalamudPlugin? it, false, true))
{ {
var submarineData = it.GetType().Assembly.GetType("SubmarineTracker.Data.Submarines"); var submarineData = it.GetType().Assembly.GetType("SubmarineTracker.Data.Submarines");
var knownSubmarineData = submarineData!.GetField("KnownSubmarines")!; var knownSubmarineData = submarineData!.GetField("KnownSubmarines")!;

View File

@ -19,7 +19,7 @@ internal sealed class ConfigurationWindow : Window
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly AllaganToolsIpc _allaganToolsIpc; private readonly AllaganToolsIpc _allaganToolsIpc;
private string[] _filterNames = Array.Empty<string>(); private string[] _filterNames = Array.Empty<string>();
private int _filterIndexToAdd = 0; private int _filterIndexToAdd;
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState, public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
Configuration configuration, AllaganToolsIpc allaganToolsIpc) Configuration configuration, AllaganToolsIpc allaganToolsIpc)
@ -44,7 +44,9 @@ internal sealed class ConfigurationWindow : Window
} }
} }
public override void OnOpen() public override void OnOpen() => RefreshFilters();
private void RefreshFilters()
{ {
_filterNames = _allaganToolsIpc.GetSearchFilters() _filterNames = _allaganToolsIpc.GetSearchFilters()
.Select(x => x.Value) .Select(x => x.Value)
@ -125,8 +127,9 @@ internal sealed class ConfigurationWindow : Window
if (ImGui.Button("Remove inclusion")) if (ImGui.Button("Remove inclusion"))
{ {
_configuration.IncludedCharacters.RemoveAll( var characterInfo =
c => c.LocalContentId == _clientState.LocalContentId); _configuration.IncludedCharacters.First(c => c.LocalContentId == _clientState.LocalContentId);
_configuration.IncludedCharacters.Remove(characterInfo);
Save(); Save();
} }
} }
@ -221,7 +224,8 @@ internal sealed class ConfigurationWindow : Window
{ {
ImGui.Combo("Add Search Filter", ref _filterIndexToAdd, _filterNames, _filterNames.Length); 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")) if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Track Filter"))
{ {
_configuration.IncludedInventoryFilters.Add(new Configuration.FilterInfo _configuration.IncludedInventoryFilters.Add(new Configuration.FilterInfo
@ -238,6 +242,11 @@ internal sealed class ConfigurationWindow : Window
ImGui.TextColored(ImGuiColors.DalamudRed, ImGui.TextColored(ImGuiColors.DalamudRed,
"You don't have any search filters, or the AllaganTools integration doesn't work."); "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) private void Save(bool sendEvent = false)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
@ -36,7 +37,7 @@ internal sealed class StatisticsWindow : Window
ImGui.Text(row.Name); ImGui.Text(row.Name);
if (ImGui.TableNextColumn()) if (ImGui.TableNextColumn())
ImGui.Text(row.Gil.ToString("N0")); ImGui.Text(row.Gil.ToString("N0", CultureInfo.InvariantCulture));
} }
ImGui.EndTable(); ImGui.EndTable();

View File

@ -1,7 +1,7 @@
{ {
"version": 1, "version": 1,
"dependencies": { "dependencies": {
"net7.0-windows7.0": { "net8.0-windows7.0": {
"DalamudPackager": { "DalamudPackager": {
"type": "Direct", "type": "Direct",
"requested": "[2.1.12, )", "requested": "[2.1.12, )",
@ -25,9 +25,9 @@
}, },
"Microsoft.Extensions.ObjectPool": { "Microsoft.Extensions.ObjectPool": {
"type": "Direct", "type": "Direct",
"requested": "[8.0.2, )", "requested": "[8.0.3, )",
"resolved": "8.0.2", "resolved": "8.0.3",
"contentHash": "LA7lDy048CVjGCwsPqRFVwH8vl5ooHmSFji13Oczw+mOnGhqenWXttkWcJ5dhIR0bhayZrQz4BaSPEVtE8Tt0A==" "contentHash": "sk/TGiccSeXUtVeBfFlWJnzp6xLlAxIW+bCHs7uVPLFPQk8vICNmu9Gc3JsKOn6fQuRkrPetQ8EHv4dCiMqhxg=="
}, },
"CsvHelper": { "CsvHelper": {
"type": "Transitive", "type": "Transitive",
@ -195,7 +195,7 @@
"autoretainerapi": { "autoretainerapi": {
"type": "Project", "type": "Project",
"dependencies": { "dependencies": {
"ECommons": "[2.1.0, )" "ECommons": "[2.1.0.7, )"
} }
}, },
"ecommons": { "ecommons": {

2
LLib

@ -1 +1 @@
Subproject commit 2f6ef354c401a9f1bb9b807327acad7e98662a2e Subproject commit 3792244261a9f5426a7916f5a6dd1966238ba84a

7
global.json Normal file
View File

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