Fix IPC, refactor IPC class

This commit is contained in:
Liza 2023-08-21 19:25:30 +02:00
parent 24879ccdb9
commit 0457a86239
Signed by: liza
GPG Key ID: 7199F8D727D55F67
17 changed files with 309 additions and 240 deletions

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.Gui;
using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Ipc.Exceptions;
using ECommons.Reflection;
namespace Influx.AllaganTools;
internal sealed class AllaganToolsIpc : IDisposable
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly ChatGui _chatGui;
private readonly Configuration _configuration;
private readonly ICallGateSubscriber<bool, bool>? _initalized;
private readonly ICallGateSubscriber<bool>? _isInitialized;
public ICharacterMonitor Characters { get; private set; } = new UnavailableCharacterMonitor();
public IInventoryMonitor Inventories { get; private set; } = new UnavailableInventoryMonitor();
public AllaganToolsIpc(DalamudPluginInterface pluginInterface, ChatGui chatGui, Configuration configuration)
{
_pluginInterface = pluginInterface;
_chatGui = chatGui;
_configuration = configuration;
_initalized = _pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
_isInitialized = _pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
_initalized.Subscribe(ConfigureIpc);
try
{
bool isInitialized = _isInitialized.InvokeFunc();
if (isInitialized)
ConfigureIpc(true);
}
catch (IpcNotReadyError e)
{
PluginLog.Debug(e, "Not initializing ATools yet");
}
}
private void ConfigureIpc(bool initialized)
{
try
{
if (DalamudReflector.TryGetDalamudPlugin("Allagan Tools", out var it, false, true) &&
_isInitialized != null && _isInitialized.InvokeFunc())
{
var pluginService = it.GetType().Assembly.GetType("InventoryTools.PluginService")!;
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
Inventories = new InventoryMonitor(
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
}
}
catch (Exception e)
{
PluginLog.Error(e, "Could not initialize IPC");
_chatGui.PrintError(e.ToString());
}
}
public Dictionary<Character, Currencies> CountCurrencies()
{
var characters = Characters.All.ToDictionary(x => x.CharacterId, x => x);
return Inventories.All
.Where(x => !_configuration.ExcludedCharacters.Contains(x.Key))
.ToDictionary(
x => characters[x.Value.CharacterId],
y =>
{
var inv = new InventoryWrapper(y.Value.GetAllItems());
return new Currencies
{
Gil = inv.Sum(1),
FcCredits = inv.Sum(80),
Ventures = inv.Sum(21072),
CeruleumTanks = inv.Sum(10155),
RepairKits = inv.Sum(10373),
};
});
}
public void Dispose()
{
_initalized?.Unsubscribe(ConfigureIpc);
Characters = new UnavailableCharacterMonitor();
Inventories = new UnavailableInventoryMonitor();
}
private sealed class InventoryWrapper
{
private readonly IEnumerable<InventoryItem> _items;
public InventoryWrapper(IEnumerable<InventoryItem> items)
{
_items = items;
}
public long Sum(int itemId) => _items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
}
}

View File

@ -0,0 +1,26 @@
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class Character
{
private readonly object _delegate;
private readonly FieldInfo _name;
public Character(object @delegate)
{
_delegate = @delegate;
_name = _delegate.GetType().GetField("Name")!;
CharacterId = (ulong)_delegate.GetType().GetField("CharacterId")!.GetValue(_delegate)!;
CharacterType = (CharacterType)_delegate.GetType().GetProperty("CharacterType")!.GetValue(_delegate)!;
OwnerId = (ulong)_delegate.GetType().GetField("OwnerId")!.GetValue(_delegate)!;
FreeCompanyId = (ulong)_delegate.GetType().GetField("FreeCompanyId")!.GetValue(_delegate)!;
}
public ulong CharacterId { get; }
public CharacterType CharacterType { get; }
public ulong OwnerId { get; }
public ulong FreeCompanyId { get; }
public string Name => (string)_name.GetValue(_delegate)!;
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class CharacterMonitor : ICharacterMonitor
{
private readonly object _delegate;
private readonly MethodInfo _getPlayerCharacters;
private readonly MethodInfo _allCharacters;
public CharacterMonitor(object @delegate)
{
_delegate = @delegate;
_getPlayerCharacters = _delegate.GetType().GetMethod("GetPlayerCharacters")!;
_allCharacters = _delegate.GetType().GetMethod("AllCharacters")!;
}
public IEnumerable<Character> PlayerCharacters => GetCharactersInternal(_getPlayerCharacters);
public IEnumerable<Character> All => GetCharactersInternal(_allCharacters);
private IEnumerable<Character> GetCharactersInternal(MethodInfo methodInfo)
{
return ((IEnumerable)methodInfo.Invoke(_delegate, Array.Empty<object>())!)
.Cast<object>()
.Select(x => x.GetType().GetProperty("Value")!.GetValue(x)!)
.Select(x => new Character(x))
.ToList();
}
}

View File

@ -0,0 +1,10 @@
namespace Influx.AllaganTools;
internal enum CharacterType
{
Character,
Retainer,
FreeCompanyChest,
Housing,
Unknown,
}

View File

@ -0,0 +1,11 @@
namespace Influx.AllaganTools;
internal struct Currencies
{
public long Gil { get; init; }
public long GcSeals { get; init; }
public long FcCredits { get; init; }
public long Ventures { get; init; }
public long CeruleumTanks { get; init; }
public long RepairKits { get; init; }
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Influx.AllaganTools;
internal interface ICharacterMonitor
{
IEnumerable<Character> PlayerCharacters { get; }
IEnumerable<Character> All { get; }
}

View File

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Influx.AllaganTools;
internal interface IInventoryMonitor
{
public IReadOnlyDictionary<ulong, Inventory> All { get; }
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class Inventory
{
private readonly object _delegate;
private readonly MethodInfo _getAllInventories;
public Inventory(object @delegate)
{
_delegate = @delegate;
_getAllInventories = _delegate.GetType().GetMethod("GetAllInventories")!;
CharacterId = (ulong)_delegate.GetType().GetProperty("CharacterId")!.GetValue(_delegate)!;
}
public ulong CharacterId { get; }
public IEnumerable<InventoryItem> GetAllItems() =>
((IEnumerable)_getAllInventories.Invoke(_delegate, Array.Empty<object>())!)
.Cast<IEnumerable>()
.SelectMany(x => x.Cast<object>())
.Select(x => new InventoryItem(x))
.ToList();
}

View File

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

View File

@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Influx.AllaganTools;
internal sealed class InventoryMonitor : IInventoryMonitor
{
private readonly object _delegate;
private readonly PropertyInfo _inventories;
public InventoryMonitor(object @delegate)
{
_delegate = @delegate;
_inventories = _delegate.GetType().GetProperty("Inventories")!;
}
public IReadOnlyDictionary<ulong, Inventory> All =>
((IEnumerable)_inventories.GetValue(_delegate)!)
.Cast<object>()
.Select(x => x.GetType().GetProperty("Value")!.GetValue(x)!)
.Select(x => new Inventory(x))
.ToDictionary(x => x.CharacterId, x => x);
}

View File

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
namespace Influx.AllaganTools;
internal sealed class UnavailableCharacterMonitor : ICharacterMonitor
{
public IEnumerable<Character> PlayerCharacters => Array.Empty<Character>();
public IEnumerable<Character> All => Array.Empty<Character>();
}

View File

@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Influx.AllaganTools;
internal sealed class UnavailableInventoryMonitor : IInventoryMonitor
{
public IReadOnlyDictionary<ulong, Inventory> All => new Dictionary<ulong, Inventory>();
}

View File

@ -1,219 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Dalamud.Game.ClientState;
using Dalamud.Game.Gui;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using ECommons.Reflection;
namespace Influx;
internal sealed class AllaganToolsIPC : IDisposable
{
private readonly DalamudPluginInterface _pluginInterface;
private readonly ChatGui _chatGui;
private readonly Configuration _configuration;
private readonly ICallGateSubscriber<bool, bool>? _initalized;
private readonly ICallGateSubscriber<bool>? _isInitialized;
public CharacterMonitor Characters { get; private set; }
public InventoryMonitor Inventories { get; private set; }
public AllaganToolsIPC(DalamudPluginInterface pluginInterface, ChatGui chatGui, Configuration configuration)
{
_pluginInterface = pluginInterface;
_chatGui = chatGui;
_configuration = configuration;
_initalized = _pluginInterface.GetIpcSubscriber<bool, bool>("AllaganTools.Initialized");
_isInitialized = _pluginInterface.GetIpcSubscriber<bool>("AllaganTools.IsInitialized");
_initalized.Subscribe(ConfigureIpc);
ConfigureIpc(true);
}
private void ConfigureIpc(bool initialized)
{
try
{
if (DalamudReflector.TryGetDalamudPlugin("Allagan Tools", out var it, false, true) &&
_isInitialized != null && _isInitialized.InvokeFunc())
{
var pluginService = it.GetType().Assembly.GetType("InventoryTools.PluginService")!;
Characters = new CharacterMonitor(pluginService.GetProperty("CharacterMonitor")!.GetValue(null)!);
Inventories = new InventoryMonitor(
pluginService.GetProperty("InventoryMonitor")!.GetValue(null)!);
}
}
catch (Exception e)
{
_chatGui.PrintError(e.ToString());
}
}
public Dictionary<Character, Currencies> CountCurrencies()
{
var characters = Characters.All.ToDictionary(x => x.CharacterId, x => x);
return Inventories.All
.Where(x => !_configuration.ExcludedCharacters.Contains(x.Key))
.ToDictionary(
x => characters[x.Value.CharacterId],
y =>
{
var inv = new InventoryWrapper(y.Value.GetAllItems());
return new Currencies
{
Gil = inv.Sum(1),
FcCredits = inv.Sum(80),
Ventures = inv.Sum(21072),
CeruleumTanks = inv.Sum(10155),
RepairKits = inv.Sum(10373),
};
});
}
public void Dispose()
{
_initalized?.Unsubscribe(ConfigureIpc);
}
public class CharacterMonitor
{
private readonly object _delegate;
private readonly MethodInfo _getPlayerCharacters;
private readonly MethodInfo _allCharacters;
public CharacterMonitor(object @delegate)
{
_delegate = @delegate;
_getPlayerCharacters = _delegate.GetType().GetMethod("GetPlayerCharacters")!;
_allCharacters = _delegate.GetType().GetMethod("AllCharacters")!;
}
public IEnumerable<Character> PlayerCharacters => GetCharactersInternal(_getPlayerCharacters);
public IEnumerable<Character> All => GetCharactersInternal(_allCharacters);
private IEnumerable<Character> GetCharactersInternal(MethodInfo methodInfo)
{
return ((IEnumerable)methodInfo.Invoke(_delegate, Array.Empty<object>())!)
.Cast<object>()
.Select(x => x.GetType().GetProperty("Value")!.GetValue(x)!)
.Select(x => new Character(x))
.ToList();
}
}
public class Character
{
private readonly object _delegate;
private readonly FieldInfo _name;
public Character(object @delegate)
{
_delegate = @delegate;
_name = _delegate.GetType().GetField("Name")!;
CharacterId = (ulong)_delegate.GetType().GetField("CharacterId")!.GetValue(_delegate)!;
CharacterType = (CharacterType)_delegate.GetType().GetProperty("CharacterType")!.GetValue(_delegate)!;
OwnerId = (ulong)_delegate.GetType().GetField("OwnerId")!.GetValue(_delegate)!;
FreeCompanyId = (ulong)_delegate.GetType().GetField("FreeCompanyId")!.GetValue(_delegate)!;
}
public ulong CharacterId { get; }
public CharacterType CharacterType { get; }
public ulong OwnerId { get; }
public ulong FreeCompanyId { get; }
public string Name => (string)_name.GetValue(_delegate)!;
}
public enum CharacterType
{
Character,
Retainer,
FreeCompanyChest,
Housing,
Unknown,
}
public struct Currencies
{
public long Gil { get; init; }
public long GcSeals { get; init; }
public long FcCredits { get; init; }
public long Ventures { get; init; }
public long CeruleumTanks { get; init; }
public long RepairKits { get; init; }
}
public sealed class InventoryMonitor
{
private readonly object _delegate;
private readonly PropertyInfo _inventories;
public InventoryMonitor(object @delegate)
{
_delegate = @delegate;
_inventories = _delegate.GetType().GetProperty("Inventories")!;
}
public IReadOnlyDictionary<ulong, Inventory> All =>
((IEnumerable)_inventories.GetValue(_delegate)!)
.Cast<object>()
.Select(x => x.GetType().GetProperty("Value")!.GetValue(x)!)
.Select(x => new Inventory(x))
.ToDictionary(x => x.CharacterId, x => x);
}
public sealed class Inventory
{
private readonly object _delegate;
private readonly MethodInfo _getAllInventories;
public Inventory(object @delegate)
{
_delegate = @delegate;
_getAllInventories = _delegate.GetType().GetMethod("GetAllInventories")!;
CharacterId = (ulong)_delegate.GetType().GetProperty("CharacterId")!.GetValue(_delegate)!;
}
public ulong CharacterId { get; }
public IEnumerable<InventoryItem> GetAllItems() =>
((IEnumerable)_getAllInventories.Invoke(_delegate, Array.Empty<object>())!)
.Cast<IEnumerable>()
.SelectMany(x => x.Cast<object>())
.Select(x => new InventoryItem(x))
.ToList();
}
public sealed class InventoryItem
{
private readonly object _delegate;
public InventoryItem(object @delegate)
{
_delegate = @delegate;
ItemId = (uint)_delegate.GetType().GetField("ItemId")!.GetValue(_delegate)!;
Quantity = (uint)_delegate.GetType().GetField("Quantity")!.GetValue(_delegate)!;
}
public uint ItemId { get; }
public uint Quantity { get; }
}
public sealed class InventoryWrapper
{
private readonly IEnumerable<InventoryItem> _items;
public InventoryWrapper(IEnumerable<InventoryItem> items)
{
_items = items;
}
public long Sum(int itemId) => _items.Where(x => x.ItemId == itemId).Sum(x => x.Quantity);
}
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using Influx.AllaganTools;
using InfluxDB.Client; using InfluxDB.Client;
using InfluxDB.Client.Api.Domain; using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Writes; using InfluxDB.Client.Writes;
@ -36,10 +37,10 @@ internal class InfluxStatisticsClient : IDisposable
return; return;
DateTime date = DateTime.UtcNow; DateTime date = DateTime.UtcNow;
IReadOnlyDictionary<AllaganToolsIPC.Character, AllaganToolsIPC.Currencies> stats = update.Currencies; IReadOnlyDictionary<Character, Currencies> stats = update.Currencies;
var validFcIds = stats.Keys var validFcIds = stats.Keys
.Where(x => x.CharacterType == AllaganToolsIPC.CharacterType.Character) .Where(x => x.CharacterType == CharacterType.Character)
.Select(x => x.FreeCompanyId) .Select(x => x.FreeCompanyId)
.ToList(); .ToList();
Task.Run(async () => Task.Run(async () =>
@ -49,7 +50,7 @@ internal class InfluxStatisticsClient : IDisposable
List<PointData> values = new(); List<PointData> values = new();
foreach (var (character, currencies) in stats) foreach (var (character, currencies) in stats)
{ {
if (character.CharacterType == AllaganToolsIPC.CharacterType.Character) if (character.CharacterType == CharacterType.Character)
{ {
values.Add(PointData.Measurement("currency") values.Add(PointData.Measurement("currency")
.Tag("id", character.CharacterId.ToString()) .Tag("id", character.CharacterId.ToString())
@ -61,7 +62,7 @@ internal class InfluxStatisticsClient : IDisposable
.Field("repair_kits", currencies.RepairKits) .Field("repair_kits", currencies.RepairKits)
.Timestamp(date, WritePrecision.S)); .Timestamp(date, WritePrecision.S));
} }
else if (character.CharacterType == AllaganToolsIPC.CharacterType.Retainer) else if (character.CharacterType == CharacterType.Retainer)
{ {
var owner = stats.Keys.First(x => x.CharacterId == character.OwnerId); var owner = stats.Keys.First(x => x.CharacterId == character.OwnerId);
values.Add(PointData.Measurement("currency") values.Add(PointData.Measurement("currency")
@ -74,7 +75,7 @@ internal class InfluxStatisticsClient : IDisposable
.Field("repair_kits", currencies.RepairKits) .Field("repair_kits", currencies.RepairKits)
.Timestamp(date, WritePrecision.S)); .Timestamp(date, WritePrecision.S));
} }
else if (character.CharacterType == AllaganToolsIPC.CharacterType.FreeCompanyChest && else if (character.CharacterType == CharacterType.FreeCompanyChest &&
validFcIds.Contains(character.CharacterId)) validFcIds.Contains(character.CharacterId))
{ {
values.Add(PointData.Measurement("currency") values.Add(PointData.Measurement("currency")

View File

@ -1,9 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Dalamud.Game.ClientState; using Dalamud.Game.ClientState;
using Dalamud.Game.Command; using Dalamud.Game.Command;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
@ -11,11 +8,9 @@ using Dalamud.Interface.Windowing;
using Dalamud.Logging; using Dalamud.Logging;
using Dalamud.Plugin; using Dalamud.Plugin;
using ECommons; using ECommons;
using Influx.AllaganTools;
using Influx.Influx; using Influx.Influx;
using Influx.Windows; using Influx.Windows;
using InfluxDB.Client;
using InfluxDB.Client.Api.Domain;
using InfluxDB.Client.Writes;
namespace Influx; namespace Influx;
@ -28,7 +23,7 @@ public class InfluxPlugin : IDalamudPlugin
private readonly Configuration _configuration; private readonly Configuration _configuration;
private readonly ClientState _clientState; private readonly ClientState _clientState;
private readonly CommandManager _commandManager; private readonly CommandManager _commandManager;
private readonly AllaganToolsIPC _allaganToolsIpc; private readonly AllaganToolsIpc _allaganToolsIpc;
private readonly InfluxStatisticsClient _influxStatisticsClient; private readonly InfluxStatisticsClient _influxStatisticsClient;
private readonly WindowSystem _windowSystem; private readonly WindowSystem _windowSystem;
private readonly StatisticsWindow _statisticsWindow; private readonly StatisticsWindow _statisticsWindow;
@ -44,7 +39,7 @@ public class InfluxPlugin : IDalamudPlugin
_configuration = LoadConfig(); _configuration = LoadConfig();
_clientState = clientState; _clientState = clientState;
_commandManager = commandManager; _commandManager = commandManager;
_allaganToolsIpc = new AllaganToolsIPC(pluginInterface, chatGui, _configuration); _allaganToolsIpc = new AllaganToolsIpc(pluginInterface, chatGui, _configuration);
_influxStatisticsClient = new InfluxStatisticsClient(chatGui, _configuration); _influxStatisticsClient = new InfluxStatisticsClient(chatGui, _configuration);
_windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName); _windowSystem = new WindowSystem(typeof(InfluxPlugin).FullName);

View File

@ -1,8 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using Influx.AllaganTools;
namespace Influx; namespace Influx;
internal sealed class StatisticsUpdate internal sealed class StatisticsUpdate
{ {
public IReadOnlyDictionary<AllaganToolsIPC.Character, AllaganToolsIPC.Currencies> Currencies { get; init; } public required IReadOnlyDictionary<Character, Currencies> Currencies { get; init; }
} }

View File

@ -1,9 +1,9 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Dalamud.Interface.Windowing; using Dalamud.Interface.Windowing;
using ImGuiNET; using ImGuiNET;
using Influx.AllaganTools;
namespace Influx.Windows; namespace Influx.Windows;
@ -46,14 +46,14 @@ internal sealed class StatisticsWindow : Window
public void OnStatisticsUpdate(StatisticsUpdate update) public void OnStatisticsUpdate(StatisticsUpdate update)
{ {
var retainers = update.Currencies var retainers = update.Currencies
.Where(x => x.Key.CharacterType == AllaganToolsIPC.CharacterType.Retainer) .Where(x => x.Key.CharacterType == CharacterType.Retainer)
.GroupBy(x => update.Currencies.FirstOrDefault(y => y.Key.CharacterId == x.Key.OwnerId).Key) .GroupBy(x => update.Currencies.FirstOrDefault(y => y.Key.CharacterId == x.Key.OwnerId).Key)
.ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList()); .ToDictionary(x => x.Key, x => x.Select(y => y.Value).ToList());
_rows = update.Currencies.Where(x => x.Key.CharacterType == AllaganToolsIPC.CharacterType.Character) _rows = update.Currencies.Where(x => x.Key.CharacterType == CharacterType.Character)
.Select(x => .Select(x =>
{ {
var currencies = new List<AllaganToolsIPC.Currencies> { x.Value }; var currencies = new List<Currencies> { x.Value };
if (retainers.TryGetValue(x.Key, out var retainerCurrencies)) if (retainers.TryGetValue(x.Key, out var retainerCurrencies))
currencies.AddRange(retainerCurrencies); currencies.AddRange(retainerCurrencies);
return new StatisticsRow return new StatisticsRow
@ -71,8 +71,8 @@ internal sealed class StatisticsWindow : Window
public sealed class StatisticsRow public sealed class StatisticsRow
{ {
public string Name { get; init; } public required string Name { get; init; }
public string Type { get; init; } public required string Type { get; init; }
public long Gil { get; init; } public long Gil { get; init; }
public long FcCredits { get; init; } public long FcCredits { get; init; }
} }