This commit is contained in:
Liza 2023-10-05 17:32:42 +02:00
parent 48697d7c1e
commit 5bc751e31b
Signed by: liza
GPG Key ID: 7199F8D727D55F67
9 changed files with 129 additions and 138 deletions

View File

@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures; using Dalamud.Utility.Signatures;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -35,13 +36,13 @@ namespace RetainerTrack.Handlers
#pragma warning restore CS0649 #pragma warning restore CS0649
public GameHooks(ILogger<GameHooks> logger, PersistenceContext persistenceContext) public GameHooks(ILogger<GameHooks> logger, PersistenceContext persistenceContext, IGameInteropProvider gameInteropProvider)
{ {
_logger = logger; _logger = logger;
_persistenceContext = persistenceContext; _persistenceContext = persistenceContext;
_logger.LogDebug("Initializing game hooks"); _logger.LogDebug("Initializing game hooks");
SignatureHelper.Initialise(this); gameInteropProvider.InitializeFromAttributes(this);
CharacterNameResultHook.Enable(); CharacterNameResultHook.Enable();
SocialListResultHook.Enable(); SocialListResultHook.Enable();

View File

@ -0,0 +1,79 @@
using System;
using System.Threading.Tasks;
using Dalamud.Game.Network.Structures;
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Info;
using Microsoft.Extensions.Logging;
namespace RetainerTrack.Handlers
{
internal sealed class MarketBoardOfferingsHandler : IDisposable
{
private unsafe delegate void* MarketBoardOfferings(InfoProxyItemSearch* a1, nint packetData);
private readonly ILogger<MarketBoardOfferingsHandler> _logger;
private readonly IClientState _clientState;
private readonly PersistenceContext _persistenceContext;
private readonly Hook<MarketBoardOfferings> _marketBoardOfferingsHook;
public unsafe MarketBoardOfferingsHandler(
ILogger<MarketBoardOfferingsHandler> logger,
IClientState clientState,
IGameGui gameGui,
IGameInteropProvider gameInteropProvider,
PersistenceContext persistenceContext)
{
_logger = logger;
_clientState = clientState;
_persistenceContext = persistenceContext;
_logger.LogDebug("Setting up offerings hook");
var uiModule = (UIModule*)gameGui.GetUIModule();
var infoModule = uiModule->GetInfoModule();
var proxy = infoModule->GetInfoProxyById(11);
_marketBoardOfferingsHook =
gameInteropProvider.HookFromAddress<MarketBoardOfferings>((nint)proxy->vtbl[12],
MarketBoardOfferingsDetour);
_marketBoardOfferingsHook.Enable();
_logger.LogDebug("Offerings hook enabled successfully");
}
public void Dispose()
{
_marketBoardOfferingsHook.Dispose();
}
// adapted from https://github.com/tesu/PennyPincher/commit/0f9b3963fd4a6e9b87f585ee491d4de59a93f7a3
private unsafe void* MarketBoardOfferingsDetour(InfoProxyItemSearch* a1, nint packetData)
{
try
{
if (packetData != nint.Zero)
{
ParseOfferings(packetData);
}
}
catch (Exception e)
{
_logger.LogError(e, "Could not parse marketboard offerings.");
}
return _marketBoardOfferingsHook.Original(a1, packetData);
}
private void ParseOfferings(nint dataPtr)
{
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
if (worldId == 0)
{
_logger.LogInformation("Skipping market board handler, current world unknown");
return;
}
var listings = MarketBoardCurrentOfferings.Read(dataPtr);
Task.Run(() => _persistenceContext.HandleMarketBoardPage(listings, worldId));
}
}
}

View File

@ -1,7 +1,8 @@
using System; using System;
using Dalamud.Game; using Dalamud.Game.Addon.Lifecycle;
using Dalamud.Game.Gui; using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
using Dalamud.Hooking; using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI; using FFXIVClientStructs.FFXIV.Component.GUI;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -10,55 +11,33 @@ namespace RetainerTrack.Handlers
{ {
internal sealed unsafe class MarketBoardUiHandler : IDisposable internal sealed unsafe class MarketBoardUiHandler : IDisposable
{ {
private const string AddonName = "ItemSearchResult";
private readonly ILogger<MarketBoardUiHandler> _logger; private readonly ILogger<MarketBoardUiHandler> _logger;
private readonly Framework _framework;
private readonly GameGui _gameGui;
private readonly PersistenceContext _persistenceContext; private readonly PersistenceContext _persistenceContext;
private readonly IAddonLifecycle _addonLifecycle;
private Hook<Draw>? _drawHook;
private delegate void Draw(AtkUnitBase* addon);
public MarketBoardUiHandler( public MarketBoardUiHandler(
ILogger<MarketBoardUiHandler> logger, ILogger<MarketBoardUiHandler> logger,
Framework framework, PersistenceContext persistenceContext,
GameGui gameGui, IAddonLifecycle addonLifecycle)
PersistenceContext persistenceContext)
{ {
_logger = logger; _logger = logger;
_framework = framework;
_gameGui = gameGui;
_persistenceContext = persistenceContext; _persistenceContext = persistenceContext;
_addonLifecycle = addonLifecycle;
_framework.Update += FrameworkUpdate; _addonLifecycle.RegisterListener(AddonEvent.PreDraw, AddonName, PreDraw);
} }
private AddonItemSearchResult* ItemSearchResult => (AddonItemSearchResult*)_gameGui.GetAddonByName("ItemSearchResult"); private void PreDraw(AddonEvent type, AddonArgs args)
private void FrameworkUpdate(Framework framework)
{ {
var addon = ItemSearchResult; UpdateRetainerNames((AddonItemSearchResult*)args.Addon);
if (addon == null)
return;
_drawHook ??= Hook<Draw>.FromAddress(
new nint(addon->AtkUnitBase.AtkEventListener.vfunc[42]),
AddonDraw);
_drawHook.Enable();
_framework.Update -= FrameworkUpdate;
} }
private void AddonDraw(AtkUnitBase* addon) private void UpdateRetainerNames(AddonItemSearchResult* addon)
{
UpdateRetainerNames();
_drawHook!.Original(addon);
}
private void UpdateRetainerNames()
{ {
try try
{ {
var addon = ItemSearchResult;
if (addon == null || !addon->AtkUnitBase.IsVisible) if (addon == null || !addon->AtkUnitBase.IsVisible)
return; return;
@ -95,8 +74,7 @@ namespace RetainerTrack.Handlers
public void Dispose() public void Dispose()
{ {
_drawHook?.Dispose(); _addonLifecycle.UnregisterListener(AddonEvent.PreDraw, AddonName, PreDraw);
_framework.Update -= FrameworkUpdate;
} }
} }
} }

View File

@ -1,61 +0,0 @@
using System;
using System.Threading.Tasks;
using Dalamud.Data;
using Dalamud.Game.ClientState;
using Dalamud.Game.Network;
using Dalamud.Game.Network.Structures;
using Microsoft.Extensions.Logging;
using Framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework;
namespace RetainerTrack.Handlers
{
internal sealed class NetworkHandler : IDisposable
{
private readonly ILogger<NetworkHandler> _logger;
private readonly GameNetwork _gameNetwork;
private readonly DataManager _dataManager;
private readonly ClientState _clientState;
private readonly PersistenceContext _persistenceContext;
public NetworkHandler(
ILogger<NetworkHandler> logger,
GameNetwork gameNetwork,
DataManager dataManager,
ClientState clientState,
PersistenceContext persistenceContext)
{
_logger = logger;
_gameNetwork = gameNetwork;
_dataManager = dataManager;
_clientState = clientState;
_persistenceContext = persistenceContext;
_gameNetwork.NetworkMessage += NetworkMessage;
}
public void Dispose()
{
_gameNetwork.NetworkMessage -= NetworkMessage;
}
private void NetworkMessage(nint dataPtr, ushort opcode, uint sourceActorId, uint targetActorId,
NetworkMessageDirection direction)
{
if (direction != NetworkMessageDirection.ZoneDown || !_dataManager.IsDataReady)
return;
if (opcode == _dataManager.ServerOpCodes["MarketBoardOfferings"])
{
ushort worldId = (ushort?)_clientState.LocalPlayer?.CurrentWorld.Id ?? 0;
if (worldId == 0)
{
_logger.LogInformation("Skipping market board handler, current world unknown");
return;
}
var listings = MarketBoardCurrentOfferings.Read(dataPtr);
Task.Run(() => _persistenceContext.HandleMarketBoardPage(listings, worldId));
}
}
}
}

View File

@ -1,22 +1,21 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Memory; using Dalamud.Memory;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Group; using FFXIVClientStructs.FFXIV.Client.Game.Group;
namespace RetainerTrack.Handlers namespace RetainerTrack.Handlers
{ {
internal sealed class PartyHandler : IDisposable internal sealed class PartyHandler : IDisposable
{ {
private readonly Framework _framework; private readonly IFramework _framework;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly PersistenceContext _persistenceContext; private readonly PersistenceContext _persistenceContext;
private long _lastUpdate = 0; private long _lastUpdate = 0;
public PartyHandler(Framework framework, ClientState clientState, PersistenceContext persistenceContext) public PartyHandler(IFramework framework, IClientState clientState, PersistenceContext persistenceContext)
{ {
_framework = framework; _framework = framework;
_clientState = clientState; _clientState = clientState;
@ -25,7 +24,7 @@ namespace RetainerTrack.Handlers
_framework.Update += FrameworkUpdate; _framework.Update += FrameworkUpdate;
} }
private unsafe void FrameworkUpdate(Framework _) private unsafe void FrameworkUpdate(IFramework _)
{ {
long now = Environment.TickCount64; long now = Environment.TickCount64;
if (!_clientState.IsLoggedIn || _clientState.IsPvPExcludingDen || now - _lastUpdate < 180_000) if (!_clientState.IsLoggedIn || _clientState.IsPvPExcludingDen || now - _lastUpdate < 180_000)

View File

@ -2,8 +2,8 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Dalamud.Game.ClientState;
using Dalamud.Game.Network.Structures; using Dalamud.Game.Network.Structures;
using Dalamud.Plugin.Services;
using LiteDB; using LiteDB;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using RetainerTrack.Database; using RetainerTrack.Database;
@ -13,12 +13,12 @@ namespace RetainerTrack.Handlers
internal sealed class PersistenceContext internal sealed class PersistenceContext
{ {
private readonly ILogger<PersistenceContext> _logger; private readonly ILogger<PersistenceContext> _logger;
private readonly ClientState _clientState; private readonly IClientState _clientState;
private readonly LiteDatabase _liteDatabase; private readonly LiteDatabase _liteDatabase;
private readonly ConcurrentDictionary<uint, ConcurrentDictionary<string, ulong>> _worldRetainerCache = new(); private readonly ConcurrentDictionary<uint, ConcurrentDictionary<string, ulong>> _worldRetainerCache = new();
private readonly ConcurrentDictionary<ulong, string> _playerNameCache = new(); private readonly ConcurrentDictionary<ulong, string> _playerNameCache = new();
public PersistenceContext(ILogger<PersistenceContext> logger, ClientState clientState, public PersistenceContext(ILogger<PersistenceContext> logger, IClientState clientState,
LiteDatabase liteDatabase) LiteDatabase liteDatabase)
{ {
_logger = logger; _logger = logger;

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Version>1.0</Version> <Version>2.0</Version>
<LangVersion>11.0</LangVersion> <LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
@ -25,9 +25,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="1.0.0" /> <PackageReference Include="Dalamud.Extensions.MicrosoftLogging" Version="2.0.0" />
<PackageReference Include="DalamudPackager" Version="2.1.10" /> <PackageReference Include="DalamudPackager" Version="2.1.12" />
<PackageReference Include="LiteDB" Version="5.0.15" /> <PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,11 +1,7 @@
using System.IO; using System.IO;
using Dalamud.Data;
using Dalamud.Extensions.MicrosoftLogging; using Dalamud.Extensions.MicrosoftLogging;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Gui;
using Dalamud.Game.Network;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using LiteDB; using LiteDB;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -19,27 +15,26 @@ namespace RetainerTrack
{ {
private readonly ServiceProvider? _serviceProvider; private readonly ServiceProvider? _serviceProvider;
public string Name => "RetainerTrack";
public RetainerTrackPlugin( public RetainerTrackPlugin(
DalamudPluginInterface pluginInterface, DalamudPluginInterface pluginInterface,
GameNetwork gameNetwork, IFramework framework,
DataManager dataManager, IClientState clientState,
Framework framework, IGameGui gameGui,
ClientState clientState, IGameInteropProvider gameInteropProvider,
GameGui gameGui) IAddonLifecycle addonLifecycle,
IPluginLog pluginLog)
{ {
ServiceCollection serviceCollection = new(); ServiceCollection serviceCollection = new();
serviceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace) serviceCollection.AddLogging(builder => builder.SetMinimumLevel(LogLevel.Trace)
.ClearProviders() .ClearProviders()
.AddDalamudLogger(this)); .AddDalamudLogger(pluginLog));
serviceCollection.AddSingleton<IDalamudPlugin>(this); serviceCollection.AddSingleton<IDalamudPlugin>(this);
serviceCollection.AddSingleton(pluginInterface); serviceCollection.AddSingleton(pluginInterface);
serviceCollection.AddSingleton(gameNetwork);
serviceCollection.AddSingleton(dataManager);
serviceCollection.AddSingleton(framework); serviceCollection.AddSingleton(framework);
serviceCollection.AddSingleton(clientState); serviceCollection.AddSingleton(clientState);
serviceCollection.AddSingleton(gameGui); serviceCollection.AddSingleton(gameGui);
serviceCollection.AddSingleton(gameInteropProvider);
serviceCollection.AddSingleton(addonLifecycle);
serviceCollection.AddSingleton<LiteDatabase>(_ => serviceCollection.AddSingleton<LiteDatabase>(_ =>
new LiteDatabase(new ConnectionString new LiteDatabase(new ConnectionString
@ -50,7 +45,7 @@ namespace RetainerTrack
})); }));
serviceCollection.AddSingleton<PersistenceContext>(); serviceCollection.AddSingleton<PersistenceContext>();
serviceCollection.AddSingleton<NetworkHandler>(); serviceCollection.AddSingleton<MarketBoardOfferingsHandler>();
serviceCollection.AddSingleton<PartyHandler>(); serviceCollection.AddSingleton<PartyHandler>();
serviceCollection.AddSingleton<MarketBoardUiHandler>(); serviceCollection.AddSingleton<MarketBoardUiHandler>();
serviceCollection.AddSingleton<GameHooks>(); serviceCollection.AddSingleton<GameHooks>();
@ -64,7 +59,7 @@ namespace RetainerTrack
.EnsureIndex(x => x.Id); .EnsureIndex(x => x.Id);
_serviceProvider.GetRequiredService<PartyHandler>(); _serviceProvider.GetRequiredService<PartyHandler>();
_serviceProvider.GetRequiredService<NetworkHandler>(); _serviceProvider.GetRequiredService<MarketBoardOfferingsHandler>();
_serviceProvider.GetRequiredService<MarketBoardUiHandler>(); _serviceProvider.GetRequiredService<MarketBoardUiHandler>();
_serviceProvider.GetRequiredService<GameHooks>(); _serviceProvider.GetRequiredService<GameHooks>();
} }

View File

@ -4,24 +4,24 @@
"net7.0-windows7.0": { "net7.0-windows7.0": {
"Dalamud.Extensions.MicrosoftLogging": { "Dalamud.Extensions.MicrosoftLogging": {
"type": "Direct", "type": "Direct",
"requested": "[1.0.0, )", "requested": "[2.0.0, )",
"resolved": "1.0.0", "resolved": "2.0.0",
"contentHash": "nPjMrT9n9GJ+TYF1lyVhlvhmFyN4ajMX2ccclgyMc8MNpOGZwxrJ4VEtrUUk7UkuX2wAhtnNsjrcf5sER3/CbA==", "contentHash": "qp2idn5GuPouUxHHFytMrorbhlcupsgPdO87HjxlBfTY+JID+qoTfPmA5V6HBP1a4DuXGPbk4JtoO/hMmnQrtw==",
"dependencies": { "dependencies": {
"Microsoft.Extensions.Logging": "7.0.0" "Microsoft.Extensions.Logging": "7.0.0"
} }
}, },
"DalamudPackager": { "DalamudPackager": {
"type": "Direct", "type": "Direct",
"requested": "[2.1.10, )", "requested": "[2.1.12, )",
"resolved": "2.1.10", "resolved": "2.1.12",
"contentHash": "S6NrvvOnLgT4GDdgwuKVJjbFo+8ZEj+JsEYk9ojjOR/MMfv1dIFpT8aRJQfI24rtDcw1uF+GnSSMN4WW1yt7fw==" "contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg=="
}, },
"LiteDB": { "LiteDB": {
"type": "Direct", "type": "Direct",
"requested": "[5.0.15, )", "requested": "[5.0.17, )",
"resolved": "5.0.15", "resolved": "5.0.17",
"contentHash": "nucyfCOGSATH553BxplxExP3BOqEwmHt0B57426EIaQjD3CC1Odb52VVCGgTxyYaD2oe3B/cJk8jDo6XiBJqPg==" "contentHash": "cKPvkdlzIts3ZKu/BzoIc/Y71e4VFKlij4LyioPFATZMot+wB7EAm1FFbZSJez6coJmQUoIg/3yHE1MMU+zOdg=="
}, },
"Microsoft.Extensions.DependencyInjection": { "Microsoft.Extensions.DependencyInjection": {
"type": "Direct", "type": "Direct",