mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 20:33:40 +01:00
Remove internal dependencies on opcodes (#1464)
- Removes the opcode lists from internal API entirely - Move NetworkHandlers to use packet handler sigs - Remove opcode data from NetworkMonitorWidget
This commit is contained in:
parent
59606ff854
commit
2083ccda00
7 changed files with 417 additions and 221 deletions
|
|
@ -37,26 +37,10 @@ internal sealed class DataManager : IDisposable, IServiceType, IDataManager
|
||||||
{
|
{
|
||||||
this.Language = (ClientLanguage)dalamud.StartInfo.Language;
|
this.Language = (ClientLanguage)dalamud.StartInfo.Language;
|
||||||
|
|
||||||
// Set up default values so plugins do not null-reference when data is being loaded.
|
|
||||||
this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
|
|
||||||
|
|
||||||
var baseDir = dalamud.AssetDirectory.FullName;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Log.Verbose("Starting data load...");
|
Log.Verbose("Starting data load...");
|
||||||
|
|
||||||
var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
|
||||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")))!;
|
|
||||||
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
|
|
||||||
|
|
||||||
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
|
|
||||||
|
|
||||||
var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
|
||||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")))!;
|
|
||||||
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
|
|
||||||
|
|
||||||
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
|
|
||||||
|
|
||||||
using (Timings.Start("Lumina Init"))
|
using (Timings.Start("Lumina Init"))
|
||||||
{
|
{
|
||||||
var luminaOptions = new LuminaOptions
|
var luminaOptions = new LuminaOptions
|
||||||
|
|
@ -130,17 +114,6 @@ internal sealed class DataManager : IDisposable, IServiceType, IDataManager
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ClientLanguage Language { get; private set; }
|
public ClientLanguage Language { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of server opcodes.
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyDictionary<string, ushort> ServerOpCodes { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of client opcodes.
|
|
||||||
/// </summary>
|
|
||||||
[UsedImplicitly]
|
|
||||||
public ReadOnlyDictionary<string, ushort> ClientOpCodes { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public GameData GameData { get; private set; }
|
public GameData GameData { get; private set; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reactive.Concurrency;
|
using System.Reactive.Concurrency;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
|
@ -14,7 +12,9 @@ using Dalamud.Game.Gui;
|
||||||
using Dalamud.Game.Network.Internal.MarketBoardUploaders;
|
using Dalamud.Game.Network.Internal.MarketBoardUploaders;
|
||||||
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
|
using Dalamud.Game.Network.Internal.MarketBoardUploaders.Universalis;
|
||||||
using Dalamud.Game.Network.Structures;
|
using Dalamud.Game.Network.Structures;
|
||||||
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -24,16 +24,30 @@ namespace Dalamud.Game.Network.Internal;
|
||||||
/// This class handles network notifications and uploading market board data.
|
/// This class handles network notifications and uploading market board data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class NetworkHandlers : IDisposable, IServiceType
|
internal unsafe class NetworkHandlers : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
private readonly IMarketBoardUploader uploader;
|
private readonly IMarketBoardUploader uploader;
|
||||||
|
|
||||||
private readonly IObservable<NetworkMessage> messages;
|
private readonly IObservable<MarketBoardPurchase> mbPurchaseObservable;
|
||||||
|
private readonly IObservable<MarketBoardHistory> mbHistoryObservable;
|
||||||
|
private readonly IObservable<MarketTaxRates> mbTaxesObservable;
|
||||||
|
private readonly IObservable<MarketBoardItemRequest> mbItemRequestObservable;
|
||||||
|
private readonly IObservable<MarketBoardCurrentOfferings> mbOfferingsObservable;
|
||||||
|
private readonly IObservable<MarketBoardPurchaseHandler> mbPurchaseSentObservable;
|
||||||
|
|
||||||
private readonly IDisposable handleMarketBoardItemRequest;
|
private readonly IDisposable handleMarketBoardItemRequest;
|
||||||
private readonly IDisposable handleMarketTaxRates;
|
private readonly IDisposable handleMarketTaxRates;
|
||||||
private readonly IDisposable handleMarketBoardPurchaseHandler;
|
private readonly IDisposable handleMarketBoardPurchaseHandler;
|
||||||
private readonly IDisposable handleCfPop;
|
|
||||||
|
private readonly NetworkHandlersAddressResolver addressResolver;
|
||||||
|
|
||||||
|
private readonly Hook<CfPopDelegate> cfPopHook;
|
||||||
|
private readonly Hook<MarketBoardPurchasePacketHandler> mbPurchaseHook;
|
||||||
|
private readonly Hook<MarketBoardHistoryPacketHandler> mbHistoryHook;
|
||||||
|
private readonly Hook<CustomTalkReceiveResponse> customTalkHook; // used for marketboard taxes
|
||||||
|
private readonly Hook<MarketBoardItemRequestStartPacketHandler> mbItemRequestStartHook;
|
||||||
|
private readonly Hook<InfoProxyItemSearchAddPage> mbOfferingsHook;
|
||||||
|
private readonly Hook<MarketBoardSendPurchaseRequestPacket> mbSendPurchaseRequestHook;
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
@ -41,42 +55,156 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
private bool disposing;
|
private bool disposing;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private NetworkHandlers(GameNetwork gameNetwork)
|
private NetworkHandlers(GameNetwork gameNetwork, TargetSigScanner sigScanner)
|
||||||
{
|
{
|
||||||
this.uploader = new UniversalisMarketBoardUploader();
|
this.uploader = new UniversalisMarketBoardUploader();
|
||||||
|
|
||||||
|
this.addressResolver = new NetworkHandlersAddressResolver();
|
||||||
|
this.addressResolver.Setup(sigScanner);
|
||||||
|
|
||||||
this.CfPop = _ => { };
|
this.CfPop = _ => { };
|
||||||
|
|
||||||
this.messages = Observable.Create<NetworkMessage>(observer =>
|
this.mbPurchaseObservable = Observable.Create<MarketBoardPurchase>(observer =>
|
||||||
{
|
{
|
||||||
void Observe(IntPtr dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
|
this.MarketBoardPurchaseReceived += Observe;
|
||||||
{
|
return () => { this.MarketBoardPurchaseReceived -= Observe; };
|
||||||
var dataManager = Service<DataManager>.GetNullable();
|
|
||||||
observer.OnNext(new NetworkMessage
|
|
||||||
{
|
|
||||||
DataManager = dataManager,
|
|
||||||
Data = dataPtr,
|
|
||||||
Opcode = opCode,
|
|
||||||
SourceActorId = sourceActorId,
|
|
||||||
TargetActorId = targetActorId,
|
|
||||||
Direction = direction,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
gameNetwork.NetworkMessage += Observe;
|
void Observe(nint packetPtr)
|
||||||
return () => { gameNetwork.NetworkMessage -= Observe; };
|
{
|
||||||
|
observer.OnNext(MarketBoardPurchase.Read(packetPtr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mbHistoryObservable = Observable.Create<MarketBoardHistory>(observer =>
|
||||||
|
{
|
||||||
|
this.MarketBoardHistoryReceived += Observe;
|
||||||
|
return () => { this.MarketBoardHistoryReceived -= Observe; };
|
||||||
|
|
||||||
|
void Observe(nint packetPtr)
|
||||||
|
{
|
||||||
|
observer.OnNext(MarketBoardHistory.Read(packetPtr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mbTaxesObservable = Observable.Create<MarketTaxRates>(observer =>
|
||||||
|
{
|
||||||
|
this.MarketBoardTaxesReceived += Observe;
|
||||||
|
return () => { this.MarketBoardTaxesReceived -= Observe; };
|
||||||
|
|
||||||
|
void Observe(nint dataPtr)
|
||||||
|
{
|
||||||
|
// n.b. we precleared the packet information so we're sure that this is *just* tax rate info.
|
||||||
|
observer.OnNext(MarketTaxRates.ReadFromCustomTalk(dataPtr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mbItemRequestObservable = Observable.Create<MarketBoardItemRequest>(observer =>
|
||||||
|
{
|
||||||
|
this.MarketBoardItemRequestStartReceived += Observe;
|
||||||
|
return () => this.MarketBoardItemRequestStartReceived -= Observe;
|
||||||
|
|
||||||
|
void Observe(nint dataPtr)
|
||||||
|
{
|
||||||
|
observer.OnNext(MarketBoardItemRequest.Read(dataPtr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mbOfferingsObservable = Observable.Create<MarketBoardCurrentOfferings>(observer =>
|
||||||
|
{
|
||||||
|
this.MarketBoardOfferingsReceived += Observe;
|
||||||
|
return () => { this.MarketBoardOfferingsReceived -= Observe; };
|
||||||
|
|
||||||
|
void Observe(nint packetPtr)
|
||||||
|
{
|
||||||
|
observer.OnNext(MarketBoardCurrentOfferings.Read(packetPtr));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.mbPurchaseSentObservable = Observable.Create<MarketBoardPurchaseHandler>(observer =>
|
||||||
|
{
|
||||||
|
this.MarketBoardPurchaseRequestSent += Observe;
|
||||||
|
return () => { this.MarketBoardPurchaseRequestSent -= Observe; };
|
||||||
|
|
||||||
|
void Observe(nint dataPtr)
|
||||||
|
{
|
||||||
|
// fortunately, this dataptr has the same structure as the sent packet.
|
||||||
|
observer.OnNext(MarketBoardPurchaseHandler.Read(dataPtr));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.handleMarketBoardItemRequest = this.HandleMarketBoardItemRequest();
|
this.handleMarketBoardItemRequest = this.HandleMarketBoardItemRequest();
|
||||||
this.handleMarketTaxRates = this.HandleMarketTaxRates();
|
this.handleMarketTaxRates = this.HandleMarketTaxRates();
|
||||||
this.handleMarketBoardPurchaseHandler = this.HandleMarketBoardPurchaseHandler();
|
this.handleMarketBoardPurchaseHandler = this.HandleMarketBoardPurchaseHandler();
|
||||||
this.handleCfPop = this.HandleCfPop();
|
|
||||||
|
this.mbPurchaseHook =
|
||||||
|
Hook<MarketBoardPurchasePacketHandler>.FromAddress(
|
||||||
|
this.addressResolver.MarketBoardPurchasePacketHandler,
|
||||||
|
this.MarketPurchasePacketDetour);
|
||||||
|
this.mbPurchaseHook.Enable();
|
||||||
|
|
||||||
|
this.mbHistoryHook =
|
||||||
|
Hook<MarketBoardHistoryPacketHandler>.FromAddress(
|
||||||
|
this.addressResolver.MarketBoardHistoryPacketHandler,
|
||||||
|
this.MarketHistoryPacketDetour);
|
||||||
|
this.mbHistoryHook.Enable();
|
||||||
|
|
||||||
|
this.customTalkHook =
|
||||||
|
Hook<CustomTalkReceiveResponse>.FromAddress(
|
||||||
|
this.addressResolver.CustomTalkEventResponsePacketHandler,
|
||||||
|
this.CustomTalkReceiveResponseDetour);
|
||||||
|
this.customTalkHook.Enable();
|
||||||
|
|
||||||
|
this.mbItemRequestStartHook = Hook<MarketBoardItemRequestStartPacketHandler>.FromAddress(
|
||||||
|
this.addressResolver.MarketBoardItemRequestStartPacketHandler,
|
||||||
|
this.MarketItemRequestStartDetour);
|
||||||
|
this.mbItemRequestStartHook.Enable();
|
||||||
|
|
||||||
|
this.mbOfferingsHook = Hook<InfoProxyItemSearchAddPage>.FromAddress(
|
||||||
|
this.addressResolver.InfoProxyItemSearchAddPage,
|
||||||
|
this.MarketBoardOfferingsDetour);
|
||||||
|
this.mbOfferingsHook.Enable();
|
||||||
|
|
||||||
|
this.mbSendPurchaseRequestHook = Hook<MarketBoardSendPurchaseRequestPacket>.FromAddress(
|
||||||
|
this.addressResolver.BuildMarketBoardPurchaseHandlerPacket,
|
||||||
|
this.MarketBoardSendPurchaseRequestDetour);
|
||||||
|
this.mbSendPurchaseRequestHook.Enable();
|
||||||
|
|
||||||
|
this.cfPopHook = Hook<CfPopDelegate>.FromAddress(this.addressResolver.CfPopPacketHandler, this.CfPopDetour);
|
||||||
|
this.cfPopHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private delegate nint MarketBoardPurchasePacketHandler(nint a1, nint packetRef);
|
||||||
|
|
||||||
|
private delegate nint MarketBoardHistoryPacketHandler(nint self, nint packetData, uint a3, char a4);
|
||||||
|
|
||||||
|
private delegate void CustomTalkReceiveResponse(
|
||||||
|
nuint a1, ushort eventId, byte responseId, uint* args, byte argCount);
|
||||||
|
|
||||||
|
private delegate nint MarketBoardItemRequestStartPacketHandler(nint a1, nint packetRef);
|
||||||
|
|
||||||
|
private delegate byte InfoProxyItemSearchAddPage(nint self, nint packetRef);
|
||||||
|
|
||||||
|
private delegate byte MarketBoardSendPurchaseRequestPacket(InfoProxyItemSearch* infoProxy);
|
||||||
|
|
||||||
|
private delegate nint CfPopDelegate(nint packetData);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event which gets fired when a duty is ready.
|
/// Event which gets fired when a duty is ready.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<ContentFinderCondition> CfPop;
|
public event Action<ContentFinderCondition> CfPop;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardPurchaseReceived;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardHistoryReceived;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardTaxesReceived;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardItemRequestStartReceived;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardOfferingsReceived;
|
||||||
|
|
||||||
|
private event Action<nint>? MarketBoardPurchaseRequestSent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Disposes of managed and unmanaged resources.
|
/// Disposes of managed and unmanaged resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -98,81 +226,75 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
this.handleMarketBoardItemRequest.Dispose();
|
this.handleMarketBoardItemRequest.Dispose();
|
||||||
this.handleMarketTaxRates.Dispose();
|
this.handleMarketTaxRates.Dispose();
|
||||||
this.handleMarketBoardPurchaseHandler.Dispose();
|
this.handleMarketBoardPurchaseHandler.Dispose();
|
||||||
this.handleCfPop.Dispose();
|
|
||||||
|
this.mbPurchaseHook.Dispose();
|
||||||
|
this.mbHistoryHook.Dispose();
|
||||||
|
this.customTalkHook.Dispose();
|
||||||
|
this.mbItemRequestStartHook.Dispose();
|
||||||
|
this.mbOfferingsHook.Dispose();
|
||||||
|
this.mbSendPurchaseRequestHook.Dispose();
|
||||||
|
this.cfPopHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IObservable<NetworkMessage> OnNetworkMessage()
|
private unsafe nint CfPopDetour(nint packetData)
|
||||||
{
|
{
|
||||||
return this.messages.Where(message => message.DataManager?.IsDataReady == true);
|
var result = this.cfPopHook.OriginalDisposeSafe(packetData);
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketBoardItemRequest> OnMarketBoardItemRequestStart()
|
try
|
||||||
{
|
{
|
||||||
return this.OnNetworkMessage()
|
using var stream = new UnmanagedMemoryStream((byte*)packetData, 64);
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
using var reader = new BinaryReader(stream);
|
||||||
.Where(message => message.Opcode ==
|
|
||||||
message.DataManager?.ServerOpCodes["MarketBoardItemRequestStart"])
|
|
||||||
.Select(message => MarketBoardItemRequest.Read(message.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketBoardCurrentOfferings> OnMarketBoardOfferings()
|
var notifyType = reader.ReadByte();
|
||||||
{
|
stream.Position += 0x1B;
|
||||||
return this.OnNetworkMessage()
|
var conditionId = reader.ReadUInt16();
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
|
||||||
.Where(message => message.Opcode == message.DataManager?.ServerOpCodes["MarketBoardOfferings"])
|
|
||||||
.Select(message => MarketBoardCurrentOfferings.Read(message.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketBoardHistory> OnMarketBoardHistory()
|
if (notifyType != 3)
|
||||||
{
|
return result;
|
||||||
return this.OnNetworkMessage()
|
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
|
||||||
.Where(message => message.Opcode == message.DataManager?.ServerOpCodes["MarketBoardHistory"])
|
|
||||||
.Select(message => MarketBoardHistory.Read(message.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketTaxRates> OnMarketTaxRates()
|
if (this.configuration.DutyFinderTaskbarFlash)
|
||||||
{
|
Util.FlashWindow();
|
||||||
return this.OnNetworkMessage()
|
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
|
||||||
.Where(message => message.Opcode == message.DataManager?.ServerOpCodes["MarketTaxRates"])
|
|
||||||
.Where(message =>
|
|
||||||
{
|
|
||||||
// Only some categories of the result dialog packet contain market tax rates
|
|
||||||
var category = (uint)Marshal.ReadInt32(message.Data);
|
|
||||||
return category == 720905;
|
|
||||||
})
|
|
||||||
.Select(message => MarketTaxRates.Read(message.Data))
|
|
||||||
.Where(taxes => taxes.Category == 0xb0009);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketBoardPurchaseHandler> OnMarketBoardPurchaseHandler()
|
var cfConditionSheet = Service<DataManager>.Get().GetExcelSheet<ContentFinderCondition>()!;
|
||||||
{
|
var cfCondition = cfConditionSheet.GetRow(conditionId);
|
||||||
return this.OnNetworkMessage()
|
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneUp)
|
|
||||||
.Where(message => message.Opcode == message.DataManager?.ClientOpCodes["MarketBoardPurchaseHandler"])
|
|
||||||
.Select(message => MarketBoardPurchaseHandler.Read(message.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<MarketBoardPurchase> OnMarketBoardPurchase()
|
if (cfCondition == null)
|
||||||
{
|
{
|
||||||
return this.OnNetworkMessage()
|
Log.Error("CFC key {ConditionId} not in Lumina data", conditionId);
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
return result;
|
||||||
.Where(message => message.Opcode == message.DataManager?.ServerOpCodes["MarketBoardPurchase"])
|
}
|
||||||
.Select(message => MarketBoardPurchase.Read(message.Data));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IObservable<NetworkMessage> OnCfNotifyPop()
|
var cfcName = cfCondition.Name.ToString();
|
||||||
{
|
if (cfcName.IsNullOrEmpty())
|
||||||
return this.OnNetworkMessage()
|
{
|
||||||
.Where(message => message.Direction == NetworkMessageDirection.ZoneDown)
|
cfcName = "Duty Roulette";
|
||||||
.Where(message => message.Opcode == message.DataManager?.ServerOpCodes["CfNotifyPop"]);
|
cfCondition.Image = 112324;
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (this.configuration.DutyFinderChatMessage)
|
||||||
|
{
|
||||||
|
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CfPop.InvokeSafely(cfCondition);
|
||||||
|
}).ContinueWith(
|
||||||
|
task => Log.Error(task.Exception, "CfPop.Invoke failed"),
|
||||||
|
TaskContinuationOptions.OnlyOnFaulted);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "CfPopDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IObservable<List<MarketBoardCurrentOfferings.MarketBoardItemListing>> OnMarketBoardListingsBatch(
|
private IObservable<List<MarketBoardCurrentOfferings.MarketBoardItemListing>> OnMarketBoardListingsBatch(
|
||||||
IObservable<MarketBoardItemRequest> start)
|
IObservable<MarketBoardItemRequest> start)
|
||||||
{
|
{
|
||||||
var offeringsObservable = this.OnMarketBoardOfferings().Publish().RefCount();
|
var offeringsObservable = this.mbOfferingsObservable.Publish().RefCount();
|
||||||
|
|
||||||
void LogEndObserved(MarketBoardCurrentOfferings offerings)
|
void LogEndObserved(MarketBoardCurrentOfferings offerings)
|
||||||
{
|
{
|
||||||
|
|
@ -222,7 +344,7 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
private IObservable<List<MarketBoardHistory.MarketBoardHistoryListing>> OnMarketBoardSalesBatch(
|
private IObservable<List<MarketBoardHistory.MarketBoardHistoryListing>> OnMarketBoardSalesBatch(
|
||||||
IObservable<MarketBoardItemRequest> start)
|
IObservable<MarketBoardItemRequest> start)
|
||||||
{
|
{
|
||||||
var historyObservable = this.OnMarketBoardHistory().Publish().RefCount();
|
var historyObservable = this.mbHistoryObservable.Publish().RefCount();
|
||||||
|
|
||||||
void LogHistoryObserved(MarketBoardHistory history)
|
void LogHistoryObserved(MarketBoardHistory history)
|
||||||
{
|
{
|
||||||
|
|
@ -265,7 +387,7 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
request.AmountToArrive);
|
request.AmountToArrive);
|
||||||
}
|
}
|
||||||
|
|
||||||
var startObservable = this.OnMarketBoardItemRequestStart()
|
var startObservable = this.mbItemRequestObservable
|
||||||
.Where(request => request.Ok).Do(LogStartObserved)
|
.Where(request => request.Ok).Do(LogStartObserved)
|
||||||
.Publish()
|
.Publish()
|
||||||
.RefCount();
|
.RefCount();
|
||||||
|
|
@ -292,7 +414,9 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
if (listings.Count != request.AmountToArrive)
|
if (listings.Count != request.AmountToArrive)
|
||||||
{
|
{
|
||||||
Log.Error("Wrong number of Market Board listings received for request: {ListingsCount} != {RequestAmountToArrive} item#{RequestCatalogId}", listings.Count, request.AmountToArrive, request.CatalogId);
|
Log.Error(
|
||||||
|
"Wrong number of Market Board listings received for request: {ListingsCount} != {RequestAmountToArrive} item#{RequestCatalogId}",
|
||||||
|
listings.Count, request.AmountToArrive, request.CatalogId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,7 +443,7 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
|
|
||||||
private IDisposable HandleMarketTaxRates()
|
private IDisposable HandleMarketTaxRates()
|
||||||
{
|
{
|
||||||
return this.OnMarketTaxRates()
|
return this.mbTaxesObservable
|
||||||
.Where(this.ShouldUpload)
|
.Where(this.ShouldUpload)
|
||||||
.SubscribeOn(ThreadPoolScheduler.Instance)
|
.SubscribeOn(ThreadPoolScheduler.Instance)
|
||||||
.Subscribe(
|
.Subscribe(
|
||||||
|
|
@ -345,8 +469,8 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
|
|
||||||
private IDisposable HandleMarketBoardPurchaseHandler()
|
private IDisposable HandleMarketBoardPurchaseHandler()
|
||||||
{
|
{
|
||||||
return this.OnMarketBoardPurchaseHandler()
|
return this.mbPurchaseSentObservable
|
||||||
.Zip(this.OnMarketBoardPurchase())
|
.Zip(this.mbPurchaseObservable)
|
||||||
.Where(this.ShouldUpload)
|
.Where(this.ShouldUpload)
|
||||||
.SubscribeOn(ThreadPoolScheduler.Instance)
|
.SubscribeOn(ThreadPoolScheduler.Instance)
|
||||||
.Subscribe(
|
.Subscribe(
|
||||||
|
|
@ -376,85 +500,93 @@ internal class NetworkHandlers : IDisposable, IServiceType
|
||||||
ex => Log.Error(ex, "Failed to handle Market Board purchase event"));
|
ex => Log.Error(ex, "Failed to handle Market Board purchase event"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe IDisposable HandleCfPop()
|
|
||||||
{
|
|
||||||
return this.OnCfNotifyPop()
|
|
||||||
.SubscribeOn(ThreadPoolScheduler.Instance)
|
|
||||||
.Subscribe(
|
|
||||||
message =>
|
|
||||||
{
|
|
||||||
using var stream = new UnmanagedMemoryStream((byte*)message.Data.ToPointer(), 64);
|
|
||||||
using var reader = new BinaryReader(stream);
|
|
||||||
|
|
||||||
var notifyType = reader.ReadByte();
|
|
||||||
stream.Position += 0x1B;
|
|
||||||
var conditionId = reader.ReadUInt16();
|
|
||||||
|
|
||||||
if (notifyType != 3)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var cfConditionSheet = message.DataManager!.GetExcelSheet<ContentFinderCondition>()!;
|
|
||||||
var cfCondition = cfConditionSheet.GetRow(conditionId);
|
|
||||||
|
|
||||||
if (cfCondition == null)
|
|
||||||
{
|
|
||||||
Log.Error("CFC key {ConditionId} not in Lumina data", conditionId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfcName = cfCondition.Name.ToString();
|
|
||||||
if (cfcName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
cfcName = "Duty Roulette";
|
|
||||||
cfCondition.Image = 112324;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flash window
|
|
||||||
if (this.configuration.DutyFinderTaskbarFlash && !NativeFunctions.ApplicationIsActivated())
|
|
||||||
{
|
|
||||||
var flashInfo = new NativeFunctions.FlashWindowInfo
|
|
||||||
{
|
|
||||||
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
|
|
||||||
Count = uint.MaxValue,
|
|
||||||
Timeout = 0,
|
|
||||||
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
|
|
||||||
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
|
|
||||||
};
|
|
||||||
NativeFunctions.FlashWindowEx(ref flashInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
Task.Run(() =>
|
|
||||||
{
|
|
||||||
if (this.configuration.DutyFinderChatMessage)
|
|
||||||
{
|
|
||||||
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.CfPop.InvokeSafely(cfCondition);
|
|
||||||
}).ContinueWith(
|
|
||||||
task => Log.Error(task.Exception, "CfPop.Invoke failed"),
|
|
||||||
TaskContinuationOptions.OnlyOnFaulted);
|
|
||||||
},
|
|
||||||
ex => Log.Error(ex, "Failed to handle Market Board purchase event"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ShouldUpload<T>(T any)
|
private bool ShouldUpload<T>(T any)
|
||||||
{
|
{
|
||||||
return this.configuration.IsMbCollect;
|
return this.configuration.IsMbCollect;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NetworkMessage
|
private nint MarketPurchasePacketDetour(nint a1, nint packetData)
|
||||||
{
|
{
|
||||||
public DataManager? DataManager { get; init; }
|
try
|
||||||
|
{
|
||||||
|
this.MarketBoardPurchaseReceived?.InvokeSafely(packetData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "MarketPurchasePacketHandler threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mbPurchaseHook.OriginalDisposeSafe(a1, packetData);
|
||||||
|
}
|
||||||
|
|
||||||
public IntPtr Data { get; init; }
|
private nint MarketHistoryPacketDetour(nint a1, nint packetData, uint a3, char a4)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.MarketBoardHistoryReceived?.InvokeSafely(packetData);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "MarketHistoryPacketDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mbHistoryHook.OriginalDisposeSafe(a1, packetData, a3, a4);
|
||||||
|
}
|
||||||
|
|
||||||
public ushort Opcode { get; init; }
|
private void CustomTalkReceiveResponseDetour(nuint a1, ushort eventId, byte responseId, uint* args, byte argCount)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (eventId == 7 && responseId == 8)
|
||||||
|
this.MarketBoardTaxesReceived?.InvokeSafely((nint)args);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "CustomTalkReceiveResponseDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
public uint SourceActorId { get; init; }
|
this.customTalkHook.OriginalDisposeSafe(a1, eventId, responseId, args, argCount);
|
||||||
|
}
|
||||||
|
|
||||||
public uint TargetActorId { get; init; }
|
private nint MarketItemRequestStartDetour(nint a1, nint packetRef)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.MarketBoardItemRequestStartReceived?.InvokeSafely(packetRef);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "MarketItemRequestStartDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mbItemRequestStartHook.OriginalDisposeSafe(a1, packetRef);
|
||||||
|
}
|
||||||
|
|
||||||
public NetworkMessageDirection Direction { get; init; }
|
private byte MarketBoardOfferingsDetour(nint a1, nint packetRef)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.MarketBoardOfferingsReceived?.InvokeSafely(packetRef);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "MarketBoardOfferingsDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mbOfferingsHook.OriginalDisposeSafe(a1, packetRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte MarketBoardSendPurchaseRequestDetour(InfoProxyItemSearch* infoProxyItemSearch)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.MarketBoardPurchaseRequestSent?.InvokeSafely((nint)infoProxyItemSearch + 0x5680);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "MarketBoardSendPurchaseRequestDetour threw an exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.mbSendPurchaseRequestHook.OriginalDisposeSafe(infoProxyItemSearch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
namespace Dalamud.Game.Network.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal address resolver for the network handlers.
|
||||||
|
/// </summary>
|
||||||
|
internal class NetworkHandlersAddressResolver : BaseAddressResolver
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method responsible for handling CfPop packets.
|
||||||
|
/// </summary>
|
||||||
|
public nint CfPopPacketHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method responsible for handling market board history. In this case, we are
|
||||||
|
/// sigging the packet handler method directly.
|
||||||
|
/// </summary>
|
||||||
|
public nint MarketBoardHistoryPacketHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method responsible for processing the market board purchase packet. In this
|
||||||
|
/// case, we are sigging the packet handler method directly.
|
||||||
|
/// </summary>
|
||||||
|
public nint MarketBoardPurchasePacketHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method responsible for custom talk events. Necessary for marketboard tax data,
|
||||||
|
/// as this isn't really exposed anywhere else.
|
||||||
|
/// </summary>
|
||||||
|
public nint CustomTalkEventResponsePacketHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method responsible for the marketboard ItemRequestStart packet.
|
||||||
|
/// </summary>
|
||||||
|
public nint MarketBoardItemRequestStartPacketHandler { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the InfoProxyItemSearch.AddPage method, used to load market data.
|
||||||
|
/// </summary>
|
||||||
|
public nint InfoProxyItemSearchAddPage { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the pointer to the method inside InfoProxyItemSearch that is responsible for building and sending
|
||||||
|
/// a purchase request packet.
|
||||||
|
/// </summary>
|
||||||
|
public nint BuildMarketBoardPurchaseHandlerPacket { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Setup64Bit(SigScanner scanner)
|
||||||
|
{
|
||||||
|
this.CfPopPacketHandler = scanner.ScanText("40 53 57 48 83 EC 78 48 8B D9 48 8D 0D");
|
||||||
|
this.MarketBoardHistoryPacketHandler = scanner.ScanText(
|
||||||
|
"40 53 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 85 C0 74 36 4C 8B 00 48 8B C8 41 FF 90 ?? ?? ?? ?? 48 8B C8 BA ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 17 48 8D 53 04");
|
||||||
|
this.MarketBoardPurchasePacketHandler =
|
||||||
|
scanner.ScanText("40 55 53 57 48 8B EC 48 83 EC 70 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 F0 48 8B 0D");
|
||||||
|
this.CustomTalkEventResponsePacketHandler =
|
||||||
|
scanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 49 8B D9 41 0F B6 F8 0F B7 F2 8B E9 E8 ?? ?? ?? ?? 48 8B C8 44 0F B6 CF 0F B6 44 24 ?? 44 0F B7 C6 88 44 24 ?? 8B D5 48 89 5C 24");
|
||||||
|
this.MarketBoardItemRequestStartPacketHandler =
|
||||||
|
scanner.ScanText("48 89 5C 24 ?? 57 48 83 EC 40 48 8B 0D ?? ?? ?? ?? 48 8B DA E8 ?? ?? ?? ?? 48 8B F8");
|
||||||
|
this.InfoProxyItemSearchAddPage =
|
||||||
|
scanner.ScanText("48 89 5C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 0F B6 82 ?? ?? ?? ?? 48 8B FA 48 8B D9 38 41 19 74 54");
|
||||||
|
this.BuildMarketBoardPurchaseHandlerPacket =
|
||||||
|
scanner.ScanText("40 53 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 8B D0 48 85 C0 0F 84 ?? ?? ?? ?? 8B 8B");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Dalamud.Game.Network.Structures;
|
namespace Dalamud.Game.Network.Structures;
|
||||||
|
|
@ -77,4 +76,27 @@ public class MarketTaxRates
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate a MarketTaxRates wrapper class from information located in a CustomTalk packet.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataPtr">The pointer to the relevant CustomTalk data.</param>
|
||||||
|
/// <returns>Returns a wrapped and ready-to-go MarketTaxRates record.</returns>
|
||||||
|
public static unsafe MarketTaxRates ReadFromCustomTalk(IntPtr dataPtr)
|
||||||
|
{
|
||||||
|
using var stream = new UnmanagedMemoryStream((byte*)dataPtr.ToPointer(), 1544);
|
||||||
|
using var reader = new BinaryReader(stream);
|
||||||
|
|
||||||
|
return new MarketTaxRates
|
||||||
|
{
|
||||||
|
Category = 0xb0009, // shim
|
||||||
|
LimsaLominsaTax = reader.ReadUInt32(),
|
||||||
|
GridaniaTax = reader.ReadUInt32(),
|
||||||
|
UldahTax = reader.ReadUInt32(),
|
||||||
|
IshgardTax = reader.ReadUInt32(),
|
||||||
|
KuganeTax = reader.ReadUInt32(),
|
||||||
|
CrystariumTax = reader.ReadUInt32(),
|
||||||
|
SharlayanTax = reader.ReadUInt32(),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
||||||
private readonly Dictionary<ushort, (string Name, int Size)> opCodeDict = new();
|
|
||||||
|
|
||||||
private bool trackNetwork;
|
private bool trackNetwork;
|
||||||
private int trackedPackets;
|
private int trackedPackets;
|
||||||
|
|
@ -71,9 +70,6 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
this.filterString = string.Empty;
|
this.filterString = string.Empty;
|
||||||
this.packets.Clear();
|
this.packets.Clear();
|
||||||
this.Ready = true;
|
this.Ready = true;
|
||||||
var dataManager = Service<DataManager>.Get();
|
|
||||||
foreach (var (name, code) in dataManager.ClientOpCodes.Concat(dataManager.ServerOpCodes))
|
|
||||||
this.opCodeDict.TryAdd(code, (name, this.GetSizeFromName(name)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -106,7 +102,7 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
this.DrawFilterInput();
|
this.DrawFilterInput();
|
||||||
this.DrawNegativeFilterInput();
|
this.DrawNegativeFilterInput();
|
||||||
|
|
||||||
ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "Known Name", "OpCode", "Hex", "Target", "Source", "Data");
|
ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "OpCode", "Hex", "Target", "Source", "Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawNetworkPacket(NetworkPacketData data)
|
private void DrawNetworkPacket(NetworkPacketData data)
|
||||||
|
|
@ -114,16 +110,6 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(data.Direction.ToString());
|
ImGui.TextUnformatted(data.Direction.ToString());
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
|
||||||
if (this.opCodeDict.TryGetValue(data.OpCode, out var pair))
|
|
||||||
{
|
|
||||||
ImGui.TextUnformatted(pair.Name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ImGui.Dummy(new Vector2(150 * ImGuiHelpers.GlobalScale, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.TextUnformatted(data.OpCode.ToString());
|
ImGui.TextUnformatted(data.OpCode.ToString());
|
||||||
|
|
||||||
|
|
@ -217,7 +203,7 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetSizeFromOpCode(ushort opCode)
|
private int GetSizeFromOpCode(ushort opCode)
|
||||||
=> this.opCodeDict.TryGetValue(opCode, out var pair) ? pair.Size : 0;
|
=> 0;
|
||||||
|
|
||||||
/// <remarks> Add known packet-name -> packet struct size associations here to copy the byte data for such packets. </remarks>>
|
/// <remarks> Add known packet-name -> packet struct size associations here to copy the byte data for such packets. </remarks>>
|
||||||
private int GetSizeFromName(string name)
|
private int GetSizeFromName(string name)
|
||||||
|
|
@ -228,5 +214,5 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
|
|
||||||
/// <remarks> The filter should find opCodes by number (decimal and hex) and name, if existing. </remarks>
|
/// <remarks> The filter should find opCodes by number (decimal and hex) and name, if existing. </remarks>
|
||||||
private string OpCodeToString(ushort opCode)
|
private string OpCodeToString(ushort opCode)
|
||||||
=> this.opCodeDict.TryGetValue(opCode, out var pair) ? $"{opCode}\0{opCode:X}\0{pair.Name}" : $"{opCode}\0{opCode:X}";
|
=> $"{opCode}\0{opCode:X}";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
using ImGuiScene;
|
|
||||||
using Lumina;
|
using Lumina;
|
||||||
using Lumina.Data;
|
using Lumina.Data;
|
||||||
using Lumina.Data.Files;
|
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
@ -19,7 +13,7 @@ public interface IDataManager
|
||||||
/// Gets the current game client language.
|
/// Gets the current game client language.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ClientLanguage Language { get; }
|
public ClientLanguage Language { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a <see cref="Lumina"/> object which gives access to any excel/game data.
|
/// Gets a <see cref="Lumina"/> object which gives access to any excel/game data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
|
@ -40,7 +39,8 @@ public static class Util
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the assembly version of Dalamud.
|
/// Gets the assembly version of Dalamud.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string AssemblyVersion { get; } = Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
|
public static string AssemblyVersion { get; } =
|
||||||
|
Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version.ToString();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Check two byte arrays for equality.
|
/// Check two byte arrays for equality.
|
||||||
|
|
@ -276,14 +276,16 @@ public static class Util
|
||||||
if (ImGui.TreeNode($"{obj}##print-obj-{addr:X}-{string.Join("-", path)}"))
|
if (ImGui.TreeNode($"{obj}##print-obj-{addr:X}-{string.Join("-", path)}"))
|
||||||
{
|
{
|
||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
foreach (var f in obj.GetType().GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance))
|
foreach (var f in obj.GetType()
|
||||||
|
.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance))
|
||||||
{
|
{
|
||||||
var fixedBuffer = (FixedBufferAttribute)f.GetCustomAttribute(typeof(FixedBufferAttribute));
|
var fixedBuffer = (FixedBufferAttribute)f.GetCustomAttribute(typeof(FixedBufferAttribute));
|
||||||
if (fixedBuffer != null)
|
if (fixedBuffer != null)
|
||||||
{
|
{
|
||||||
ImGui.Text($"fixed");
|
ImGui.Text($"fixed");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1), $"{fixedBuffer.ElementType.Name}[0x{fixedBuffer.Length:X}]");
|
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.9f, 1),
|
||||||
|
$"{fixedBuffer.ElementType.Name}[0x{fixedBuffer.Length:X}]");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -294,7 +296,7 @@ public static class Util
|
||||||
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.4f, 1), $"{f.Name}: ");
|
ImGui.TextColored(new Vector4(0.2f, 0.9f, 0.4f, 1), $"{f.Name}: ");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
ShowValue(addr, new List<string>(path) { f.Name }, f.FieldType, f.GetValue(obj));
|
ShowValue(addr, new List<string>(path) {f.Name}, f.FieldType, f.GetValue(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var p in obj.GetType().GetProperties().Where(p => p.GetGetMethod()?.GetParameters().Length == 0))
|
foreach (var p in obj.GetType().GetProperties().Where(p => p.GetGetMethod()?.GetParameters().Length == 0))
|
||||||
|
|
@ -304,7 +306,7 @@ public static class Util
|
||||||
ImGui.TextColored(new Vector4(0.2f, 0.6f, 0.4f, 1), $"{p.Name}: ");
|
ImGui.TextColored(new Vector4(0.2f, 0.6f, 0.4f, 1), $"{p.Name}: ");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
ShowValue(addr, new List<string>(path) { p.Name }, p.PropertyType, p.GetValue(obj));
|
ShowValue(addr, new List<string>(path) {p.Name}, p.PropertyType, p.GetValue(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.TreePop();
|
ImGui.TreePop();
|
||||||
|
|
@ -399,7 +401,8 @@ public static class Util
|
||||||
/// <param name="exit">Specify whether to exit immediately.</param>
|
/// <param name="exit">Specify whether to exit immediately.</param>
|
||||||
public static void Fatal(string message, string caption, bool exit = true)
|
public static void Fatal(string message, string caption, bool exit = true)
|
||||||
{
|
{
|
||||||
var flags = NativeFunctions.MessageBoxType.Ok | NativeFunctions.MessageBoxType.IconError | NativeFunctions.MessageBoxType.Topmost;
|
var flags = NativeFunctions.MessageBoxType.Ok | NativeFunctions.MessageBoxType.IconError |
|
||||||
|
NativeFunctions.MessageBoxType.Topmost;
|
||||||
_ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
|
_ = NativeFunctions.MessageBoxW(Process.GetCurrentProcess().MainWindowHandle, message, caption, flags);
|
||||||
|
|
||||||
if (exit)
|
if (exit)
|
||||||
|
|
@ -413,7 +416,7 @@ public static class Util
|
||||||
/// <returns>Human readable version.</returns>
|
/// <returns>Human readable version.</returns>
|
||||||
public static string FormatBytes(long bytes)
|
public static string FormatBytes(long bytes)
|
||||||
{
|
{
|
||||||
string[] suffix = { "B", "KB", "MB", "GB", "TB" };
|
string[] suffix = {"B", "KB", "MB", "GB", "TB"};
|
||||||
int i;
|
int i;
|
||||||
double dblSByte = bytes;
|
double dblSByte = bytes;
|
||||||
for (i = 0; i < suffix.Length && bytes >= 1024; i++, bytes /= 1024)
|
for (i = 0; i < suffix.Length && bytes >= 1024; i++, bytes /= 1024)
|
||||||
|
|
@ -601,7 +604,7 @@ public static class Util
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
foreach (var enumerator in enumerators)
|
foreach (var enumerator in enumerators)
|
||||||
|
|
@ -611,6 +614,27 @@ public static class Util
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request that Windows flash the game window to grab the user's attention.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flashIfOpen">Attempt to flash even if the game is currently focused.</param>
|
||||||
|
public static void FlashWindow(bool flashIfOpen = false)
|
||||||
|
{
|
||||||
|
if (NativeFunctions.ApplicationIsActivated() && flashIfOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var flashInfo = new NativeFunctions.FlashWindowInfo
|
||||||
|
{
|
||||||
|
Size = (uint)Marshal.SizeOf<NativeFunctions.FlashWindowInfo>(),
|
||||||
|
Count = uint.MaxValue,
|
||||||
|
Timeout = 0,
|
||||||
|
Flags = NativeFunctions.FlashWindow.All | NativeFunctions.FlashWindow.TimerNoFG,
|
||||||
|
Hwnd = Process.GetCurrentProcess().MainWindowHandle,
|
||||||
|
};
|
||||||
|
|
||||||
|
NativeFunctions.FlashWindowEx(ref flashInfo);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overwrite text in a file by first writing it to a temporary file, and then
|
/// Overwrite text in a file by first writing it to a temporary file, and then
|
||||||
/// moving that file to the path specified.
|
/// moving that file to the path specified.
|
||||||
|
|
@ -693,7 +717,8 @@ public static class Util
|
||||||
/// <param name="logMessage">Log message to print, if specified and an error occurs.</param>
|
/// <param name="logMessage">Log message to print, if specified and an error occurs.</param>
|
||||||
/// <param name="moduleLog">Module logger, if any.</param>
|
/// <param name="moduleLog">Module logger, if any.</param>
|
||||||
/// <typeparam name="T">The type of object to dispose.</typeparam>
|
/// <typeparam name="T">The type of object to dispose.</typeparam>
|
||||||
internal static void ExplicitDisposeIgnoreExceptions<T>(this T obj, string? logMessage = null, ModuleLog? moduleLog = null) where T : IDisposable
|
internal static void ExplicitDisposeIgnoreExceptions<T>(
|
||||||
|
this T obj, string? logMessage = null, ModuleLog? moduleLog = null) where T : IDisposable
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue