feat: add some data handling

This commit is contained in:
Anna 2024-07-24 17:48:24 -04:00
parent d4bfbcbdf9
commit 60d766ced4
Signed by: anna
GPG Key ID: D0943384CD9F87D1
4 changed files with 89 additions and 11 deletions

View File

@ -1,5 +1,7 @@
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PartyDamage; namespace PartyDamage;
@ -9,6 +11,8 @@ public class Client : IDisposable {
private Task? MainTask { get; set; } private Task? MainTask { get; set; }
private bool _disposed; private bool _disposed;
internal CombatData? Data { get; private set; }
internal Client() { internal Client() {
this.WebSocket = new ClientWebSocket(); this.WebSocket = new ClientWebSocket();
this.TokenSource = new CancellationTokenSource(); this.TokenSource = new CancellationTokenSource();
@ -16,6 +20,8 @@ public class Client : IDisposable {
while (!this._disposed) { while (!this._disposed) {
try { try {
await this.Main(); await this.Main();
} catch (TaskCanceledException) {
break;
} catch (Exception ex) { } catch (Exception ex) {
Plugin.Log.Error(ex, "Error in WebSocket loop"); Plugin.Log.Error(ex, "Error in WebSocket loop");
await Task.Delay(TimeSpan.FromSeconds(3)); await Task.Delay(TimeSpan.FromSeconds(3));
@ -32,24 +38,30 @@ public class Client : IDisposable {
this._disposed = true; this._disposed = true;
this.TokenSource.Cancel(); this.TokenSource.Cancel();
this.WebSocket.Dispose(); this.WebSocket.Dispose();
this.MainTask?.Dispose();
} }
private async Task Main() { private async Task Main() {
await this.WebSocket.ConnectAsync(new Uri("http://127.0.0.1:10501/"), this.TokenSource.Token); await this.WebSocket.ConnectAsync(new Uri("ws://127.0.0.1:10501/ws"), this.TokenSource.Token);
var msg = "{\"call\": \"subscribe\", \"events\": [\"CombatData\"]}"; const string msg = "{\"call\": \"subscribe\", \"events\": [\"CombatData\"]}";
var msgBytes = Encoding.UTF8.GetBytes(msg); var msgBytes = Encoding.UTF8.GetBytes(msg);
await this.WebSocket.SendAsync(msgBytes, WebSocketMessageType.Text, true, this.TokenSource.Token); await this.WebSocket.SendAsync(msgBytes, WebSocketMessageType.Text, true, this.TokenSource.Token);
var received = new List<byte>(); var buf = new byte[1024];
var bytes = new List<byte>();
while (this.WebSocket.State == WebSocketState.Open) { while (this.WebSocket.State == WebSocketState.Open) {
var bytes = new byte[1024]; var resp = await this.WebSocket.ReceiveAsync(buf, this.TokenSource.Token);
var resp = await this.WebSocket.ReceiveAsync(bytes, this.TokenSource.Token); bytes.AddRange(buf[..resp.Count]);
received.AddRange(bytes[..resp.Count]); if (!resp.EndOfMessage) {
if (resp.EndOfMessage) { continue;
var text = Encoding.UTF8.GetString(bytes.AsSpan()); }
received.Clear();
Plugin.Log.Info(text); var text = Encoding.UTF8.GetString(bytes.ToArray());
bytes.Clear();
var jobj = JObject.Parse(text);
if (jobj["type"]?.Value<string>() == "CombatData") {
var data = JsonConvert.DeserializeObject<CombatData>(text);
this.Data = data;
} }
} }

View File

@ -66,6 +66,10 @@ public class Combatant {
[JsonProperty("encdps")] [JsonProperty("encdps")]
public string EncDps; public string EncDps;
// seems like newtonsoft gets confused if we don't also specify this one
[JsonProperty("ENCDPS")]
public string EncDps2;
public string Hits; public string Hits;
[JsonProperty("crithits")] [JsonProperty("crithits")]

6
PartyDamage.yaml Executable file
View File

@ -0,0 +1,6 @@
name: Party Damage
author: anna
punchline: Show your DPS in the party list
description: |-
Does what it says on the tin.
repo_url: https://git.anna.lgbt/anna/PartyDamage

View File

@ -1,6 +1,9 @@
using Dalamud.IoC; using Dalamud.IoC;
using Dalamud.Plugin; using Dalamud.Plugin;
using Dalamud.Plugin.Services; using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Group;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
namespace PartyDamage; namespace PartyDamage;
@ -8,13 +11,66 @@ public class Plugin : IDalamudPlugin {
[PluginService] [PluginService]
internal static IPluginLog Log { get; private set; } internal static IPluginLog Log { get; private set; }
[PluginService]
private IClientState ClientState { get; init; }
[PluginService]
private IFramework Framework { get; init; }
private Client Client { get; } private Client Client { get; }
public Plugin() { public Plugin() {
this.Client = new Client(); this.Client = new Client();
this.Framework!.Update += this.OnFramework;
} }
public void Dispose() { public void Dispose() {
this.Framework!.Update -= this.OnFramework;
this.Client.Dispose(); this.Client.Dispose();
} }
private unsafe void OnFramework(IFramework framework) {
if (this.Client.Data is not { } data) {
return;
}
if (this.ClientState.LocalPlayer is not { } player) {
return;
}
var list = (AddonPartyList*) AtkStage.Instance()->RaptureAtkUnitManager->GetAddonByName("_PartyList");
var names = new List<string>();
var group = GroupManager.Instance()->GetGroup();
if (group == null) {
Plugin.Log.Info("group null");
}
for (var i = 0; i < group->MemberCount; i++) {
names.Add(group->PartyMembers[i].NameString);
}
var numPlayers = list->PartyMembers.Length;
foreach (var combatant in data.Combatants.Values) {
var idx = combatant.Name == "YOU"
? 0
: names.IndexOf(combatant.Name);
if (idx == -1 || idx >= numPlayers) {
continue;
}
if (!float.TryParse(combatant.EncDps, out var dps)) {
dps = 0;
}
var dpsText = dps switch {
float.PositiveInfinity => "0",
float.NegativeInfinity => "0",
< 1_000 => dps.ToString("N1"),
< 1_000_000 => $"{dps / 1_000:N1}K",
_ => $"{dps / 1_000_000:N1}M",
};
list->PartyMembers[idx].Name->SetText(dpsText);
}
}
} }