refactor(plugin): fix some code warnings

This commit is contained in:
Anna 2020-10-31 21:25:52 -04:00
parent 10b5aa5c9b
commit fa97137517
8 changed files with 303 additions and 316 deletions

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
namespace XIVChatPlugin {
public class Configuration : IPluginConfiguration {
private Plugin plugin;
private Plugin? plugin;
public int Version { get; set; } = 1;
public ushort Port { get; set; } = 14777;
@ -20,14 +20,14 @@ namespace XIVChatPlugin {
public bool AcceptNewClients { get; set; } = true;
public Dictionary<Guid, Tuple<string, byte[]>> TrustedKeys { get; set; } = new Dictionary<Guid, Tuple<string, byte[]>>();
public KeyPair KeyPair { get; set; } = null;
public KeyPair? KeyPair { get; set; }
public void Initialise(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
}
public void Save() {
this.plugin.Interface.SavePluginConfig(this);
this.plugin?.Interface.SavePluginConfig(this);
}
}
}

View File

@ -1,12 +1,11 @@
using Dalamud.Plugin;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
namespace XIVChatPlugin {
public static class Extensions {
public static string ToHexString(this byte[] bytes, bool upper = false, string separator = "") {
public static string ToHexString(this IEnumerable<byte> bytes, bool upper = false, string separator = "") {
return string.Join(separator, bytes.Select(b => b.ToString(upper ? "X2" : "x2")));
}

View File

@ -2,6 +2,7 @@
using Lumina.Excel.GeneratedSheets;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@ -11,38 +12,43 @@ namespace XIVChatPlugin {
public class GameFunctions : IDisposable {
private readonly Plugin plugin;
private delegate IntPtr GetUIBaseDelegate();
private delegate IntPtr GetUIModuleDelegate(IntPtr basePtr);
private delegate void EasierProcessChatBoxDelegate(IntPtr uiModule, IntPtr message, IntPtr unused, byte a4);
private delegate byte RequestFriendListDelegate(IntPtr manager);
private delegate int FormatFriendListNameDelegate(long a1, long a2, long a3, int a4, IntPtr data, long a6);
private delegate IntPtr OnReceiveFriendListChunkDelegate(IntPtr a1, IntPtr data);
private delegate IntPtr GetColourInfoDelegate(IntPtr handler, uint lookupResult);
private readonly Hook<RequestFriendListDelegate> friendListHook;
private readonly Hook<FormatFriendListNameDelegate> formatHook;
private readonly Hook<OnReceiveFriendListChunkDelegate> receiveChunkHook;
private readonly GetUIModuleDelegate GetUIModule;
private readonly EasierProcessChatBoxDelegate _EasierProcessChatBox;
private readonly GetColourInfoDelegate GetColourInfo;
private readonly GetUIModuleDelegate getUiModule;
private readonly EasierProcessChatBoxDelegate easierProcessChatBox;
private readonly GetColourInfoDelegate getColourInfo;
private readonly IntPtr uiModulePtr;
private readonly IntPtr colourHandler;
private readonly IntPtr colourLookup;
private IntPtr friendListManager = IntPtr.Zero;
private bool requestingFriendList = false;
public bool RequestingFriendList => this.requestingFriendList;
public bool RequestingFriendList { get; private set; }
private readonly List<Player> friends = new List<Player>();
public delegate void ReceiveFriendListHandler(List<Player> friends);
public event ReceiveFriendListHandler ReceiveFriendList;
public event ReceiveFriendListHandler? ReceiveFriendList;
public GameFunctions(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
var getUIModulePtr = this.plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0");
var getUiModulePtr = this.plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0");
var easierProcessChatBoxPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9");
var friendListPtr = this.plugin.Interface.TargetModuleScanner.ScanText("40 53 48 81 EC 80 0F 00 00 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 48 8B D9 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 0F 84 ?? ?? ?? ?? 44 0F B6 43 ?? 33 C9");
var formatPtr = this.plugin.Interface.TargetModuleScanner.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 30 48 8B 6C 24 ??");
@ -53,9 +59,9 @@ namespace XIVChatPlugin {
this.colourHandler = this.plugin.Interface.TargetModuleScanner.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8B A8 ?? ?? ?? ?? 48 85 ED 0F 84 ?? ?? ?? ??");
this.colourLookup = this.plugin.Interface.TargetModuleScanner.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D ??");
this.GetUIModule = Marshal.GetDelegateForFunctionPointer<GetUIModuleDelegate>(getUIModulePtr);
this._EasierProcessChatBox = Marshal.GetDelegateForFunctionPointer<EasierProcessChatBoxDelegate>(easierProcessChatBoxPtr);
this.GetColourInfo = Marshal.GetDelegateForFunctionPointer<GetColourInfoDelegate>(getColourPtr);
this.getUiModule = Marshal.GetDelegateForFunctionPointer<GetUIModuleDelegate>(getUiModulePtr);
this.easierProcessChatBox = Marshal.GetDelegateForFunctionPointer<EasierProcessChatBoxDelegate>(easierProcessChatBoxPtr);
this.getColourInfo = Marshal.GetDelegateForFunctionPointer<GetColourInfoDelegate>(getColourPtr);
this.friendListHook = new Hook<RequestFriendListDelegate>(friendListPtr, new RequestFriendListDelegate(this.OnRequestFriendList));
this.formatHook = new Hook<FormatFriendListNameDelegate>(formatPtr, new FormatFriendListNameDelegate(this.OnFormatFriendList));
@ -85,7 +91,7 @@ namespace XIVChatPlugin {
}
var lookupResult = (uint)Marshal.ReadInt32(this.colourLookup, (int)parent * 4);
var info = this.GetColourInfo(Marshal.ReadIntPtr(this.colourHandler) + 16, lookupResult);
var info = this.getColourInfo(Marshal.ReadIntPtr(this.colourHandler) + 16, lookupResult);
var rgb = (uint)Marshal.ReadInt32(info, 32) & 0xFFFFFF;
if (rgb == 0) {
@ -96,28 +102,27 @@ namespace XIVChatPlugin {
}
public void ProcessChatBox(string message) {
IntPtr uiModule = this.GetUIModule(Marshal.ReadIntPtr(this.uiModulePtr));
IntPtr uiModule = this.getUiModule(Marshal.ReadIntPtr(this.uiModulePtr));
if (uiModule == IntPtr.Zero) {
throw new ApplicationException("uiModule was null");
}
using (var payload = new ChatPayload(message)) {
IntPtr mem1 = Marshal.AllocHGlobal(400);
Marshal.StructureToPtr(payload, mem1, false);
using var payload = new ChatPayload(message);
IntPtr mem1 = Marshal.AllocHGlobal(400);
Marshal.StructureToPtr(payload, mem1, false);
this._EasierProcessChatBox(uiModule, mem1, IntPtr.Zero, 0);
this.easierProcessChatBox(uiModule, mem1, IntPtr.Zero, 0);
Marshal.FreeHGlobal(mem1);
}
Marshal.FreeHGlobal(mem1);
}
public bool RequestFriendList() {
if (this.friendListManager == IntPtr.Zero || this.friendListHook == null) {
if (this.friendListManager == IntPtr.Zero) {
return false;
}
this.requestingFriendList = true;
this.RequestingFriendList = true;
this.friendListHook.Original(this.friendListManager);
return true;
}
@ -137,7 +142,7 @@ namespace XIVChatPlugin {
var entry = Marshal.PtrToStructure<FriendListEntryRaw>(data);
string jobName = null;
string? jobName = null;
if (entry.job > 0) {
jobName = this.plugin.Interface.Data.GetExcelSheet<ClassJob>().GetRow(entry.job)?.Name;
}
@ -188,32 +193,29 @@ namespace XIVChatPlugin {
goto Return;
}
this.ReceiveFriendList(this.friends);
this.ReceiveFriendList?.Invoke(this.friends);
this.friends.Clear();
this.requestingFriendList = false;
this.RequestingFriendList = false;
Return:
Return:
return ret;
}
public void Dispose() {
this.friendListHook?.Dispose();
this.formatHook?.Dispose();
this.receiveChunkHook?.Dispose();
this.friendListHook.Dispose();
this.formatHook.Dispose();
this.receiveChunkHook.Dispose();
}
}
[StructLayout(LayoutKind.Explicit)]
struct ChatPayload : IDisposable {
[FieldOffset(0)]
readonly IntPtr textPtr;
[FieldOffset(16)]
readonly ulong textLen;
[SuppressMessage("ReSharper", "PrivateFieldCanBeConvertedToLocalVariable")]
readonly struct ChatPayload : IDisposable {
[FieldOffset(0)] readonly IntPtr textPtr;
[FieldOffset(16)] readonly ulong textLen;
[FieldOffset(8)]
readonly ulong unk1;
[FieldOffset(24)]
readonly ulong unk2;
[FieldOffset(8)] readonly ulong unk1;
[FieldOffset(24)] readonly ulong unk2;
internal ChatPayload(string text) {
byte[] stringBytes = Encoding.UTF8.GetBytes(text);
@ -235,34 +237,36 @@ namespace XIVChatPlugin {
[StructLayout(LayoutKind.Sequential)]
struct FriendListEntryRaw {
readonly ulong unk1;
internal ulong flags;
internal readonly ulong flags;
readonly uint unk2;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
readonly byte[] unk3;
internal readonly ushort currentWorldId;
internal readonly ushort homeWorldId;
internal readonly ushort territoryId;
internal readonly byte grandCompany;
internal readonly byte mainLanguage;
internal readonly byte langsEnabled;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
readonly byte[] unk4;
internal readonly byte job;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
readonly byte[] name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
readonly byte[] fc;
private static string HandleString(byte[] bytes) {
private static string? HandleString(IEnumerable<byte> bytes) {
byte[] nonNull = bytes.TakeWhile(b => b != 0).ToArray();
if (nonNull.Length == 0) {
return null;
}
return Encoding.UTF8.GetString(nonNull);
return nonNull.Length == 0 ? null : Encoding.UTF8.GetString(nonNull);
}
public string Name() => HandleString(this.name);
public string FreeCompany() => HandleString(this.fc);
public string? Name() => HandleString(this.name);
public string? FreeCompany() => HandleString(this.fc);
}
}

View File

@ -3,6 +3,7 @@ using Dalamud.Hooking;
using Dalamud.Plugin;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
#if DEBUG
using System.IO;
#endif
@ -16,33 +17,37 @@ namespace XIVChatPlugin {
public string Name => "XIVChat";
// ReSharper disable once MemberCanBePrivate.Global
internal string Location { get; private set; } = Assembly.GetExecutingAssembly().Location;
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "LivePluginLoader")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
private void SetLocation(string path) {
this.Location = path;
}
#pragma warning disable 8618
public DalamudPluginInterface Interface { get; private set; }
public Configuration Config { get; private set; }
public PluginUI Ui { get; private set; }
private PluginUI Ui { get; set; }
public Server Server { get; private set; }
public GameFunctions Functions { get; private set; }
#pragma warning restore 8618
private delegate byte ChatChannelChangeDelegate(IntPtr a1, uint channel);
private Hook<ChatChannelChangeDelegate> chatChannelChangeHook;
private Hook<ChatChannelChangeDelegate>? chatChannelChangeHook;
public void Initialize(DalamudPluginInterface pluginInterface) {
this.Interface = pluginInterface ?? throw new ArgumentNullException(nameof(pluginInterface), "DalamudPluginInterface cannot be null");
// load libsodium.so from debug location if in debug mode
#if DEBUG
string path = Environment.GetEnvironmentVariable("PATH");
string newPath = Path.GetDirectoryName(this.Location);
#if DEBUG
string path = Environment.GetEnvironmentVariable("PATH")!;
string newPath = Path.GetDirectoryName(this.Location)!;
Environment.SetEnvironmentVariable("PATH", $"{path};{newPath}");
#endif
#endif
this.Config = (Configuration)this.Interface.GetPluginConfig() ?? new Configuration();
this.Config = (Configuration?)this.Interface.GetPluginConfig() ?? new Configuration();
this.Config.Initialise(this);
this.Functions = new GameFunctions(this);
@ -74,37 +79,40 @@ namespace XIVChatPlugin {
private byte ChangeChatChannelDetour(IntPtr a1, uint channel) {
// a1 + 0xfd0 is the chat channel byte (including for when clicking on shout)
this.Server.OnChatChannelChange(channel);
return this.chatChannelChangeHook.Original(a1, channel);
return this.chatChannelChangeHook!.Original(a1, channel);
}
private void OnCommand(string command, string args) {
this.Ui.OpenSettings(null, null);
}
[SuppressMessage("ReSharper", "DelegateSubtraction")]
protected virtual void Dispose(bool disposing) {
if (!this.disposedValue) {
if (disposing) {
this.Server.Dispose();
this.Interface.UiBuilder.OnBuildUi -= this.Ui.Draw;
this.Interface.UiBuilder.OnOpenConfigUi -= this.Ui.OpenSettings;
this.Interface.Framework.OnUpdateEvent -= this.Server.OnFrameworkUpdate;
this.Interface.Framework.Gui.Chat.OnChatMessage -= this.Server.OnChat;
this.Interface.ClientState.OnLogin -= this.Server.OnLogIn;
this.Interface.ClientState.OnLogout -= this.Server.OnLogOut;
this.Interface.ClientState.TerritoryChanged -= this.Server.OnTerritoryChange;
this.Interface.CommandManager.RemoveHandler("/xivchat");
this.chatChannelChangeHook?.Dispose();
}
this.disposedValue = true;
if (this.disposedValue) {
return;
}
if (disposing) {
this.Server.Dispose();
this.Interface.UiBuilder.OnBuildUi -= this.Ui.Draw;
this.Interface.UiBuilder.OnOpenConfigUi -= this.Ui.OpenSettings;
this.Interface.Framework.OnUpdateEvent -= this.Server.OnFrameworkUpdate;
this.Interface.Framework.Gui.Chat.OnChatMessage -= this.Server.OnChat;
this.Interface.ClientState.OnLogin -= this.Server.OnLogIn;
this.Interface.ClientState.OnLogout -= this.Server.OnLogOut;
this.Interface.ClientState.TerritoryChanged -= this.Server.OnTerritoryChange;
this.Interface.CommandManager.RemoveHandler("/xivchat");
this.chatChannelChangeHook?.Dispose();
}
this.disposedValue = true;
}
public void Dispose() {
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
this.Dispose(disposing: true);
this.Dispose(true);
GC.SuppressFinalize(this);
}
}

View File

@ -10,8 +10,8 @@ namespace XIVChatPlugin {
public class PluginUI {
private readonly Plugin plugin;
private bool _showSettings = false;
public bool ShowSettings { get => this._showSettings; set => this._showSettings = value; }
private bool showSettings;
private bool ShowSettings { get => this.showSettings; set => this.showSettings = value; }
private readonly Dictionary<Guid, Tuple<Client, Channel<bool>>> pending = new Dictionary<Guid, Tuple<Client, Channel<bool>>>();
private readonly Dictionary<Guid, string> pendingNames = new Dictionary<Guid, string>(0);
@ -21,26 +21,26 @@ namespace XIVChatPlugin {
}
private static class Colours {
public static readonly Vector4 primary = new Vector4(2 / 255f, 204 / 255f, 238 / 255f, 1.0f);
public static readonly Vector4 primaryDark = new Vector4(2 / 255f, 180 / 255f, 211 / 255f, 1.0f);
public static readonly Vector4 background = new Vector4(46 / 255f, 46 / 255f, 46 / 255f, 1.0f);
public static readonly Vector4 text = new Vector4(190 / 255f, 190 / 255f, 190 / 255f, 1.0f);
public static readonly Vector4 button = new Vector4(90 / 255f, 89 / 255f, 90 / 255f, 1.0f);
public static readonly Vector4 buttonActive = new Vector4(123 / 255f, 122 / 255f, 124 / 255f, 1.0f);
public static readonly Vector4 buttonHovered = new Vector4(108 / 255f, 107 / 255f, 109 / 255f, 1.0f);
public static readonly Vector4 Primary = new Vector4(2 / 255f, 204 / 255f, 238 / 255f, 1.0f);
public static readonly Vector4 PrimaryDark = new Vector4(2 / 255f, 180 / 255f, 211 / 255f, 1.0f);
public static readonly Vector4 Background = new Vector4(46 / 255f, 46 / 255f, 46 / 255f, 1.0f);
public static readonly Vector4 Text = new Vector4(190 / 255f, 190 / 255f, 190 / 255f, 1.0f);
public static readonly Vector4 Button = new Vector4(90 / 255f, 89 / 255f, 90 / 255f, 1.0f);
public static readonly Vector4 ButtonActive = new Vector4(123 / 255f, 122 / 255f, 124 / 255f, 1.0f);
public static readonly Vector4 ButtonHovered = new Vector4(108 / 255f, 107 / 255f, 109 / 255f, 1.0f);
public static readonly Vector4 white = new Vector4(1f, 1f, 1f, 1f);
public static readonly Vector4 White = new Vector4(1f, 1f, 1f, 1f);
}
public void Draw() {
ImGui.PushStyleColor(ImGuiCol.TitleBg, Colours.primaryDark);
ImGui.PushStyleColor(ImGuiCol.TitleBgActive, Colours.primary);
ImGui.PushStyleColor(ImGuiCol.TitleBgCollapsed, Colours.primaryDark);
ImGui.PushStyleColor(ImGuiCol.WindowBg, Colours.background);
ImGui.PushStyleColor(ImGuiCol.Text, Colours.text);
ImGui.PushStyleColor(ImGuiCol.Button, Colours.button);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Colours.buttonActive);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Colours.buttonHovered);
ImGui.PushStyleColor(ImGuiCol.TitleBg, Colours.PrimaryDark);
ImGui.PushStyleColor(ImGuiCol.TitleBgActive, Colours.Primary);
ImGui.PushStyleColor(ImGuiCol.TitleBgCollapsed, Colours.PrimaryDark);
ImGui.PushStyleColor(ImGuiCol.WindowBg, Colours.Background);
ImGui.PushStyleColor(ImGuiCol.Text, Colours.Text);
ImGui.PushStyleColor(ImGuiCol.Button, Colours.Button);
ImGui.PushStyleColor(ImGuiCol.ButtonActive, Colours.ButtonActive);
ImGui.PushStyleColor(ImGuiCol.ButtonHovered, Colours.ButtonHovered);
this.DrawInner();
@ -48,14 +48,14 @@ namespace XIVChatPlugin {
}
private static V WithWhiteText<V>(Func<V> func) {
ImGui.PushStyleColor(ImGuiCol.Text, Colours.white);
ImGui.PushStyleColor(ImGuiCol.Text, Colours.White);
var ret = func();
ImGui.PopStyleColor();
return ret;
}
private static void WithWhiteText(Action func) {
ImGui.PushStyleColor(ImGuiCol.Text, Colours.white);
ImGui.PushStyleColor(ImGuiCol.Text, Colours.White);
func();
ImGui.PopStyleColor();
}
@ -65,7 +65,7 @@ namespace XIVChatPlugin {
}
private static bool Begin(string name, ref bool showSettings, ImGuiWindowFlags flags) {
ImGui.PushStyleColor(ImGuiCol.Text, Colours.white);
ImGui.PushStyleColor(ImGuiCol.Text, Colours.White);
var result = ImGui.Begin(name, ref showSettings, flags);
ImGui.PopStyleColor();
return result;
@ -75,13 +75,15 @@ namespace XIVChatPlugin {
private static void HelpMarker(string text) {
ImGui.TextDisabled("(?)");
if (ImGui.IsItemHovered()) {
ImGui.BeginTooltip();
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 20f);
ImGui.TextUnformatted(text);
ImGui.PopTextWrapPos();
ImGui.EndTooltip();
if (!ImGui.IsItemHovered()) {
return;
}
ImGui.BeginTooltip();
ImGui.PushTextWrapPos(ImGui.GetFontSize() * 20f);
ImGui.TextUnformatted(text);
ImGui.PopTextWrapPos();
ImGui.EndTooltip();
}
private void DrawInner() {
@ -93,12 +95,12 @@ namespace XIVChatPlugin {
}
}
if (!this.ShowSettings || !Begin(this.plugin.Name, ref this._showSettings, ImGuiWindowFlags.AlwaysAutoResize)) {
if (!this.ShowSettings || !Begin(this.plugin.Name, ref this.showSettings, ImGuiWindowFlags.AlwaysAutoResize)) {
return;
}
if (WithWhiteText(() => ImGui.CollapsingHeader("Server public key"))) {
string serverPublic = this.plugin.Config.KeyPair.PublicKey.ToHexString(upper: true);
string serverPublic = this.plugin.Config.KeyPair!.PublicKey.ToHexString(upper: true);
ImGui.TextUnformatted(serverPublic);
this.DrawColours(this.plugin.Config.KeyPair.PublicKey, serverPublic);
@ -138,6 +140,7 @@ namespace XIVChatPlugin {
this.plugin.Config.SendBattle = sendBattle;
this.plugin.Config.Save();
}
ImGui.SameLine();
HelpMarker("Changing this setting will not affect messages already in the backlog.");
@ -148,6 +151,7 @@ namespace XIVChatPlugin {
this.plugin.Config.PairingMode = pairingMode;
this.plugin.Config.Save();
}
ImGui.SameLine();
HelpMarker("While in pairing mode, XIVChat Server will listen for information requests from clients broadcast on your local network and respond with information about the server. This will make it easier to add your server to a client, but this should be turned off when not actively adding new devices.");
@ -158,6 +162,7 @@ namespace XIVChatPlugin {
this.plugin.Config.AcceptNewClients = acceptNew;
this.plugin.Config.Save();
}
ImGui.SameLine();
HelpMarker("If this is disabled, XIVChat Server will only allow clients with already-trusted keys to connect.");
}
@ -173,7 +178,7 @@ namespace XIVChatPlugin {
var name = entry.Value.Item1;
var key = entry.Value.Item2;
var hex = key.ToHexString(upper: true);
var hex = key.ToHexString(true);
maxKeyLength = Math.Max(maxKeyLength, ImGui.CalcTextSize(name).X);
@ -184,14 +189,17 @@ namespace XIVChatPlugin {
this.DrawColours(key, hex);
ImGui.EndTooltip();
}
ImGui.NextColumn();
if (WithWhiteText(() => ImGui.Button($"Untrust##{entry.Key}"))) {
this.plugin.Config.TrustedKeys.Remove(entry.Key);
this.plugin.Config.Save();
}
ImGui.NextColumn();
}
ImGui.SetColumnWidth(0, maxKeyLength + ImGui.GetStyle().ItemSpacing.X * 2);
ImGui.Columns(1);
}
@ -216,29 +224,32 @@ namespace XIVChatPlugin {
} catch (ObjectDisposedException) {
continue;
}
string ipAddress;
if (remote is IPEndPoint ip) {
ipAddress = ip.Address.ToString();
} else {
ipAddress = "Unknown";
}
ImGui.TextUnformatted(ipAddress);
ImGui.NextColumn();
var trustedKey = this.plugin.Config.TrustedKeys.Values.FirstOrDefault(entry => entry.Item2.SequenceEqual(client.Value.Handshake.RemotePublicKey));
if (trustedKey != default(Tuple<string, byte[]>)) {
ImGui.TextUnformatted(trustedKey.Item1);
var trustedKey = this.plugin.Config.TrustedKeys.Values.FirstOrDefault(entry => entry.Item2.SequenceEqual(client.Value.Handshake!.RemotePublicKey));
if (trustedKey != null && !trustedKey.Equals(default(Tuple<string, byte[]>))) {
ImGui.TextUnformatted(trustedKey!.Item1);
if (ImGui.IsItemHovered()) {
ImGui.BeginTooltip();
var hex = trustedKey.Item2.ToHexString(upper: true);
var hex = trustedKey.Item2.ToHexString(true);
ImGui.TextUnformatted(hex);
this.DrawColours(trustedKey.Item2, hex);
ImGui.EndTooltip();
}
}
ImGui.NextColumn();
if (WithWhiteText(() => ImGui.Button($"Disconnect##{client.Key}"))) {
@ -247,6 +258,7 @@ namespace XIVChatPlugin {
ImGui.NextColumn();
}
ImGui.Columns(1);
}
}
@ -297,7 +309,7 @@ namespace XIVChatPlugin {
ImGui.Dummy(new Vector2(0, 16 + spacing.Y * 2));
}
public void OpenSettings(object sender, EventArgs args) {
public void OpenSettings(object? sender, EventArgs? args) {
this.ShowSettings = true;
}
@ -307,18 +319,18 @@ namespace XIVChatPlugin {
}
}
private bool DrawPending(Guid id, Client client, Channel<bool> accepted) {
private bool DrawPending(Guid id, Client client, Channel<bool, bool> accepted) {
bool ret = false;
var clientPublic = client.Handshake.RemotePublicKey;
var clientPublic = client.Handshake!.RemotePublicKey;
var clientPublicHex = clientPublic.ToHexString(upper: true);
var serverPublic = this.plugin.Config.KeyPair.PublicKey;
var serverPublic = this.plugin.Config.KeyPair!.PublicKey;
var serverPublicHex = serverPublic.ToHexString(upper: true);
var width = Math.Max(ImGui.CalcTextSize(clientPublicHex).X, ImGui.CalcTextSize(serverPublicHex).X) + (ImGui.GetStyle().WindowPadding.X * 2);
if (!Begin($"Incoming XIVChat connection##{clientPublic}", ImGuiWindowFlags.AlwaysAutoResize)) {
return ret;
return false;
}
ImGui.PushTextWrapPos(width);
@ -361,6 +373,7 @@ namespace XIVChatPlugin {
this.pendingNames.Remove(id);
ret = true;
}
ImGui.SameLine();
if (WithWhiteText(() => ImGui.Button("No"))) {
accepted.Writer.TryWrite(false);

View File

@ -3,19 +3,17 @@ using Dalamud.Game.Chat.SeStringHandling;
using Dalamud.Game.Chat.SeStringHandling.Payloads;
using Dalamud.Game.Internal;
using Dalamud.Plugin;
using Lumina.Data;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using MessagePack;
using Sodium;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
@ -32,20 +30,20 @@ namespace XIVChatPlugin {
public IReadOnlyDictionary<Guid, Client> Clients => this.clients;
public readonly Channel<Tuple<Client, Channel<bool>>> pendingClients = Channel.CreateUnbounded<Tuple<Client, Channel<bool>>>();
private readonly HashSet<Guid> WaitingForFriendList = new HashSet<Guid>();
private readonly HashSet<Guid> waitingForFriendList = new HashSet<Guid>();
private readonly LinkedList<ServerMessage> Backlog = new LinkedList<ServerMessage>();
private readonly LinkedList<ServerMessage> backlog = new LinkedList<ServerMessage>();
private TcpListener listener;
private TcpListener? listener;
private bool sendPlayerData = false;
private bool sendPlayerData;
private volatile bool _running = false;
public bool Running => this._running;
private volatile bool running;
private bool Running => this.running;
private InputChannel currentChannel = InputChannel.Say;
private const int MAX_MESSAGE_SIZE = 128_000;
private const int MaxMessageSize = 128_000;
public Server(Plugin plugin) {
this.plugin = plugin ?? throw new ArgumentNullException(nameof(plugin), "Plugin cannot be null");
@ -65,12 +63,11 @@ namespace XIVChatPlugin {
using var udp = new UdpClient(new IPEndPoint(IPAddress.Any, multicastPort));
var multicastAddr = IPAddress.Parse("224.0.0.147");
var endpoint = new IPEndPoint(multicastAddr, multicastPort);
udp.JoinMulticastGroup(multicastAddr);
string lastPlayerName = null;
string? lastPlayerName = null;
Task<UdpReceiveResult> receiveTask = null;
Task<UdpReceiveResult>? receiveTask = null;
while (this.Running) {
if (!this.plugin.Config.PairingMode) {
@ -89,9 +86,7 @@ namespace XIVChatPlugin {
continue;
}
if (receiveTask == null) {
receiveTask = udp.ReceiveAsync();
}
receiveTask ??= udp.ReceiveAsync();
var result = await Task.WhenAny(
receiveTask,
@ -116,7 +111,7 @@ namespace XIVChatPlugin {
var utf8 = Encoding.UTF8.GetBytes(lastPlayerName);
var portBytes = BitConverter.GetBytes(this.plugin.Config.Port).Reverse().ToArray();
var key = this.plugin.Config.KeyPair.PublicKey;
var key = this.plugin.Config.KeyPair!.PublicKey;
// magic + string length + string + port + key
var payload = new byte[1 + 1 + utf8.Length + portBytes.Length + key.Length]; // assuming names can only be 32 bytes here
payload[0] = 14;
@ -138,19 +133,19 @@ namespace XIVChatPlugin {
Players = friends.ToArray(),
};
foreach (var id in this.WaitingForFriendList) {
foreach (var id in this.waitingForFriendList) {
if (!this.Clients.TryGetValue(id, out var client)) {
continue;
}
try {
await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, msg, client.TokenSource.Token);
await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake!.Keys.tx, msg, client.TokenSource.Token);
} catch (Exception ex) {
PluginLog.LogError($"Could not send message: {ex.Message}");
}
}
this.WaitingForFriendList.Clear();
this.waitingForFriendList.Clear();
}
public void Spawn() {
@ -160,14 +155,15 @@ namespace XIVChatPlugin {
this.listener = new TcpListener(IPAddress.Any, port);
this.listener.Start();
this._running = true;
this.running = true;
PluginLog.Log("Running...");
this.SpawnPairingModeTask();
while (!this.tokenSource.IsCancellationRequested) {
var conn = await this.listener.GetTcpClient(this.tokenSource);
this.SpawnClientTask(conn);
}
this._running = false;
this.running = false;
});
}
@ -219,9 +215,9 @@ namespace XIVChatPlugin {
Chunks = chunks,
};
this.Backlog.AddLast(msg);
while (this.Backlog.Count > this.plugin.Config.BacklogCount) {
this.Backlog.RemoveFirst();
this.backlog.AddLast(msg);
while (this.backlog.Count > this.plugin.Config.BacklogCount) {
this.backlog.RemoveFirst();
}
foreach (var client in this.clients.Values) {
@ -243,13 +239,11 @@ namespace XIVChatPlugin {
this.plugin.Functions.ProcessChatBox(message);
}
private static readonly IReadOnlyList<byte> MAGIC = new byte[] {
14,
20,
67,
private static readonly IReadOnlyList<byte> Magic = new byte[] {
14, 20, 67,
};
private void SpawnClientTask(TcpClient conn) {
private void SpawnClientTask(TcpClient? conn) {
if (conn == null) {
return;
}
@ -258,7 +252,7 @@ namespace XIVChatPlugin {
var stream = conn.GetStream();
// get ready for reading magic bytes
var magic = new byte[MAGIC.Count];
var magic = new byte[Magic.Count];
var read = 0;
// only listen for magic for five seconds
@ -275,7 +269,7 @@ namespace XIVChatPlugin {
}
// ignore this connection if incorrect magic bytes
if (!magic.SequenceEqual(MAGIC)) {
if (!magic.SequenceEqual(Magic)) {
return;
}
@ -293,8 +287,8 @@ namespace XIVChatPlugin {
var accepted = Channel.CreateBounded<bool>(1);
await this.pendingClients.Writer.WriteAsync(Tuple.Create(newClient, accepted));
if (!await accepted.Reader.ReadAsync()) {
await this.pendingClients.Writer.WriteAsync(Tuple.Create(newClient, accepted), this.tokenSource.Token);
if (!await accepted.Reader.ReadAsync(this.tokenSource.Token)) {
return;
}
}
@ -306,7 +300,7 @@ namespace XIVChatPlugin {
// send availability
var available = this.plugin.Interface.ClientState.LocalPlayer != null;
try {
await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, new Availability(available));
await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, new Availability(available), this.tokenSource.Token);
} catch (Exception ex) {
PluginLog.LogError($"Could not send message: {ex.Message}");
}
@ -321,10 +315,15 @@ namespace XIVChatPlugin {
// send current channel
try {
var channel = this.currentChannel;
await SecretMessage.SendSecretMessage(stream, handshake.Keys.tx, new ServerChannel(
channel,
this.LocalisedChannelName(channel)
));
await SecretMessage.SendSecretMessage(
stream,
handshake.Keys.tx,
new ServerChannel(
channel,
this.LocalisedChannelName(channel)
),
this.tokenSource.Token
);
} catch (Exception ex) {
PluginLog.LogError($"Could not send message: {ex.Message}");
}
@ -355,22 +354,25 @@ namespace XIVChatPlugin {
} catch (Exception ex) {
PluginLog.LogError($"Could not send message: {ex.Message}");
}
break;
case ClientOperation.Message:
var clientMessage = ClientMessage.Decode(payload);
foreach (var part in Wrap(clientMessage.Content)) {
this.toGame.Enqueue(part);
}
break;
case ClientOperation.Shutdown:
client.Disconnect();
break;
case ClientOperation.Backlog:
// ReSharper disable once LocalVariableHidesMember
var backlog = ClientBacklog.Decode(payload);
var backlogMessages = new List<ServerMessage>();
var node = this.Backlog.Last;
var node = this.backlog.Last;
while (node != null) {
if (backlogMessages.Count >= backlog.Amount) {
break;
@ -382,7 +384,7 @@ namespace XIVChatPlugin {
backlogMessages.Reverse();
await this.SendBacklogs(backlogMessages.ToArray(), stream, client);
await SendBacklogs(backlogMessages.ToArray(), stream, client);
break;
case ClientOperation.CatchUp:
var catchUp = ClientCatchUp.Decode(payload);
@ -390,13 +392,13 @@ namespace XIVChatPlugin {
var after = catchUp.After.AddMilliseconds(1);
var msgs = this.MessagesAfter(after);
await this.SendBacklogs(msgs, stream, client);
await SendBacklogs(msgs, stream, client);
break;
case ClientOperation.PlayerList:
var playerList = ClientPlayerList.Decode(payload);
if (playerList.Type == PlayerListType.Friend) {
this.WaitingForFriendList.Add(id);
this.waitingForFriendList.Add(id);
if (!this.plugin.Functions.RequestingFriendList && !this.plugin.Functions.RequestFriendList()) {
this.plugin.Interface.Framework.Gui.Chat.PrintError($"[{this.plugin.Name}] Please open your friend list to enable friend list support. You should only need to do this on initial install or after updates.");
@ -423,37 +425,20 @@ namespace XIVChatPlugin {
await listen;
this.clients.TryRemove(id, out var _);
this.clients.TryRemove(id, out _);
PluginLog.Log($"Client thread ended: {id}");
}).ContinueWith(_ => {
try {
conn.Close();
} catch (ObjectDisposedException) { }
}); ;
});
}
private static readonly Regex colorRegex = new Regex(@"<Color\(.+?\)>", RegexOptions.Compiled);
/*
private Dictionary<ChatType, NameFormatting> Formats { get; } = new Dictionary<ChatType, NameFormatting>();
*/
[Sheet("LogKind")]
class LogKind : IExcelRow {
public byte Unknown0;
public byte[] Format;
public bool Unknown2;
public uint RowId { get; set; }
public uint SubRowId { get; set; }
public void PopulateData(RowParser parser, Lumina.Lumina lumina, Language language) {
this.RowId = parser.Row;
this.SubRowId = parser.SubRow;
this.Unknown0 = parser.ReadColumn<byte>(0);
this.Format = parser.ReadColumn<byte[]>(1);
this.Unknown2 = parser.ReadColumn<bool>(2);
}
}
/*
private NameFormatting FormatFor(ChatType type) {
if (this.Formats.TryGetValue(type, out var cached)) {
return cached;
@ -493,15 +478,16 @@ namespace XIVChatPlugin {
//return nameFormatting;
}
*/
private async Task SendBacklogs(ServerMessage[] messages, NetworkStream stream, Client client) {
private static async Task SendBacklogs(IEnumerable<ServerMessage> messages, Stream stream, Client client) {
var size = 5 + SecretMessage.MacSize(); // assume 5 bytes for payload lead-in, although it's likely to be less
var responseMessages = new List<ServerMessage>();
async Task SendBacklog() {
var resp = new ServerBacklog(responseMessages.ToArray());
try {
await SecretMessage.SendSecretMessage(stream, client.Handshake.Keys.tx, resp, client.TokenSource.Token);
await SecretMessage.SendSecretMessage(stream, client.Handshake!.Keys.tx, resp, client.TokenSource.Token);
} catch (Exception ex) {
PluginLog.LogError($"Could not send backlog: {ex.Message}");
}
@ -511,13 +497,13 @@ namespace XIVChatPlugin {
// FIXME: this is very gross
var len = MessagePackSerializer.Serialize(catchUpMessage).Length;
// send message if it would've gone over length
if (size + len >= MAX_MESSAGE_SIZE) {
if (size + len >= MaxMessageSize) {
await SendBacklog();
size = 5 + SecretMessage.MacSize();
responseMessages.Clear();
}
size += len;
responseMessages.Add(catchUpMessage);
}
@ -527,7 +513,7 @@ namespace XIVChatPlugin {
}
}
private static List<Chunk> ToChunks(SeString msg, uint? defaultColour) {
private static IEnumerable<Chunk> ToChunks(SeString msg, uint? defaultColour) {
var chunks = new List<Chunk>();
var italic = false;
@ -557,6 +543,7 @@ namespace XIVChatPlugin {
} else if (foreground.Count > 0) {
foreground.Pop();
}
break;
case PayloadType.UIGlow:
var glowPayload = (UIGlowPayload)payload;
@ -565,16 +552,23 @@ namespace XIVChatPlugin {
} else if (glow.Count > 0) {
glow.Pop();
}
break;
case PayloadType.AutoTranslateText:
chunks.Add(new IconChunk { Index = 54 });
chunks.Add(new IconChunk {
Index = 54,
});
var autoText = ((AutoTranslatePayload)payload).Text;
Append(autoText.Substring(2, autoText.Length - 4));
chunks.Add(new IconChunk { Index = 55 });
chunks.Add(new IconChunk {
Index = 55,
});
break;
case PayloadType.Icon:
var index = ((IconPayload)payload).IconIndex;
chunks.Add(new IconChunk { Index = (byte)index });
chunks.Add(new IconChunk {
Index = (byte)index,
});
break;
// FIXME: use ITextProvider directly once it's exposed
case PayloadType.RawText:
@ -586,26 +580,29 @@ namespace XIVChatPlugin {
foreground.Pop();
glow.Pop();
}
break;
//default:
// var textProviderType = typeof(SeString).Assembly.GetType("Dalamud.Game.Chat.SeStringHandling.ITextProvider");
// var textProp = textProviderType.GetProperty("Text", BindingFlags.NonPublic | BindingFlags.Instance);
// var text = (string)textProp.GetValue(payload);
// append(text);
// break;
//default:
// var textProviderType = typeof(SeString).Assembly.GetType("Dalamud.Game.Chat.SeStringHandling.ITextProvider");
// var textProp = textProviderType.GetProperty("Text", BindingFlags.NonPublic | BindingFlags.Instance);
// var text = (string)textProp.GetValue(payload);
// append(text);
// break;
}
}
return chunks;
}
private ServerMessage[] MessagesAfter(DateTime time) => this.Backlog.Where(msg => msg.Timestamp > time).ToArray();
private IEnumerable<ServerMessage> MessagesAfter(DateTime time) => this.backlog.Where(msg => msg.Timestamp > time).ToArray();
private static string[] Wrap(string input) {
const int LIMIT = 500;
private static IEnumerable<string> Wrap(string input) {
const int limit = 500;
if (input.Length <= LIMIT) {
return new string[] { input };
if (input.Length <= limit) {
return new[] {
input,
};
}
string prefix = string.Empty;
@ -619,14 +616,14 @@ namespace XIVChatPlugin {
var parts = new List<string>();
var builder = new StringBuilder(LIMIT);
var builder = new StringBuilder(limit);
foreach (var word in input.Split(' ')) {
if (word.Length > LIMIT) {
int wordParts = (int)Math.Ceiling((float)word.Length / LIMIT);
if (word.Length > limit) {
int wordParts = (int)Math.Ceiling((float)word.Length / limit);
for (int i = 0; i < wordParts; i++) {
var start = i == 0 ? 0 : (i * LIMIT);
var partLength = LIMIT;
var start = i == 0 ? 0 : (i * limit);
var partLength = limit;
if (prefix.Length != 0) {
start = start == 0 ? 0 : (start - (prefix.Length + 1) * i);
partLength = partLength - prefix.Length - 1;
@ -643,10 +640,11 @@ namespace XIVChatPlugin {
parts.Add(part);
}
continue;
}
if (builder.Length + word.Length > LIMIT) {
if (builder.Length + word.Length > limit) {
parts.Add(builder.ToString().TrimEnd(' '));
builder.Clear();
}
@ -680,87 +678,35 @@ namespace XIVChatPlugin {
}
private string LocalisedChannelName(InputChannel channel) {
uint rowId;
switch (channel) {
case InputChannel.Tell:
rowId = 3;
break;
case InputChannel.Say:
rowId = 1;
break;
case InputChannel.Party:
rowId = 4;
break;
case InputChannel.Alliance:
rowId = 17;
break;
case InputChannel.Yell:
rowId = 16;
break;
case InputChannel.Shout:
rowId = 2;
break;
case InputChannel.FreeCompany:
rowId = 7;
break;
case InputChannel.PvpTeam:
rowId = 19;
break;
case InputChannel.NoviceNetwork:
rowId = 18;
break;
case InputChannel.CrossLinkshell1:
rowId = 20;
break;
case InputChannel.CrossLinkshell2:
rowId = 300;
break;
case InputChannel.CrossLinkshell3:
rowId = 301;
break;
case InputChannel.CrossLinkshell4:
rowId = 302;
break;
case InputChannel.CrossLinkshell5:
rowId = 303;
break;
case InputChannel.CrossLinkshell6:
rowId = 304;
break;
case InputChannel.CrossLinkshell7:
rowId = 305;
break;
case InputChannel.CrossLinkshell8:
rowId = 306;
break;
case InputChannel.Linkshell1:
rowId = 8;
break;
case InputChannel.Linkshell2:
rowId = 9;
break;
case InputChannel.Linkshell3:
rowId = 10;
break;
case InputChannel.Linkshell4:
rowId = 11;
break;
case InputChannel.Linkshell5:
rowId = 12;
break;
case InputChannel.Linkshell6:
rowId = 13;
break;
case InputChannel.Linkshell7:
rowId = 14;
break;
case InputChannel.Linkshell8:
rowId = 15;
break;
default:
rowId = 0;
break;
uint rowId = channel switch {
InputChannel.Tell => 3,
InputChannel.Say => 1,
InputChannel.Party => 4,
InputChannel.Alliance => 17,
InputChannel.Yell => 16,
InputChannel.Shout => 2,
InputChannel.FreeCompany => 7,
InputChannel.PvpTeam => 19,
InputChannel.NoviceNetwork => 18,
InputChannel.CrossLinkshell1 => 20,
InputChannel.CrossLinkshell2 => 300,
InputChannel.CrossLinkshell3 => 301,
InputChannel.CrossLinkshell4 => 302,
InputChannel.CrossLinkshell5 => 303,
InputChannel.CrossLinkshell6 => 304,
InputChannel.CrossLinkshell7 => 305,
InputChannel.CrossLinkshell8 => 306,
InputChannel.Linkshell1 => 8,
InputChannel.Linkshell2 => 9,
InputChannel.Linkshell3 => 10,
InputChannel.Linkshell4 => 11,
InputChannel.Linkshell5 => 12,
InputChannel.Linkshell6 => 13,
InputChannel.Linkshell7 => 14,
InputChannel.Linkshell8 => 15,
_ => 0,
};
return this.plugin.Interface.Data.GetExcelSheet<LogFilter>().GetRow(rowId).Name;
}
@ -778,7 +724,7 @@ namespace XIVChatPlugin {
this.BroadcastMessage(new Availability(available));
}
private PlayerData GeneratePlayerData() {
private PlayerData? GeneratePlayerData() {
var player = this.plugin.Interface.ClientState.LocalPlayer;
if (player == null) {
return null;
@ -786,6 +732,7 @@ namespace XIVChatPlugin {
var homeWorld = player.HomeWorld.GameData.Name;
var currentWorld = player.CurrentWorld.GameData.Name;
// FIXME: NPE if injected late
var territory = this.plugin.Interface.Data.GetExcelSheet<TerritoryType>().GetRow(this.plugin.Interface.ClientState.TerritoryType);
var location = territory.PlaceName.Value.Name;
var name = player.Name;
@ -798,7 +745,8 @@ namespace XIVChatPlugin {
if (playerData == null) {
return;
}
await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, playerData);
await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake!.Keys.tx, playerData);
}
private void BroadcastPlayerData() {
@ -838,18 +786,20 @@ namespace XIVChatPlugin {
await SecretMessage.SendSecretMessage(client.Conn.GetStream(), client.Handshake.Keys.tx, ServerShutdown.Instance);
} catch (Exception) { }
}
// cancel threads for open clients
client.TokenSource.Cancel();
});
}
this.plugin.Functions.ReceiveFriendList -= this.OnReceiveFriendList;
}
}
public class Client {
public bool Connected { get; set; } = false;
public TcpClient Conn { get; private set; }
public HandshakeInfo Handshake { get; set; }
public bool Connected { get; set; }
public TcpClient Conn { get; }
public HandshakeInfo? Handshake { get; set; }
public CancellationTokenSource TokenSource { get; } = new CancellationTokenSource();
public Channel<ServerMessage> Queue { get; } = Channel.CreateUnbounded<ServerMessage>();
@ -865,18 +815,18 @@ namespace XIVChatPlugin {
}
internal static class TcpListenerExt {
public static async Task<TcpClient> GetTcpClient(this TcpListener listener, CancellationTokenSource source) {
public static async Task<TcpClient?> GetTcpClient(this TcpListener listener, CancellationTokenSource source) {
using (source.Token.Register(listener.Stop)) {
try {
var client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
return client;
} catch (ObjectDisposedException ex) {
} catch (ObjectDisposedException) {
// Token was canceled - swallow the exception and return null
if (source.Token.IsCancellationRequested) {
return null;
}
throw ex;
throw;
}
}
}

View File

@ -16,6 +16,7 @@
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -57,29 +58,40 @@
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\Lumina.Generated.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MessagePack, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be, processorArchitecture=MSIL">
<HintPath>..\packages\MessagePack.2.1.194\lib\netstandard2.0\MessagePack.dll</HintPath>
<Reference Include="MessagePack, Version=2.2.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be">
<HintPath>..\packages\MessagePack.2.2.60\lib\netstandard2.0\MessagePack.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="MessagePack.Annotations, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be, processorArchitecture=MSIL">
<HintPath>..\packages\MessagePack.Annotations.2.1.194\lib\netstandard2.0\MessagePack.Annotations.dll</HintPath>
<Reference Include="MessagePack.Annotations, Version=2.2.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be">
<HintPath>..\packages\MessagePack.Annotations.2.2.60\lib\netstandard2.0\MessagePack.Annotations.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.1.1\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="Sodium.Core, Version=1.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sodium.Core.1.2.3\lib\netstandard2.0\Sodium.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Collections.Immutable, Version=1.2.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\packages\System.Collections.Immutable.1.7.1\lib\net461\System.Collections.Immutable.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.7.1\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>

View File

@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILMerge" version="3.0.41" targetFramework="net48" />
<package id="MessagePack" version="2.2.60" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.1.1" targetFramework="net48" />
<package id="System.Memory" version="4.5.4" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
<package id="System.Reflection.Emit" version="4.7.0" targetFramework="net48" />
<package id="System.Reflection.Emit.Lightweight" version="4.7.0" targetFramework="net48" />
<package id="System.Collections.Immutable" version="1.7.1" targetFramework="net48" />
<package id="libsodium" version="1.0.18" targetFramework="net48" />
<package id="MessagePack" version="2.1.194" targetFramework="net48" />
<package id="MessagePack.Annotations" version="2.1.194" targetFramework="net48" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net48" />
<package id="MessagePack.Annotations" version="2.2.60" targetFramework="net48" />
<package id="Microsoft.NETCore.Platforms" version="3.1.3" targetFramework="net48" />
<package id="MSBuild.ILMerge.Task" version="1.1.3" targetFramework="net48" />
<package id="Sodium.Core" version="1.2.3" targetFramework="net48" />
<package id="System.Buffers" version="4.4.0" targetFramework="net48" />
<package id="System.Memory" version="4.5.3" targetFramework="net48" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net48" />
<package id="System.Reflection.Emit" version="4.6.0" targetFramework="net48" />
<package id="System.Reflection.Emit.Lightweight" version="4.6.0" targetFramework="net48" />
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.7.1" targetFramework="net48" />
<package id="System.Threading.Channels" version="4.7.1" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />