mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-07 08:24:37 +01:00
Rework NetworkMonitorWidget, remove GameNetwork (#2593)
* Rework NetworkMonitorWidget, remove GameNetwork * Rework packet filtering
This commit is contained in:
parent
afa7b0c1f3
commit
3abf7bb00b
6 changed files with 212 additions and 345 deletions
|
|
@ -1,147 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Utility;
|
||||
|
||||
using FFXIVClientStructs.FFXIV.Client.Network;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.Network;
|
||||
|
||||
/// <summary>
|
||||
/// This class handles interacting with game network events.
|
||||
/// </summary>
|
||||
[ServiceManager.EarlyLoadedService]
|
||||
internal sealed unsafe class GameNetwork : IInternalDisposableService
|
||||
{
|
||||
private readonly GameNetworkAddressResolver address;
|
||||
private readonly Hook<PacketDispatcher.Delegates.OnReceivePacket> processZonePacketDownHook;
|
||||
private readonly Hook<ProcessZonePacketUpDelegate> processZonePacketUpHook;
|
||||
|
||||
private readonly HitchDetector hitchDetectorUp;
|
||||
private readonly HitchDetector hitchDetectorDown;
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private unsafe GameNetwork(TargetSigScanner sigScanner)
|
||||
{
|
||||
this.hitchDetectorUp = new HitchDetector("GameNetworkUp", this.configuration.GameNetworkUpHitch);
|
||||
this.hitchDetectorDown = new HitchDetector("GameNetworkDown", this.configuration.GameNetworkDownHitch);
|
||||
|
||||
this.address = new GameNetworkAddressResolver();
|
||||
this.address.Setup(sigScanner);
|
||||
|
||||
var onReceivePacketAddress = (nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket;
|
||||
|
||||
Log.Verbose("===== G A M E N E T W O R K =====");
|
||||
Log.Verbose($"OnReceivePacket address {Util.DescribeAddress(onReceivePacketAddress)}");
|
||||
Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}");
|
||||
|
||||
this.processZonePacketDownHook = Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(onReceivePacketAddress, this.ProcessZonePacketDownDetour);
|
||||
this.processZonePacketUpHook = Hook<ProcessZonePacketUpDelegate>.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);
|
||||
|
||||
this.processZonePacketDownHook.Enable();
|
||||
this.processZonePacketUpHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type of a network message event.
|
||||
/// </summary>
|
||||
/// <param name="dataPtr">The pointer to the raw data.</param>
|
||||
/// <param name="opCode">The operation ID code.</param>
|
||||
/// <param name="sourceActorId">The source actor ID.</param>
|
||||
/// <param name="targetActorId">The taret actor ID.</param>
|
||||
/// <param name="direction">The direction of the packed.</param>
|
||||
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
|
||||
|
||||
/// <summary>
|
||||
/// Event that is called when a network message is sent/received.
|
||||
/// </summary>
|
||||
public event OnNetworkMessageDelegate? NetworkMessage;
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IInternalDisposableService.DisposeService()
|
||||
{
|
||||
this.processZonePacketDownHook.Dispose();
|
||||
this.processZonePacketUpHook.Dispose();
|
||||
}
|
||||
|
||||
private void ProcessZonePacketDownDetour(PacketDispatcher* dispatcher, uint targetId, IntPtr dataPtr)
|
||||
{
|
||||
this.hitchDetectorDown.Start();
|
||||
|
||||
// Go back 0x10 to get back to the start of the packet header
|
||||
dataPtr -= 0x10;
|
||||
|
||||
foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage))
|
||||
{
|
||||
try
|
||||
{
|
||||
d.Invoke(
|
||||
dataPtr + 0x20,
|
||||
(ushort)Marshal.ReadInt16(dataPtr, 0x12),
|
||||
0,
|
||||
targetId,
|
||||
NetworkMessageDirection.ZoneDown);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string header;
|
||||
try
|
||||
{
|
||||
var data = new byte[32];
|
||||
Marshal.Copy(dataPtr, data, 0, 32);
|
||||
header = BitConverter.ToString(data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
header = "failed";
|
||||
}
|
||||
|
||||
Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header);
|
||||
}
|
||||
}
|
||||
|
||||
this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10);
|
||||
this.hitchDetectorDown.Stop();
|
||||
}
|
||||
|
||||
private byte ProcessZonePacketUpDetour(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4)
|
||||
{
|
||||
this.hitchDetectorUp.Start();
|
||||
|
||||
try
|
||||
{
|
||||
// Call events
|
||||
// TODO: Implement actor IDs
|
||||
this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr), 0x0, 0x0, NetworkMessageDirection.ZoneUp);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string header;
|
||||
try
|
||||
{
|
||||
var data = new byte[32];
|
||||
Marshal.Copy(dataPtr, data, 0, 32);
|
||||
header = BitConverter.ToString(data);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
header = "failed";
|
||||
}
|
||||
|
||||
Log.Error(ex, "Exception on ProcessZonePacketUp hook. Header: " + header);
|
||||
}
|
||||
|
||||
this.hitchDetectorUp.Stop();
|
||||
|
||||
return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using Dalamud.Plugin.Services;
|
||||
|
||||
namespace Dalamud.Game.Network;
|
||||
|
||||
/// <summary>
|
||||
/// The address resolver for the <see cref="GameNetwork"/> class.
|
||||
/// </summary>
|
||||
internal sealed class GameNetworkAddressResolver : BaseAddressResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the address of the ProcessZonePacketUp method.
|
||||
/// </summary>
|
||||
public IntPtr ProcessZonePacketUp { get; private set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Setup64Bit(ISigScanner sig)
|
||||
{
|
||||
this.ProcessZonePacketUp = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 4C 89 64 24 ?? 55 41 56 41 57 48 8B EC 48 83 EC 70"); // unnamed in cs
|
||||
}
|
||||
}
|
||||
|
|
@ -55,10 +55,7 @@ internal unsafe class NetworkHandlers : IInternalDisposableService
|
|||
private bool disposing;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private NetworkHandlers(
|
||||
GameNetwork gameNetwork,
|
||||
TargetSigScanner sigScanner,
|
||||
HappyHttpClient happyHttpClient)
|
||||
private NetworkHandlers(TargetSigScanner sigScanner, HappyHttpClient happyHttpClient)
|
||||
{
|
||||
this.uploader = new UniversalisMarketBoardUploader(happyHttpClient);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
namespace Dalamud.Game.Network;
|
||||
|
||||
/// <summary>
|
||||
/// This represents the direction of a network message.
|
||||
/// </summary>
|
||||
public enum NetworkMessageDirection
|
||||
{
|
||||
/// <summary>
|
||||
/// A zone down message.
|
||||
/// </summary>
|
||||
ZoneDown,
|
||||
|
||||
/// <summary>
|
||||
/// A zone up message.
|
||||
/// </summary>
|
||||
ZoneUp,
|
||||
}
|
||||
|
|
@ -1,44 +1,51 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Game.Network;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Interface.Components;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Dalamud.Memory;
|
||||
|
||||
using ImGuiTable = Dalamud.Interface.Utility.ImGuiTable;
|
||||
using FFXIVClientStructs.FFXIV.Application.Network;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||
using FFXIVClientStructs.FFXIV.Client.Network;
|
||||
|
||||
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
||||
|
||||
/// <summary>
|
||||
/// Widget to display the current packets.
|
||||
/// </summary>
|
||||
internal class NetworkMonitorWidget : IDataWindowWidget
|
||||
internal unsafe class NetworkMonitorWidget : IDataWindowWidget
|
||||
{
|
||||
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
||||
|
||||
private Hook<PacketDispatcher.Delegates.OnReceivePacket>? hookDown;
|
||||
private Hook<ZoneClientSendPacketDelegate>? hookUp;
|
||||
|
||||
private bool trackNetwork;
|
||||
private int trackedPackets;
|
||||
private Regex? trackedOpCodes;
|
||||
private int trackedPackets = 20;
|
||||
private ulong nextPacketIndex;
|
||||
private string filterString = string.Empty;
|
||||
private Regex? untrackedOpCodes;
|
||||
private string negativeFilterString = string.Empty;
|
||||
private bool filterRecording = true;
|
||||
private bool autoScroll = true;
|
||||
private bool autoScrollPending;
|
||||
|
||||
/// <summary> Finalizes an instance of the <see cref="NetworkMonitorWidget"/> class. </summary>
|
||||
~NetworkMonitorWidget()
|
||||
{
|
||||
if (this.trackNetwork)
|
||||
{
|
||||
this.trackNetwork = false;
|
||||
var network = Service<GameNetwork>.GetNullable();
|
||||
if (network != null)
|
||||
{
|
||||
network.NetworkMessage -= this.OnNetworkMessage;
|
||||
}
|
||||
}
|
||||
this.hookDown?.Dispose();
|
||||
this.hookUp?.Dispose();
|
||||
}
|
||||
|
||||
private delegate byte ZoneClientSendPacketDelegate(ZoneClient* thisPtr, nint packet, uint a3, uint a4, byte a5);
|
||||
|
||||
private enum NetworkMessageDirection
|
||||
{
|
||||
ZoneDown,
|
||||
ZoneUp,
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -53,31 +60,36 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
|||
/// <inheritdoc/>
|
||||
public void Load()
|
||||
{
|
||||
this.trackNetwork = false;
|
||||
this.trackedPackets = 20;
|
||||
this.trackedOpCodes = null;
|
||||
this.filterString = string.Empty;
|
||||
this.packets.Clear();
|
||||
this.hookDown = Hook<PacketDispatcher.Delegates.OnReceivePacket>.FromAddress(
|
||||
(nint)PacketDispatcher.StaticVirtualTablePointer->OnReceivePacket,
|
||||
this.OnReceivePacketDetour);
|
||||
|
||||
// TODO: switch to ZoneClient.SendPacket from CS
|
||||
if (Service<TargetSigScanner>.Get().TryScanText("E8 ?? ?? ?? ?? 4C 8B 44 24 ?? E9", out var address))
|
||||
this.hookUp = Hook<ZoneClientSendPacketDelegate>.FromAddress(address, this.SendPacketDetour);
|
||||
|
||||
this.Ready = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Draw()
|
||||
{
|
||||
var network = Service<GameNetwork>.Get();
|
||||
if (ImGui.Checkbox("Track Network Packets"u8, ref this.trackNetwork))
|
||||
{
|
||||
if (this.trackNetwork)
|
||||
{
|
||||
network.NetworkMessage += this.OnNetworkMessage;
|
||||
this.nextPacketIndex = 0;
|
||||
this.hookDown?.Enable();
|
||||
this.hookUp?.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
network.NetworkMessage -= this.OnNetworkMessage;
|
||||
this.hookDown?.Disable();
|
||||
this.hookUp?.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2);
|
||||
ImGui.SetNextItemWidth(-1);
|
||||
if (ImGui.DragInt("Stored Number of Packets"u8, ref this.trackedPackets, 0.1f, 1, 512))
|
||||
{
|
||||
this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512);
|
||||
|
|
@ -88,131 +100,200 @@ internal class NetworkMonitorWidget : IDataWindowWidget
|
|||
this.packets.Clear();
|
||||
}
|
||||
|
||||
this.DrawFilterInput();
|
||||
this.DrawNegativeFilterInput();
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Auto-Scroll"u8, ref this.autoScroll);
|
||||
|
||||
ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "OpCode", "Hex", "Target", "Source", "Data");
|
||||
}
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - (ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetFrameHeight()) * 2);
|
||||
ImGui.InputTextWithHint("##Filter"u8, "Filter OpCodes..."u8, ref this.filterString, 1024, ImGuiInputTextFlags.AutoSelectAll);
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGui.Checkbox("##FilterRecording"u8, ref this.filterRecording);
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Apply filter to incoming packets.\nUncheck to record all packets and filter the table instead."u8);
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGuiComponents.HelpMarker("Enter OpCodes in a comma-separated list.\nRanges are supported. Exclude OpCodes with exclamation mark.\nExample: -400,!50-100,650,700-980,!941");
|
||||
|
||||
private void DrawNetworkPacket(NetworkPacketData data)
|
||||
{
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(data.Direction.ToString());
|
||||
using var table = ImRaii.Table("NetworkMonitorTableV2"u8, 6, ImGuiTableFlags.Borders | ImGuiTableFlags.ScrollY | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoSavedSettings);
|
||||
if (!table) return;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(data.OpCode.ToString());
|
||||
ImGui.TableSetupColumn("Index"u8, ImGuiTableColumnFlags.WidthFixed, 50);
|
||||
ImGui.TableSetupColumn("Time"u8, ImGuiTableColumnFlags.WidthFixed, 100);
|
||||
ImGui.TableSetupColumn("Direction"u8, ImGuiTableColumnFlags.WidthFixed, 100);
|
||||
ImGui.TableSetupColumn("OpCode"u8, ImGuiTableColumnFlags.WidthFixed, 100);
|
||||
ImGui.TableSetupColumn("OpCode (Hex)"u8, ImGuiTableColumnFlags.WidthFixed, 100);
|
||||
ImGui.TableSetupColumn("Target EntityId"u8, ImGuiTableColumnFlags.WidthStretch);
|
||||
ImGui.TableSetupScrollFreeze(0, 1);
|
||||
ImGui.TableHeadersRow();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text($"0x{data.OpCode:X4}");
|
||||
var autoScrollDisabled = false;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(data.TargetActorId > 0 ? $"0x{data.TargetActorId:X}" : string.Empty);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(data.SourceActorId > 0 ? $"0x{data.SourceActorId:X}" : string.Empty);
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (data.Data.Count > 0)
|
||||
foreach (var packet in this.packets)
|
||||
{
|
||||
ImGui.Text(string.Join(" ", data.Data.Select(b => b.ToString("X2"))));
|
||||
if (!this.filterRecording && !this.IsFiltered(packet.OpCode))
|
||||
continue;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(packet.Index.ToString());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(packet.Time.ToLongTimeString());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.Text(packet.Direction.ToString());
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
using (ImRaii.PushId(packet.Index.ToString()))
|
||||
{
|
||||
if (ImGui.SmallButton("X"))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.filterString))
|
||||
this.filterString += ",";
|
||||
|
||||
this.filterString += $"!{packet.OpCode}";
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
ImGui.SetTooltip("Filter OpCode"u8);
|
||||
|
||||
autoScrollDisabled |= ImGui.IsItemHovered();
|
||||
|
||||
ImGui.SameLine();
|
||||
WidgetUtil.DrawCopyableText(packet.OpCode.ToString());
|
||||
autoScrollDisabled |= ImGui.IsItemHovered();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
WidgetUtil.DrawCopyableText($"0x{packet.OpCode:X3}");
|
||||
autoScrollDisabled |= ImGui.IsItemHovered();
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
if (packet.TargetEntityId > 0)
|
||||
{
|
||||
WidgetUtil.DrawCopyableText($"{packet.TargetEntityId:X}");
|
||||
|
||||
var name = !string.IsNullOrEmpty(packet.TargetName)
|
||||
? packet.TargetName
|
||||
: GetTargetName(packet.TargetEntityId);
|
||||
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
ImGui.SameLine(0, ImGui.GetStyle().ItemInnerSpacing.X);
|
||||
ImGui.Text($"({name})");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (this.autoScroll && this.autoScrollPending && !autoScrollDisabled)
|
||||
{
|
||||
ImGui.Dummy(ImGui.GetContentRegionAvail() with { Y = 0 });
|
||||
ImGui.SetScrollHereY();
|
||||
this.autoScrollPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawFilterInput()
|
||||
private static string GetTargetName(uint targetId)
|
||||
{
|
||||
var invalidRegEx = this.filterString.Length > 0 && this.trackedOpCodes == null;
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx);
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx);
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
if (!ImGui.InputTextWithHint("##Filter"u8, "Regex Filter OpCodes..."u8, ref this.filterString, 1024))
|
||||
{
|
||||
if (targetId == PlayerState.Instance()->EntityId)
|
||||
return "Local Player";
|
||||
|
||||
var cachedName = NameCache.Instance()->GetNameByEntityId(targetId);
|
||||
if (cachedName.HasValue)
|
||||
return cachedName.ToString();
|
||||
|
||||
var obj = GameObjectManager.Instance()->Objects.GetObjectByEntityId(targetId);
|
||||
if (obj != null)
|
||||
return obj->NameString;
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void OnReceivePacketDetour(PacketDispatcher* thisPtr, uint targetId, nint packet)
|
||||
{
|
||||
var opCode = *(ushort*)(packet + 2);
|
||||
var targetName = GetTargetName(targetId);
|
||||
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneDown, targetId, targetName));
|
||||
this.hookDown.OriginalDisposeSafe(thisPtr, targetId, packet);
|
||||
}
|
||||
|
||||
private byte SendPacketDetour(ZoneClient* thisPtr, nint packet, uint a3, uint a4, byte a5)
|
||||
{
|
||||
var opCode = *(ushort*)packet;
|
||||
this.RecordPacket(new NetworkPacketData(Interlocked.Increment(ref this.nextPacketIndex), DateTime.Now, opCode, NetworkMessageDirection.ZoneUp, 0, string.Empty));
|
||||
return this.hookUp.OriginalDisposeSafe(thisPtr, packet, a3, a4, a5);
|
||||
}
|
||||
|
||||
private void RecordPacket(NetworkPacketData packet)
|
||||
{
|
||||
if (this.filterRecording && !this.IsFiltered(packet.OpCode))
|
||||
return;
|
||||
|
||||
this.packets.Enqueue(packet);
|
||||
|
||||
while (this.packets.Count > this.trackedPackets)
|
||||
{
|
||||
this.packets.TryDequeue(out _);
|
||||
}
|
||||
|
||||
if (this.filterString.Length == 0)
|
||||
{
|
||||
this.trackedOpCodes = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
this.trackedOpCodes = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.trackedOpCodes = null;
|
||||
}
|
||||
}
|
||||
this.autoScrollPending = true;
|
||||
}
|
||||
|
||||
private void DrawNegativeFilterInput()
|
||||
private bool IsFiltered(ushort opcode)
|
||||
{
|
||||
var invalidRegEx = this.negativeFilterString.Length > 0 && this.untrackedOpCodes == null;
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, invalidRegEx);
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Border, 0xFF0000FF, invalidRegEx);
|
||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
|
||||
if (!ImGui.InputTextWithHint("##NegativeFilter"u8, "Regex Filter Against OpCodes..."u8, ref this.negativeFilterString, 1024))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var filterString = this.filterString.Replace(" ", string.Empty);
|
||||
|
||||
if (this.negativeFilterString.Length == 0)
|
||||
if (filterString.Length == 0)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
this.untrackedOpCodes = null;
|
||||
var offset = 0;
|
||||
var included = false;
|
||||
var hasInclude = false;
|
||||
|
||||
while (filterString.Length - offset > 0)
|
||||
{
|
||||
var remaining = filterString[offset..];
|
||||
|
||||
// find the end of the current entry
|
||||
var entryEnd = remaining.IndexOf(',');
|
||||
if (entryEnd == -1)
|
||||
entryEnd = remaining.Length;
|
||||
|
||||
var entry = filterString[offset..(offset + entryEnd)];
|
||||
var dash = entry.IndexOf('-');
|
||||
var isExcluded = entry.StartsWith('!');
|
||||
var startOffset = isExcluded ? 1 : 0;
|
||||
|
||||
var entryMatch = dash == -1
|
||||
? ushort.Parse(entry[startOffset..]) == opcode
|
||||
: ((dash - startOffset == 0 || opcode >= ushort.Parse(entry[startOffset..dash]))
|
||||
&& (entry[(dash + 1)..].Length == 0 || opcode <= ushort.Parse(entry[(dash + 1)..])));
|
||||
|
||||
if (isExcluded)
|
||||
{
|
||||
if (entryMatch)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasInclude = true;
|
||||
included |= entryMatch;
|
||||
}
|
||||
|
||||
if (entryEnd == filterString.Length)
|
||||
break;
|
||||
|
||||
offset += entryEnd + 1;
|
||||
}
|
||||
|
||||
return !hasInclude || included;
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.untrackedOpCodes = new Regex(this.negativeFilterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
this.untrackedOpCodes = null;
|
||||
}
|
||||
Serilog.Log.Error(ex, "Invalid filter string");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
|
||||
{
|
||||
if ((this.trackedOpCodes == null || this.trackedOpCodes.IsMatch(this.OpCodeToString(opCode)))
|
||||
&& (this.untrackedOpCodes == null || !this.untrackedOpCodes.IsMatch(this.OpCodeToString(opCode))))
|
||||
{
|
||||
this.packets.Enqueue(new NetworkPacketData(this, opCode, direction, sourceActorId, targetActorId, dataPtr));
|
||||
while (this.packets.Count > this.trackedPackets)
|
||||
{
|
||||
this.packets.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetSizeFromOpCode(ushort opCode)
|
||||
=> 0;
|
||||
|
||||
/// <remarks> Add known packet-name -> packet struct size associations here to copy the byte data for such packets. </remarks>>
|
||||
private int GetSizeFromName(string name)
|
||||
=> name switch
|
||||
{
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
/// <remarks> The filter should find opCodes by number (decimal and hex) and name, if existing. </remarks>
|
||||
private string OpCodeToString(ushort opCode)
|
||||
=> $"{opCode}\0{opCode:X}";
|
||||
|
||||
#pragma warning disable SA1313
|
||||
private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId)
|
||||
private readonly record struct NetworkPacketData(ulong Index, DateTime Time, ushort OpCode, NetworkMessageDirection Direction, uint TargetEntityId, string TargetName);
|
||||
#pragma warning restore SA1313
|
||||
{
|
||||
public readonly IReadOnlyList<byte> Data = [];
|
||||
|
||||
public NetworkPacketData(NetworkMonitorWidget widget, ushort opCode, NetworkMessageDirection direction, uint sourceActorId, uint targetActorId, nint dataPtr)
|
||||
: this(opCode, direction, sourceActorId, targetActorId)
|
||||
=> this.Data = MemoryHelper.Read<byte>(dataPtr, widget.GetSizeFromOpCode(opCode), false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
using Dalamud.Game.Network;
|
||||
|
||||
namespace Dalamud.Plugin.Services;
|
||||
|
||||
/// <summary>
|
||||
/// This class handles interacting with game network events.
|
||||
/// </summary>
|
||||
[Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)]
|
||||
public interface IGameNetwork : IDalamudService
|
||||
{
|
||||
// TODO(v9): we shouldn't be passing pointers to the actual data here
|
||||
|
||||
/// <summary>
|
||||
/// The delegate type of a network message event.
|
||||
/// </summary>
|
||||
/// <param name="dataPtr">The pointer to the raw data.</param>
|
||||
/// <param name="opCode">The operation ID code.</param>
|
||||
/// <param name="sourceActorId">The source actor ID.</param>
|
||||
/// <param name="targetActorId">The taret actor ID.</param>
|
||||
/// <param name="direction">The direction of the packed.</param>
|
||||
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
|
||||
|
||||
/// <summary>
|
||||
/// Event that is called when a network message is sent/received.
|
||||
/// </summary>
|
||||
public event OnNetworkMessageDelegate NetworkMessage;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue