mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Add a simple network monitor to xldata.
This commit is contained in:
parent
a88151de7f
commit
98221471d5
4 changed files with 180 additions and 2 deletions
|
|
@ -154,5 +154,10 @@ internal enum DataKind
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Share.
|
/// Data Share.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
DataShare,
|
Data_Share,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Network Monitor.
|
||||||
|
/// </summary>
|
||||||
|
Network_Monitor,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ internal class DataWindow : Window
|
||||||
new DtrBarWidget(),
|
new DtrBarWidget(),
|
||||||
new UIColorWidget(),
|
new UIColorWidget(),
|
||||||
new DataShareWidget(),
|
new DataShareWidget(),
|
||||||
|
new NetworkMonitorWidget(),
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly Dictionary<DataKind, string> dataKindNames = new();
|
private readonly Dictionary<DataKind, string> dataKindNames = new();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Dalamud.Interface.Internal.Windows.Data;
|
||||||
internal class DataShareWidget : IDataWindowWidget
|
internal class DataShareWidget : IDataWindowWidget
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public DataKind DataKind { get; init; } = DataKind.DataShare;
|
public DataKind DataKind { get; init; } = DataKind.Data_Share;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Ready { get; set; }
|
public bool Ready { get; set; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
using Dalamud.Data;
|
||||||
|
using Dalamud.Game.Network;
|
||||||
|
using Dalamud.Interface.Raii;
|
||||||
|
using Dalamud.Memory;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal.Windows.Data;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Widget to display the current packets.
|
||||||
|
/// </summary>
|
||||||
|
internal class NetworkMonitorWidget : IDataWindowWidget
|
||||||
|
{
|
||||||
|
private readonly record struct NetworkPacketData(ushort OpCode, NetworkMessageDirection Direction, uint SourceActorId, uint TargetActorId)
|
||||||
|
{
|
||||||
|
public readonly IReadOnlyList<byte> Data = Array.Empty<byte>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly ConcurrentQueue<NetworkPacketData> packets = new();
|
||||||
|
private readonly Dictionary<ushort, (string, int)> opCodeDict = new();
|
||||||
|
|
||||||
|
private bool trackNetwork;
|
||||||
|
private int trackedPackets;
|
||||||
|
private Regex? trackedOpCodes;
|
||||||
|
private string filterString = string.Empty;
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public DataKind DataKind { get; init; } = DataKind.Network_Monitor;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Ready { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
this.trackNetwork = false;
|
||||||
|
this.trackedPackets = 20;
|
||||||
|
this.trackedOpCodes = null;
|
||||||
|
this.filterString = string.Empty;
|
||||||
|
this.packets.Clear();
|
||||||
|
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/>
|
||||||
|
public void Draw()
|
||||||
|
{
|
||||||
|
var network = Service<GameNetwork>.Get();
|
||||||
|
if (ImGui.Checkbox("Track Network Packets", ref this.trackNetwork))
|
||||||
|
{
|
||||||
|
if (this.trackNetwork)
|
||||||
|
{
|
||||||
|
network.NetworkMessage += this.OnNetworkMessage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
network.NetworkMessage -= this.OnNetworkMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X / 2);
|
||||||
|
if (ImGui.DragInt("Stored Number of Packets", ref this.trackedPackets, 0.1f, 1, 512))
|
||||||
|
{
|
||||||
|
this.trackedPackets = Math.Clamp(this.trackedPackets, 1, 512);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.DrawFilterInput();
|
||||||
|
|
||||||
|
ImGuiTable.DrawTable(string.Empty, this.packets, this.DrawNetworkPacket, ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg, "Direction", "OpCode", "Source", "Target", "Data");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawNetworkPacket(NetworkPacketData data)
|
||||||
|
{
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(data.Direction.ToString());
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted(this.opCodeDict.TryGetValue(data.OpCode, out var pair) ? pair.Item1 : data.OpCode.ToString());
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted($"0x{data.SourceActorId:X}");
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
ImGui.TextUnformatted($"0x{data.TargetActorId:X}");
|
||||||
|
|
||||||
|
ImGui.TableNextColumn();
|
||||||
|
if (data.Data.Count > 0)
|
||||||
|
{
|
||||||
|
ImGui.TextUnformatted(string.Join(" ", data.Data.Select(b => b.ToString("X2"))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawFilterInput()
|
||||||
|
{
|
||||||
|
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", "Regex Filter OpCodes...", ref this.filterString, 256))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.filterString.Length == 0)
|
||||||
|
{
|
||||||
|
this.trackedOpCodes = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.trackedOpCodes = new Regex(this.filterString, RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
this.trackedOpCodes = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNetworkMessage(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
|
||||||
|
{
|
||||||
|
if (this.trackedOpCodes == null || this.trackedOpCodes.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)
|
||||||
|
=> this.opCodeDict.TryGetValue(opCode, out var pair) ? pair.Item2 : 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)
|
||||||
|
=> this.opCodeDict.TryGetValue(opCode, out var pair) ? $"{pair.Item1}\0{opCode}\0{opCode:X}" : $"{opCode}\0{opCode:X}";
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue