Track AllaganTools filters
This commit is contained in:
parent
c7ceaa2cfd
commit
ef1769c4f7
@ -15,22 +15,27 @@ 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>? _initalized;
|
private readonly ICallGateSubscriber<bool, bool>? _initialized;
|
||||||
private readonly ICallGateSubscriber<bool>? _isInitialized;
|
private readonly ICallGateSubscriber<bool>? _isInitialized;
|
||||||
|
private readonly ICallGateSubscriber<Dictionary<string, string>> _getSearchFilters;
|
||||||
|
|
||||||
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
|
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
|
||||||
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor();
|
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor();
|
||||||
|
public IFilterService Filters { get; set; } = new UnavailableFilterService();
|
||||||
|
|
||||||
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector, IFramework framework, IPluginLog pluginLog)
|
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, IChatGui chatGui, DalamudReflector dalamudReflector,
|
||||||
|
IFramework framework, IPluginLog pluginLog)
|
||||||
{
|
{
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
_dalamudReflector = dalamudReflector;
|
_dalamudReflector = dalamudReflector;
|
||||||
_framework = framework;
|
_framework = framework;
|
||||||
_pluginLog = pluginLog;
|
_pluginLog = pluginLog;
|
||||||
|
|
||||||
_initalized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
|
_initialized = pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
|
||||||
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
|
_isInitialized = pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
|
||||||
_initalized.Subscribe(ConfigureIpc);
|
_initialized.Subscribe(ConfigureIpc);
|
||||||
|
_getSearchFilters =
|
||||||
|
pluginInterface.GetIpcSubscriber<Dictionary<string, string>>("AllaganTools.GetSearchFilters");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -58,6 +63,7 @@ internal sealed class AllaganToolsIpc : IDisposable
|
|||||||
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)!);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -72,6 +78,32 @@ internal sealed class AllaganToolsIpc : IDisposable
|
|||||||
}, TimeSpan.FromMilliseconds(100));
|
}, TimeSpan.FromMilliseconds(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, string> GetSearchFilters()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _getSearchFilters.InvokeFunc();
|
||||||
|
}
|
||||||
|
catch (IpcError e)
|
||||||
|
{
|
||||||
|
_pluginLog.Error(e, "Unable to retrieve allagantools filters");
|
||||||
|
return new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Filter? GetFilter(string keyOrName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Filters.GetFilterByKeyOrName(keyOrName);
|
||||||
|
}
|
||||||
|
catch (IpcError e)
|
||||||
|
{
|
||||||
|
_pluginLog.Error(e, $"Unable to retrieve filter items for filter '{keyOrName}'");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Dictionary<Character, Currencies> CountCurrencies()
|
public Dictionary<Character, Currencies> CountCurrencies()
|
||||||
{
|
{
|
||||||
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}");
|
_pluginLog.Debug($"{Characters.GetType()}, {Inventories.GetType()}");
|
||||||
@ -98,9 +130,10 @@ internal sealed class AllaganToolsIpc : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_initalized?.Unsubscribe(ConfigureIpc);
|
_initialized?.Unsubscribe(ConfigureIpc);
|
||||||
Characters = new UnavailableCharacterMonitor();
|
Characters = new UnavailableCharacterMonitor();
|
||||||
Inventories = new UnavailableInventoryMonitor();
|
Inventories = new UnavailableInventoryMonitor();
|
||||||
|
Filters = new UnavailableFilterService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class InventoryWrapper
|
private sealed class InventoryWrapper
|
||||||
|
29
Influx/AllaganTools/Filter.cs
Normal file
29
Influx/AllaganTools/Filter.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
21
Influx/AllaganTools/FilterService.cs
Normal file
21
Influx/AllaganTools/FilterService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
6
Influx/AllaganTools/IFilterService.cs
Normal file
6
Influx/AllaganTools/IFilterService.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Influx.AllaganTools;
|
||||||
|
|
||||||
|
internal interface IFilterService
|
||||||
|
{
|
||||||
|
Filter? GetFilterByKeyOrName(string keyOrName);
|
||||||
|
}
|
27
Influx/AllaganTools/SortingResult.cs
Normal file
27
Influx/AllaganTools/SortingResult.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Influx.AllaganTools;
|
||||||
|
|
||||||
|
using ItemFlags = FFXIVClientStructs.FFXIV.Client.Game.InventoryItem.ItemFlags;
|
||||||
|
internal sealed class SortingResult
|
||||||
|
{
|
||||||
|
public SortingResult(object @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().GetField("ItemId")!.GetValue(inventoryItem)!;
|
||||||
|
Flags = (ItemFlags)inventoryItem.GetType().GetField("Flags")!.GetValue(inventoryItem)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong LocalContentId { get; }
|
||||||
|
public uint ItemId { get; }
|
||||||
|
public ItemFlags Flags { get; }
|
||||||
|
public int Quantity { get; }
|
||||||
|
|
||||||
|
public bool IsHq => Flags.HasFlag(ItemFlags.HQ);
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return $"{LocalContentId}, {ItemId}, {Quantity}";
|
||||||
|
}
|
||||||
|
}
|
6
Influx/AllaganTools/UnavailableFilterService.cs
Normal file
6
Influx/AllaganTools/UnavailableFilterService.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace Influx.AllaganTools;
|
||||||
|
|
||||||
|
internal sealed class UnavailableFilterService : IFilterService
|
||||||
|
{
|
||||||
|
public Filter? GetFilterByKeyOrName(string keyOrName) => null;
|
||||||
|
}
|
@ -10,6 +10,7 @@ public sealed class Configuration : IPluginConfiguration
|
|||||||
public ServerConfiguration Server { get; set; } = new();
|
public ServerConfiguration Server { get; set; } = new();
|
||||||
|
|
||||||
public List<CharacterInfo> IncludedCharacters { get; set; } = new();
|
public List<CharacterInfo> IncludedCharacters { get; set; } = new();
|
||||||
|
public List<FilterInfo> IncludedInventoryFilters { get; set; } = new();
|
||||||
|
|
||||||
public sealed class ServerConfiguration
|
public sealed class ServerConfiguration
|
||||||
{
|
{
|
||||||
@ -27,4 +28,9 @@ public sealed class Configuration : IPluginConfiguration
|
|||||||
public string? CachedWorldName { get; set; }
|
public string? CachedWorldName { get; set; }
|
||||||
public bool IncludeFreeCompany { get; set; } = true;
|
public bool IncludeFreeCompany { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed class FilterInfo
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0-windows</TargetFramework>
|
<TargetFramework>net7.0-windows</TargetFramework>
|
||||||
<Version>0.8</Version>
|
<Version>0.9</Version>
|
||||||
<LangVersion>11.0</LangVersion>
|
<LangVersion>11.0</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
@ -7,6 +7,7 @@ using Influx.AllaganTools;
|
|||||||
using Influx.LocalStatistics;
|
using Influx.LocalStatistics;
|
||||||
using InfluxDB.Client;
|
using InfluxDB.Client;
|
||||||
using InfluxDB.Client.Api.Domain;
|
using InfluxDB.Client.Api.Domain;
|
||||||
|
using InfluxDB.Client.Core;
|
||||||
using InfluxDB.Client.Writes;
|
using InfluxDB.Client.Writes;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
using GrandCompany = FFXIVClientStructs.FFXIV.Client.UI.Agent.GrandCompany;
|
||||||
@ -22,7 +23,8 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
|||||||
private readonly IPluginLog _pluginLog;
|
private readonly IPluginLog _pluginLog;
|
||||||
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 Dictionary<sbyte, string> _expToJobs;
|
private readonly IReadOnlyDictionary<sbyte, string> _expToJobs;
|
||||||
|
private readonly IReadOnlyDictionary<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)
|
||||||
@ -41,6 +43,13 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
|||||||
.Where(x => x.JobIndex > 0)
|
.Where(x => x.JobIndex > 0)
|
||||||
.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>()!
|
||||||
|
.ToDictionary(x => x.RowId, x => new PriceInfo
|
||||||
|
{
|
||||||
|
Name = x.Name.ToString(),
|
||||||
|
Normal = x.PriceLow,
|
||||||
|
UiCategory = x.ItemUICategory.Row,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Enabled => _configuration.Server.Enabled &&
|
public bool Enabled => _configuration.Server.Enabled &&
|
||||||
@ -84,143 +93,16 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
|||||||
{
|
{
|
||||||
if (character.CharacterType == CharacterType.Character)
|
if (character.CharacterType == CharacterType.Character)
|
||||||
{
|
{
|
||||||
update.LocalStats.TryGetValue(character, out LocalStats? localStats);
|
values.AddRange(GenerateCharacterStats(character, currencies, update, date));
|
||||||
|
|
||||||
bool includeFc = character.FreeCompanyId > 0 &&
|
|
||||||
_configuration.IncludedCharacters.Any(x =>
|
|
||||||
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
|
|
||||||
|
|
||||||
values.Add(PointData.Measurement("currency")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("player_name", character.Name)
|
|
||||||
.Tag("type", character.CharacterType.ToString())
|
|
||||||
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
|
|
||||||
.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)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
|
|
||||||
if (localStats != null)
|
|
||||||
{
|
|
||||||
values.Add(PointData.Measurement("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
|
|
||||||
{
|
|
||||||
GrandCompany.Maelstrom => currencies.GcSealsMaelstrom,
|
|
||||||
GrandCompany.TwinAdder => currencies.GcSealsTwinAdders,
|
|
||||||
GrandCompany.ImmortalFlames => currencies.GcSealsImmortalFlames,
|
|
||||||
_ => 0,
|
|
||||||
})
|
|
||||||
.Field("seal_cap", localStats.GcRank switch
|
|
||||||
{
|
|
||||||
1 => 10_000,
|
|
||||||
2 => 15_000,
|
|
||||||
3 => 20_000,
|
|
||||||
4 => 25_000,
|
|
||||||
5 => 30_000,
|
|
||||||
6 => 35_000,
|
|
||||||
7 => 40_000,
|
|
||||||
8 => 45_000,
|
|
||||||
9 => 50_000,
|
|
||||||
10 => 80_000,
|
|
||||||
11 => 90_000,
|
|
||||||
_ => 0,
|
|
||||||
})
|
|
||||||
.Field("squadron_unlocked", localStats.SquadronUnlocked ? 1 : 0)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
|
|
||||||
if (localStats.ClassJobLevels.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var (expIndex, abbreviation) in _expToJobs)
|
|
||||||
{
|
|
||||||
var level = localStats.ClassJobLevels[expIndex];
|
|
||||||
if (level > 0)
|
|
||||||
{
|
|
||||||
values.Add(PointData.Measurement("experience")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("player_name", character.Name)
|
|
||||||
.Tag("type", character.CharacterType.ToString())
|
|
||||||
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
|
|
||||||
.Tag("job", abbreviation)
|
|
||||||
.Field("level", level)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (localStats.MsqCount != -1)
|
|
||||||
{
|
|
||||||
values.Add(PointData.Measurement("quests")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("player_name", character.Name)
|
|
||||||
.Tag("msq_name", localStats.MsqName)
|
|
||||||
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
|
|
||||||
.Field("msq_count", localStats.MsqCount)
|
|
||||||
.Field("msq_genre", localStats.MsqGenre)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (character.CharacterType == CharacterType.Retainer)
|
else if (character.CharacterType == CharacterType.Retainer)
|
||||||
{
|
{
|
||||||
var owner = currencyStats.Keys.First(x => x.CharacterId == character.OwnerId);
|
values.AddRange(GenerateRetainerStats(character, currencies, update, date));
|
||||||
values.Add(PointData.Measurement("currency")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("player_name", owner.Name)
|
|
||||||
.Tag("player_id", character.OwnerId.ToString())
|
|
||||||
.Tag("type", character.CharacterType.ToString())
|
|
||||||
.Tag("retainer_name", character.Name)
|
|
||||||
.Field("gil", currencies.Gil)
|
|
||||||
.Field("ceruleum_tanks", currencies.CeruleumTanks)
|
|
||||||
.Field("repair_kits", currencies.RepairKits)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
|
|
||||||
if (update.LocalStats.TryGetValue(owner, out var ownerStats) && character.ClassJob != 0)
|
|
||||||
{
|
|
||||||
values.Add(PointData.Measurement("retainer")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("player_name", owner.Name)
|
|
||||||
.Tag("player_id", character.OwnerId.ToString())
|
|
||||||
.Tag("type", character.CharacterType.ToString())
|
|
||||||
.Tag("retainer_name", character.Name)
|
|
||||||
.Tag("class", _classJobNames[character.ClassJob])
|
|
||||||
.Field("level", character.Level)
|
|
||||||
.Field("is_max_level", character.Level == ownerStats.MaxLevel ? 1 : 0)
|
|
||||||
.Field("can_reach_max_level",
|
|
||||||
ownerStats.ClassJobLevels.Count > 0 &&
|
|
||||||
ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] ==
|
|
||||||
ownerStats.MaxLevel
|
|
||||||
? 1
|
|
||||||
: 0)
|
|
||||||
.Field("levels_before_cap",
|
|
||||||
ownerStats.ClassJobLevels.Count > 0
|
|
||||||
? ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] -
|
|
||||||
character.Level
|
|
||||||
: 0)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (character.CharacterType == CharacterType.FreeCompanyChest &&
|
else if (character.CharacterType == CharacterType.FreeCompanyChest &&
|
||||||
validFcIds.Contains(character.CharacterId))
|
validFcIds.Contains(character.CharacterId))
|
||||||
{
|
{
|
||||||
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
|
values.AddRange(GenerateFcStats(character, currencies, update, date));
|
||||||
|
|
||||||
values.Add(PointData.Measurement("currency")
|
|
||||||
.Tag("id", character.CharacterId.ToString())
|
|
||||||
.Tag("fc_name", character.Name)
|
|
||||||
.Tag("type", character.CharacterType.ToString())
|
|
||||||
.Field("gil", currencies.Gil)
|
|
||||||
.Field("fccredit", fcStats?.FcCredits ?? 0)
|
|
||||||
.Field("ceruleum_tanks", currencies.CeruleumTanks)
|
|
||||||
.Field("repair_kits", currencies.RepairKits)
|
|
||||||
.Timestamp(date, WritePrecision.S));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,8 +145,186 @@ internal sealed class InfluxStatisticsClient : IDisposable
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PointData> GenerateCharacterStats(Character character, Currencies currencies,
|
||||||
|
StatisticsUpdate update, DateTime date)
|
||||||
|
{
|
||||||
|
update.LocalStats.TryGetValue(character, out LocalStats? localStats);
|
||||||
|
|
||||||
|
bool includeFc = character.FreeCompanyId > 0 &&
|
||||||
|
_configuration.IncludedCharacters.Any(x =>
|
||||||
|
x.LocalContentId == character.CharacterId && x.IncludeFreeCompany);
|
||||||
|
|
||||||
|
Func<string, PointData> pointData = s => PointData.Measurement(s)
|
||||||
|
.Tag("id", character.CharacterId.ToString())
|
||||||
|
.Tag("player_name", character.Name)
|
||||||
|
.Tag("type", character.CharacterType.ToString())
|
||||||
|
.Tag("fc_id", includeFc ? character.FreeCompanyId.ToString() : null)
|
||||||
|
.Timestamp(date, WritePrecision.S);
|
||||||
|
|
||||||
|
yield return pointData("currency")
|
||||||
|
.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);
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
GrandCompany.Maelstrom => currencies.GcSealsMaelstrom,
|
||||||
|
GrandCompany.TwinAdder => currencies.GcSealsTwinAdders,
|
||||||
|
GrandCompany.ImmortalFlames => currencies.GcSealsImmortalFlames,
|
||||||
|
_ => 0,
|
||||||
|
})
|
||||||
|
.Field("seal_cap", localStats.GcRank switch
|
||||||
|
{
|
||||||
|
1 => 10_000,
|
||||||
|
2 => 15_000,
|
||||||
|
3 => 20_000,
|
||||||
|
4 => 25_000,
|
||||||
|
5 => 30_000,
|
||||||
|
6 => 35_000,
|
||||||
|
7 => 40_000,
|
||||||
|
8 => 45_000,
|
||||||
|
9 => 50_000,
|
||||||
|
10 => 80_000,
|
||||||
|
11 => 90_000,
|
||||||
|
_ => 0,
|
||||||
|
})
|
||||||
|
.Field("squadron_unlocked", localStats.SquadronUnlocked ? 1 : 0);
|
||||||
|
|
||||||
|
if (localStats.ClassJobLevels.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (expIndex, abbreviation) in _expToJobs)
|
||||||
|
{
|
||||||
|
var level = localStats.ClassJobLevels[expIndex];
|
||||||
|
if (level > 0)
|
||||||
|
{
|
||||||
|
yield return pointData("experience")
|
||||||
|
.Tag("job", abbreviation)
|
||||||
|
.Field("level", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStats.MsqCount != -1)
|
||||||
|
{
|
||||||
|
yield return pointData("quests")
|
||||||
|
.Tag("msq_name", localStats.MsqName)
|
||||||
|
.Field("msq_count", localStats.MsqCount)
|
||||||
|
.Field("msq_genre", localStats.MsqGenre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
|
||||||
|
yield return inventoryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PointData> GenerateRetainerStats(Character character, Currencies currencies,
|
||||||
|
StatisticsUpdate update, DateTime date)
|
||||||
|
{
|
||||||
|
var owner = update.Currencies.Keys.First(x => x.CharacterId == character.OwnerId);
|
||||||
|
|
||||||
|
Func<string, PointData> pointData = s => PointData.Measurement(s)
|
||||||
|
.Tag("id", character.CharacterId.ToString())
|
||||||
|
.Tag("player_name", owner.Name)
|
||||||
|
.Tag("player_id", character.OwnerId.ToString())
|
||||||
|
.Tag("type", character.CharacterType.ToString())
|
||||||
|
.Tag("retainer_name", character.Name)
|
||||||
|
.Timestamp(date, WritePrecision.S);
|
||||||
|
|
||||||
|
yield return pointData("currency")
|
||||||
|
.Field("gil", currencies.Gil)
|
||||||
|
.Field("ceruleum_tanks", currencies.CeruleumTanks)
|
||||||
|
.Field("repair_kits", currencies.RepairKits);
|
||||||
|
|
||||||
|
if (update.LocalStats.TryGetValue(owner, out var ownerStats) && character.ClassJob != 0)
|
||||||
|
{
|
||||||
|
yield return pointData("retainer")
|
||||||
|
.Tag("class", _classJobNames[character.ClassJob])
|
||||||
|
.Field("level", character.Level)
|
||||||
|
.Field("is_max_level", character.Level == ownerStats.MaxLevel ? 1 : 0)
|
||||||
|
.Field("can_reach_max_level",
|
||||||
|
ownerStats.ClassJobLevels.Count > 0 &&
|
||||||
|
ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] ==
|
||||||
|
ownerStats.MaxLevel
|
||||||
|
? 1
|
||||||
|
: 0)
|
||||||
|
.Field("levels_before_cap",
|
||||||
|
ownerStats.ClassJobLevels.Count > 0
|
||||||
|
? ownerStats.ClassJobLevels[_classJobToArrayIndex[character.ClassJob]] -
|
||||||
|
character.Level
|
||||||
|
: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
|
||||||
|
yield return inventoryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PointData> GenerateInventoryStats(ulong localContentId, StatisticsUpdate update,
|
||||||
|
Func<string, PointData> pointData)
|
||||||
|
{
|
||||||
|
foreach (var (filterName, items) in update.InventoryItems)
|
||||||
|
{
|
||||||
|
foreach (var item in items.Where(x => x.LocalContentId == localContentId)
|
||||||
|
.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())
|
||||||
|
.Tag("item_name", priceInfo.Name)
|
||||||
|
.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<PointData> GenerateFcStats(Character character, Currencies currencies, StatisticsUpdate update,
|
||||||
|
DateTime date)
|
||||||
|
{
|
||||||
|
update.FcStats.TryGetValue(character.CharacterId, out FcStats? fcStats);
|
||||||
|
|
||||||
|
Func<string, PointData> pointData = s => PointData.Measurement(s)
|
||||||
|
.Tag("id", character.CharacterId.ToString())
|
||||||
|
.Tag("fc_name", character.Name)
|
||||||
|
.Tag("type", character.CharacterType.ToString())
|
||||||
|
.Timestamp(date, WritePrecision.S);
|
||||||
|
|
||||||
|
yield return pointData("currency")
|
||||||
|
.Field("gil", currencies.Gil)
|
||||||
|
.Field("fccredit", fcStats?.FcCredits ?? 0)
|
||||||
|
.Field("ceruleum_tanks", currencies.CeruleumTanks)
|
||||||
|
.Field("repair_kits", currencies.RepairKits);
|
||||||
|
|
||||||
|
foreach (var inventoryPoint in GenerateInventoryStats(character.CharacterId, update, pointData))
|
||||||
|
yield return inventoryPoint;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_influxClient?.Dispose();
|
_influxClient?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct PriceInfo
|
||||||
|
{
|
||||||
|
public string Name { get; init; }
|
||||||
|
public uint Normal { get; init; }
|
||||||
|
public uint Hq => Normal + (uint)Math.Ceiling((decimal)Normal / 10);
|
||||||
|
public uint UiCategory { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -66,7 +67,7 @@ public class InfluxPlugin : IDalamudPlugin
|
|||||||
_windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName);
|
_windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName);
|
||||||
_statisticsWindow = new StatisticsWindow();
|
_statisticsWindow = new StatisticsWindow();
|
||||||
_windowSystem.AddWindow(_statisticsWindow);
|
_windowSystem.AddWindow(_statisticsWindow);
|
||||||
_configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration);
|
_configurationWindow = new ConfigurationWindow(_pluginInterface, clientState, _configuration, _allaganToolsIpc);
|
||||||
_configurationWindow.ConfigUpdated += (_, _) => _influxStatisticsClient.UpdateClient();
|
_configurationWindow.ConfigUpdated += (_, _) => _influxStatisticsClient.UpdateClient();
|
||||||
_windowSystem.AddWindow(_configurationWindow);
|
_windowSystem.AddWindow(_configurationWindow);
|
||||||
|
|
||||||
@ -110,6 +111,18 @@ public class InfluxPlugin : IDalamudPlugin
|
|||||||
if (characters.Count == 0)
|
if (characters.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
var update = new StatisticsUpdate
|
var update = new StatisticsUpdate
|
||||||
{
|
{
|
||||||
Currencies = currencies
|
Currencies = currencies
|
||||||
@ -118,6 +131,7 @@ public class InfluxPlugin : IDalamudPlugin
|
|||||||
y.LocalContentId == x.Key.OwnerId ||
|
y.LocalContentId == x.Key.OwnerId ||
|
||||||
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
|
characters.Any(z => y.LocalContentId == z.CharacterId && z.FreeCompanyId == x.Key.CharacterId)))
|
||||||
.ToDictionary(x => x.Key, x => x.Value),
|
.ToDictionary(x => x.Key, x => x.Value),
|
||||||
|
InventoryItems = inventoryItems,
|
||||||
Submarines = _submarineTrackerIpc.GetSubmarineStats(characters),
|
Submarines = _submarineTrackerIpc.GetSubmarineStats(characters),
|
||||||
LocalStats = _localStatsCalculator.GetAllCharacterStats()
|
LocalStats = _localStatsCalculator.GetAllCharacterStats()
|
||||||
.Where(x => characters.Any(y => y.CharacterId == x.Key))
|
.Where(x => characters.Any(y => y.CharacterId == x.Key))
|
||||||
|
@ -8,6 +8,7 @@ namespace Influx;
|
|||||||
internal sealed class StatisticsUpdate
|
internal sealed class StatisticsUpdate
|
||||||
{
|
{
|
||||||
public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; }
|
public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; }
|
||||||
|
public required IReadOnlyDictionary<string, IReadOnlyList<SortingResult>> InventoryItems { get; init; }
|
||||||
public required Dictionary<Character, List<SubmarineStats>> Submarines { get; init; }
|
public required Dictionary<Character, List<SubmarineStats>> Submarines { get; init; }
|
||||||
public required Dictionary<Character, LocalStats> LocalStats { get; init; }
|
public required Dictionary<Character, LocalStats> LocalStats { get; init; }
|
||||||
public required Dictionary<ulong, FcStats> FcStats { get; init; }
|
public required Dictionary<ulong, FcStats> FcStats { get; init; }
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
|
using Dalamud.Interface.Components;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
using Influx.AllaganTools;
|
||||||
|
|
||||||
namespace Influx.Windows;
|
namespace Influx.Windows;
|
||||||
|
|
||||||
@ -14,14 +17,18 @@ internal sealed class ConfigurationWindow : Window
|
|||||||
private readonly DalamudPluginInterface _pluginInterface;
|
private readonly DalamudPluginInterface _pluginInterface;
|
||||||
private readonly IClientState _clientState;
|
private readonly IClientState _clientState;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
|
private readonly AllaganToolsIpc _allaganToolsIpc;
|
||||||
|
private string[] _filterNames = Array.Empty<string>();
|
||||||
|
private int _filterIndexToAdd = 0;
|
||||||
|
|
||||||
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
|
public ConfigurationWindow(DalamudPluginInterface pluginInterface, IClientState clientState,
|
||||||
Configuration configuration)
|
Configuration configuration, AllaganToolsIpc allaganToolsIpc)
|
||||||
: base("Configuration###InfluxConfiguration")
|
: base("Configuration###InfluxConfiguration")
|
||||||
{
|
{
|
||||||
_pluginInterface = pluginInterface;
|
_pluginInterface = pluginInterface;
|
||||||
_clientState = clientState;
|
_clientState = clientState;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_allaganToolsIpc = allaganToolsIpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler? ConfigUpdated;
|
public event EventHandler? ConfigUpdated;
|
||||||
@ -33,9 +40,19 @@ internal sealed class ConfigurationWindow : Window
|
|||||||
{
|
{
|
||||||
DrawConnectionSettings();
|
DrawConnectionSettings();
|
||||||
DrawIncludedCharacters();
|
DrawIncludedCharacters();
|
||||||
|
DrawAllaganToolsFilters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void OnOpen()
|
||||||
|
{
|
||||||
|
_filterNames = _allaganToolsIpc.GetSearchFilters()
|
||||||
|
.Select(x => x.Value)
|
||||||
|
.Order()
|
||||||
|
.ToArray();
|
||||||
|
_filterIndexToAdd = 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawConnectionSettings()
|
private void DrawConnectionSettings()
|
||||||
{
|
{
|
||||||
using var tabItem = ImRaii.TabItem("Connection Settings");
|
using var tabItem = ImRaii.TabItem("Connection Settings");
|
||||||
@ -165,6 +182,64 @@ internal sealed class ConfigurationWindow : Window
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawAllaganToolsFilters()
|
||||||
|
{
|
||||||
|
using var tabItem = ImRaii.TabItem("Inventory Filters");
|
||||||
|
if (!tabItem)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_configuration.IncludedInventoryFilters.Count > 0)
|
||||||
|
{
|
||||||
|
int? indexToRemove = null;
|
||||||
|
|
||||||
|
ImGui.Text("Selected Filters:");
|
||||||
|
ImGui.Indent(30);
|
||||||
|
foreach (var filter in _configuration.IncludedInventoryFilters)
|
||||||
|
{
|
||||||
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Times, $"{filter.Name}"))
|
||||||
|
{
|
||||||
|
indexToRemove = _configuration.IncludedInventoryFilters.IndexOf(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Unindent(30);
|
||||||
|
|
||||||
|
if (indexToRemove != null)
|
||||||
|
{
|
||||||
|
_configuration.IncludedInventoryFilters.RemoveAt(indexToRemove.Value);
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.Text("You are not tracking any AllaganTools filters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Separator();
|
||||||
|
|
||||||
|
if (_filterNames.Length > 0)
|
||||||
|
{
|
||||||
|
ImGui.Combo("Add Search Filter", ref _filterIndexToAdd, _filterNames, _filterNames.Length);
|
||||||
|
|
||||||
|
ImGui.BeginDisabled(_configuration.IncludedInventoryFilters.Any(x => x.Name == _filterNames[_filterIndexToAdd]));
|
||||||
|
if (ImGuiComponents.IconButtonWithText(FontAwesomeIcon.Plus, "Track Filter"))
|
||||||
|
{
|
||||||
|
_configuration.IncludedInventoryFilters.Add(new Configuration.FilterInfo
|
||||||
|
{
|
||||||
|
Name = _filterNames[_filterIndexToAdd],
|
||||||
|
});
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.EndDisabled();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudRed,
|
||||||
|
"You don't have any search filters, or the AllaganTools integration doesn't work.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Save(bool sendEvent = false)
|
private void Save(bool sendEvent = false)
|
||||||
{
|
{
|
||||||
_pluginInterface.SavePluginConfig(_configuration);
|
_pluginInterface.SavePluginConfig(_configuration);
|
||||||
|
Loading…
Reference in New Issue
Block a user