refactor: update to 6.0

This commit is contained in:
Anna 2021-12-07 02:23:49 -05:00
parent 48a7636797
commit 91ccf0ae0d
6 changed files with 184 additions and 25 deletions

View File

@ -26,6 +26,8 @@ namespace XIVChatPlugin {
internal Channel<Encodable> Queue { get; } = Channel.CreateUnbounded<Encodable>(); internal Channel<Encodable> Queue { get; } = Channel.CreateUnbounded<Encodable>();
internal uint BacklogSequence { get; set; }
internal void Disconnect() { internal void Disconnect() {
this.Connected = false; this.Connected = false;
this.TokenSource.Cancel(); this.TokenSource.Cancel();

View File

@ -6,11 +6,35 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Logging; using Dalamud.Logging;
using Dalamud.Memory;
using XIVChatCommon.Message; using XIVChatCommon.Message;
using XIVChatCommon.Message.Server;
namespace XIVChatPlugin { namespace XIVChatPlugin {
internal class GameFunctions : IDisposable { internal class GameFunctions : IDisposable {
private static class Signatures {
internal const string GetUiModule = "E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0";
internal const string ProcessChat = "48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9";
internal const string Input = "80 B9 ?? ?? ?? ?? ?? 0F 9C C0";
internal const string InputAfk = "E8 ?? ?? ?? ?? 0F 28 74 24 ?? 0F B6 F0";
internal const string FriendList = "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";
internal const string Format = "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 30 48 8B 6C 24";
internal const string ReceiveChunk = "48 89 5C 24 ?? 56 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B F2";
internal const string GetColour = "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9";
internal const string Channel = "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 85 D2 BB";
internal const string ChannelCommand = "E8 ?? ?? ?? ?? 0F B7 44 37";
internal const string ChannelNameChange = "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8D 4D B0 48 8B F8 E8 ?? ?? ?? ?? 41 8B D6";
internal const string XivStringCtor = "E8 ?? ?? ?? ?? 44 2B F7";
internal const string XivStringDtor = "E8 ?? ?? ?? ?? B0 6E";
internal const string UiModule = "48 8B 0D ?? ?? ?? ?? 48 8D 54 24 ?? 48 83 C1 10 E8";
internal const string ColourHandler = "48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 85 C9 0F 84 ?? ?? ?? ?? 66 85 DB 0F 94 C2 E8 ?? ?? ?? ?? E9";
internal const string ColourLookup = "48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D";
}
private Plugin Plugin { get; } private Plugin Plugin { get; }
private delegate IntPtr GetUiModuleDelegate(IntPtr basePtr); private delegate IntPtr GetUiModuleDelegate(IntPtr basePtr);
@ -31,6 +55,8 @@ namespace XIVChatPlugin {
private delegate byte ChatChannelChangeDelegate(IntPtr a1, uint channel); private delegate byte ChatChannelChangeDelegate(IntPtr a1, uint channel);
private delegate IntPtr ChatChannelChangeNameDelegate(IntPtr a1);
private delegate IntPtr ChannelChangeCommandDelegate(IntPtr a1, int inputChannel, uint linkshellIdx, IntPtr tellTarget, char canChangeChannel); private delegate IntPtr ChannelChangeCommandDelegate(IntPtr a1, int inputChannel, uint linkshellIdx, IntPtr tellTarget, char canChangeChannel);
private delegate IntPtr XivStringCtorDelegate(IntPtr memory); private delegate IntPtr XivStringCtorDelegate(IntPtr memory);
@ -43,6 +69,7 @@ namespace XIVChatPlugin {
private readonly Hook<FormatFriendListNameDelegate>? _formatHook; private readonly Hook<FormatFriendListNameDelegate>? _formatHook;
private readonly Hook<OnReceiveFriendListChunkDelegate>? _receiveChunkHook; private readonly Hook<OnReceiveFriendListChunkDelegate>? _receiveChunkHook;
private readonly Hook<ChatChannelChangeDelegate>? _chatChannelChangeHook; private readonly Hook<ChatChannelChangeDelegate>? _chatChannelChangeHook;
private readonly Hook<ChatChannelChangeNameDelegate>? _chatChannelChangeNameHook;
private readonly GetUiModuleDelegate? _getUiModule; private readonly GetUiModuleDelegate? _getUiModule;
private readonly EasierProcessChatBoxDelegate? _easierProcessChatBox; private readonly EasierProcessChatBoxDelegate? _easierProcessChatBox;
@ -51,6 +78,22 @@ namespace XIVChatPlugin {
private readonly XivStringCtorDelegate? _xivStringCtor; private readonly XivStringCtorDelegate? _xivStringCtor;
private readonly XivStringDtorDelegate? _xivStringDtor; private readonly XivStringDtorDelegate? _xivStringDtor;
public ServerHousingLocation HousingLocation {
get {
var info = this.Plugin.Common.Functions.Housing.Location;
if (info == null) {
return new ServerHousingLocation(null, null, false, null);
}
var ward = info.Ward;
var plot = info.Plot ?? info.Yard ?? info.Apartment;
var wing = (byte?) info.ApartmentWing;
var exterior = info.Yard != null;
return new ServerHousingLocation(ward, plot, exterior, wing);
}
}
[Flags] [Flags]
private enum InputSetters { private enum InputSetters {
None = 0, None = 0,
@ -77,30 +120,31 @@ namespace XIVChatPlugin {
internal GameFunctions(Plugin plugin) { internal GameFunctions(Plugin plugin) {
this.Plugin = plugin; this.Plugin = plugin;
var getUiModulePtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? 48 83 7F ?? 00 48 8B F0"); var getUiModulePtr = this.Plugin.ScanText(Signatures.GetUiModule);
var easierProcessChatBoxPtr = this.Plugin.ScanText("48 89 5C 24 ?? 57 48 83 EC 20 48 8B FA 48 8B D9 45 84 C9"); var easierProcessChatBoxPtr = this.Plugin.ScanText(Signatures.ProcessChat);
var inputPtr = this.Plugin.ScanText("80 B9 ?? ?? ?? ?? ?? 0F 9C C0"); var inputPtr = this.Plugin.ScanText(Signatures.Input);
var inputAfkPtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? 0F 28 74 24 ?? 0F B6 F0"); var inputAfkPtr = this.Plugin.ScanText(Signatures.InputAfk);
var friendListPtr = this.Plugin.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 friendListPtr = this.Plugin.ScanText(Signatures.FriendList);
var formatPtr = this.Plugin.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 41 56 48 83 EC 30 48 8B 6C 24 ??"); var formatPtr = this.Plugin.ScanText(Signatures.Format);
var recvChunkPtr = this.Plugin.ScanText("48 89 5C 24 ?? 56 48 83 EC 20 48 8B 0D ?? ?? ?? ?? 48 8B F2"); var recvChunkPtr = this.Plugin.ScanText(Signatures.ReceiveChunk);
var getColourPtr = this.Plugin.ScanText("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B F2 48 8D B9 ?? ?? ?? ??"); var getColourPtr = this.Plugin.ScanText(Signatures.GetColour);
var channelPtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 85 D2 BB ?? ?? ?? ??"); var channelPtr = this.Plugin.ScanText(Signatures.Channel);
var channelCommandPtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? 0F B7 44 37 ??"); var channelNamePtr = this.Plugin.ScanText(Signatures.ChannelNameChange);
var xivStringCtorPtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? 44 2B F7"); var channelCommandPtr = this.Plugin.ScanText(Signatures.ChannelCommand);
var xivStringDtorPtr = this.Plugin.ScanText("E8 ?? ?? ?? ?? B0 6E"); var xivStringCtorPtr = this.Plugin.ScanText(Signatures.XivStringCtor);
var xivStringDtorPtr = this.Plugin.ScanText(Signatures.XivStringDtor);
this.UiModulePtr = this.Plugin.GetStaticAddressFromSig("48 8B 0D ?? ?? ?? ?? 48 8D 54 24 ?? 48 83 C1 10 E8 ?? ?? ?? ??"); this.UiModulePtr = this.Plugin.GetStaticAddressFromSig(Signatures.UiModule);
if (this.UiModulePtr == IntPtr.Zero) { if (this.UiModulePtr == IntPtr.Zero) {
PluginLog.Warning("Static pointer was null: {0}", nameof(this.UiModulePtr)); PluginLog.Warning("Static pointer was null: {0}", nameof(this.UiModulePtr));
} }
this.ColourHandler = this.Plugin.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8B A8 ?? ?? ?? ?? 48 85 ED 0F 84 ?? ?? ?? ??"); this.ColourHandler = this.Plugin.GetStaticAddressFromSig(Signatures.ColourHandler);
if (this.ColourHandler == IntPtr.Zero) { if (this.ColourHandler == IntPtr.Zero) {
PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourHandler)); PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourHandler));
} }
this.ColourLookup = this.Plugin.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 8B 14 ?? 85 D2 7E ?? 48 8B 0D ?? ?? ?? ?? 48 83 C1 10 E8 ?? ?? ?? ?? 8B 70 ?? 41 8D 4D ??"); this.ColourLookup = this.Plugin.GetStaticAddressFromSig(Signatures.ColourLookup);
if (this.ColourLookup == IntPtr.Zero) { if (this.ColourLookup == IntPtr.Zero) {
PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourLookup)); PluginLog.Warning("Static pointer was null: {0}", nameof(this.ColourLookup));
} }
@ -165,6 +209,12 @@ namespace XIVChatPlugin {
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(channelPtr)); PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(channelPtr));
} }
if (channelNamePtr != IntPtr.Zero) {
this._chatChannelChangeNameHook = new Hook<ChatChannelChangeNameDelegate>(channelNamePtr, this.ChangeChatChannelNameDetour);
} else {
PluginLog.Warning("Pointer was null, disabling hook: {0}", nameof(channelNamePtr));
}
if (inputPtr != IntPtr.Zero) { if (inputPtr != IntPtr.Zero) {
this._isInputHook = new Hook<IsInputDelegate>(inputPtr, this.IsInputDetour); this._isInputHook = new Hook<IsInputDelegate>(inputPtr, this.IsInputDetour);
} else { } else {
@ -181,6 +231,7 @@ namespace XIVChatPlugin {
this._formatHook?.Enable(); this._formatHook?.Enable();
this._receiveChunkHook?.Enable(); this._receiveChunkHook?.Enable();
this._chatChannelChangeHook?.Enable(); this._chatChannelChangeHook?.Enable();
this._chatChannelChangeNameHook?.Enable();
this._isInputHook?.Enable(); this._isInputHook?.Enable();
this._isInputAfkHook?.Enable(); this._isInputAfkHook?.Enable();
@ -283,11 +334,45 @@ namespace XIVChatPlugin {
private byte ChangeChatChannelDetour(IntPtr a1, uint channel) { private byte ChangeChatChannelDetour(IntPtr a1, uint channel) {
this._chatManager = a1; this._chatManager = a1;
// Last ShB patch
// a1 + 0xfd0 is the chat channel byte (including for when clicking on shout) // a1 + 0xfd0 is the chat channel byte (including for when clicking on shout)
this.Plugin.Server.OnChatChannelChange(channel);
return this._chatChannelChangeHook!.Original(a1, channel); return this._chatChannelChangeHook!.Original(a1, channel);
} }
private unsafe IntPtr ChangeChatChannelNameDetour(IntPtr a1) {
// Last ShB patch
// +0x40 = chat channel (byte or uint?)
// channel is 17 (maybe 18?) for tells
// +0x48 = pointer to channel name string
var ret = this._chatChannelChangeNameHook!.Original(a1);
if (a1 == IntPtr.Zero) {
return ret;
}
var channel = *(uint*) (a1 + 0x40);
if (channel is 17 or 18) {
channel = 0;
}
SeString? name = null;
var namePtrPtr = (byte**) (a1 + 0x48);
if (namePtrPtr != null) {
var namePtr = *namePtrPtr;
name = MemoryHelper.ReadSeStringNullTerminated((IntPtr) namePtr);
if (name.Payloads.Count == 0) {
name = null;
}
}
if (name == null) {
return ret;
}
this.Plugin.Server.OnChatChannelChange(channel, name);
return ret;
}
private byte OnRequestFriendList(IntPtr manager) { private byte OnRequestFriendList(IntPtr manager) {
this._friendListManager = manager; this._friendListManager = manager;
// NOTE: if this is being called, hook isn't null // NOTE: if this is being called, hook isn't null
@ -378,6 +463,7 @@ namespace XIVChatPlugin {
this._formatHook?.Dispose(); this._formatHook?.Dispose();
this._receiveChunkHook?.Dispose(); this._receiveChunkHook?.Dispose();
this._chatChannelChangeHook?.Dispose(); this._chatChannelChangeHook?.Dispose();
this._chatChannelChangeNameHook?.Dispose();
this._isInputHook?.Dispose(); this._isInputHook?.Dispose();
this._isInputAfkHook?.Dispose(); this._isInputAfkHook?.Dispose();

View File

@ -10,6 +10,7 @@ using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Objects; using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.Gui; using Dalamud.Game.Gui;
using Dalamud.IoC; using Dalamud.IoC;
using XivCommon;
#if DEBUG #if DEBUG
using System.IO; using System.IO;
#endif #endif
@ -44,6 +45,7 @@ namespace XIVChatPlugin {
[PluginService] [PluginService]
private SigScanner SigScanner { get; init; } = null!; private SigScanner SigScanner { get; init; } = null!;
internal XivCommonBase Common { get; }
internal Configuration Config { get; } internal Configuration Config { get; }
private PluginUi Ui { get; } private PluginUi Ui { get; }
internal Server Server { get; private set; } internal Server Server { get; private set; }
@ -64,6 +66,7 @@ namespace XIVChatPlugin {
} }
public Plugin() { public Plugin() {
this.Common = new XivCommonBase();
this.Events = new InternalEvents(); this.Events = new InternalEvents();
// load libsodium.so from debug location if in debug mode // load libsodium.so from debug location if in debug mode
@ -118,10 +121,13 @@ namespace XIVChatPlugin {
this.ClientState.Logout -= this.Server.OnLogOut; this.ClientState.Logout -= this.Server.OnLogOut;
this.ClientState.TerritoryChanged -= this.Server.OnTerritoryChange; this.ClientState.TerritoryChanged -= this.Server.OnTerritoryChange;
this.CommandManager.RemoveHandler("/xivchat"); this.CommandManager.RemoveHandler("/xivchat");
this.Functions.Dispose();
foreach (var ipc in this.Ipcs) { foreach (var ipc in this.Ipcs) {
ipc.Dispose(); ipc.Dispose();
} }
this.Common.Dispose();
} }
internal void StartRelay() { internal void StartRelay() {

View File

@ -59,11 +59,15 @@ namespace XIVChatPlugin {
private bool _sendPlayerData; private bool _sendPlayerData;
private readonly ConcurrentQueue<Guid> _awaitingPlayerData = new(); private readonly ConcurrentQueue<Guid> _awaitingPlayerData = new();
private readonly ConcurrentQueue<Guid> _awaitingAvailability = new(); private readonly ConcurrentQueue<Guid> _awaitingAvailability = new();
private readonly ConcurrentQueue<Guid> _awaitingHousingLocation = new();
private volatile bool _running; private volatile bool _running;
private bool Running => this._running; private bool Running => this._running;
private InputChannel _currentChannel = InputChannel.Say; private InputChannel _currentChannel = InputChannel.Say;
private SeString? _currentChannelName;
private ServerHousingLocation _lastHousingLocation;
private const int MaxMessageSize = 128_000; private const int MaxMessageSize = 128_000;
@ -73,6 +77,8 @@ namespace XIVChatPlugin {
this.RegenerateKeyPair(); this.RegenerateKeyPair();
} }
this._lastHousingLocation = this._plugin.Functions.HousingLocation;
this._sendWatch.Start(); this._sendWatch.Start();
this._plugin.Functions.ReceiveFriendList += this.OnReceiveFriendList; this._plugin.Functions.ReceiveFriendList += this.OnReceiveFriendList;
@ -251,6 +257,12 @@ namespace XIVChatPlugin {
this._sendPlayerData = false; this._sendPlayerData = false;
} }
var housingLocation = this._plugin.Functions.HousingLocation;
if (!Equals(housingLocation, this._lastHousingLocation)) {
this.BroadcastMessage(housingLocation, ClientPreference.HousingLocationSupport);
this._lastHousingLocation = housingLocation;
}
while (this._awaitingPlayerData.TryDequeue(out var id)) { while (this._awaitingPlayerData.TryDequeue(out var id)) {
if (!this.Clients.TryGetValue(id, out var client)) { if (!this.Clients.TryGetValue(id, out var client)) {
continue; continue;
@ -269,6 +281,14 @@ namespace XIVChatPlugin {
client.Queue.Writer.TryWrite(new Availability(available)); client.Queue.Writer.TryWrite(new Availability(available));
} }
while (this._awaitingHousingLocation.TryDequeue(out var id)) {
if (!this.Clients.TryGetValue(id, out var client) || client.Handshake == null) {
continue;
}
client.Queue.Writer.TryWrite(this._lastHousingLocation);
}
int time; int time;
if (this._toGame.TryPeek(out var peek) && PublicPrefixes.Any(prefix => peek.StartsWith(prefix))) { if (this._toGame.TryPeek(out var peek) && PublicPrefixes.Any(prefix => peek.StartsWith(prefix))) {
time = 1_000; time = 1_000;
@ -358,7 +378,7 @@ namespace XIVChatPlugin {
handshake.Keys.tx, handshake.Keys.tx,
new ServerChannel( new ServerChannel(
channel, channel,
this.LocalisedChannelName(channel) this._currentChannelName?.TextValue ?? this.LocalisedChannelName(channel)
), ),
this._tokenSource.Token this._tokenSource.Token
); );
@ -491,6 +511,11 @@ namespace XIVChatPlugin {
var preferences = ClientPreferences.Decode(payload); var preferences = ClientPreferences.Decode(payload);
client.Preferences = preferences; client.Preferences = preferences;
// immediately queue housing location
if (client.GetPreference(ClientPreference.HousingLocationSupport, false)) {
this._awaitingHousingLocation.Enqueue(id);
}
break; break;
case ClientOperation.Channel: case ClientOperation.Channel:
var channel = ClientChannel.Decode(payload); var channel = ClientChannel.Decode(payload);
@ -574,7 +599,7 @@ namespace XIVChatPlugin {
var responseMessages = new List<ServerMessage>(); var responseMessages = new List<ServerMessage>();
async Task SendBacklog() { async Task SendBacklog() {
var resp = new ServerBacklog(responseMessages.ToArray()); var resp = new ServerBacklog(responseMessages.ToArray(), ++client.BacklogSequence);
try { try {
await client.Queue.Writer.WriteAsync(resp); await client.Queue.Writer.WriteAsync(resp);
} catch (Exception ex) { } catch (Exception ex) {
@ -716,6 +741,14 @@ namespace XIVChatPlugin {
} }
} }
private void BroadcastMessage(Encodable message, ClientPreference preference) {
foreach (var client in this.Clients.Values) {
if (client.GetPreference(preference, false)) {
client.Queue.Writer.TryWrite(message);
}
}
}
private string LocalisedChannelName(InputChannel channel) { private string LocalisedChannelName(InputChannel channel) {
uint rowId = channel switch { uint rowId = channel switch {
InputChannel.Tell => 3, InputChannel.Tell => 3,
@ -749,13 +782,24 @@ namespace XIVChatPlugin {
return this._plugin.DataManager.GetExcelSheet<LogFilter>()!.GetRow(rowId)?.Name ?? string.Empty; return this._plugin.DataManager.GetExcelSheet<LogFilter>()!.GetRow(rowId)?.Name ?? string.Empty;
} }
internal void OnChatChannelChange(uint channel) { internal void OnChatChannelChange(uint channel, SeString name) {
// for now, to avoid changing the protocol further, convert crossworld icon into font icon
for (var i = 0; i < name.Payloads.Count; i++) {
var payload = name.Payloads[i];
if (payload is IconPayload { Icon: BitmapFontIcon.CrossWorld }) {
name.Payloads[i] = new TextPayload("\ue05d");
}
}
var inputChannel = (InputChannel) channel; var inputChannel = (InputChannel) channel;
if (inputChannel == this._currentChannel && name.Encode().SequenceEqual(this._currentChannelName?.Encode() ?? Array.Empty<byte>())) {
return;
}
this._currentChannel = inputChannel; this._currentChannel = inputChannel;
this._currentChannelName = name;
var localisedName = this.LocalisedChannelName(inputChannel); var msg = new ServerChannel(inputChannel, name.TextValue);
var msg = new ServerChannel(inputChannel, localisedName);
this.BroadcastMessage(msg); this.BroadcastMessage(msg);
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices;
namespace XIVChatPlugin { namespace XIVChatPlugin {
internal static class Util { internal static class Util {
@ -70,5 +71,23 @@ namespace XIVChatPlugin {
return bytes.ToArray(); return bytes.ToArray();
} }
internal static IntPtr FollowPointerChain(IntPtr start, IEnumerable<int> offsets) {
if (start == IntPtr.Zero) {
return IntPtr.Zero;
}
// PluginLog.Log($"start: {start.ToInt64():x}");
foreach (var offset in offsets) {
start = Marshal.ReadIntPtr(start + offset);
// PluginLog.Log($" + {offset}: {start.ToInt64():x}");
if (start == IntPtr.Zero) {
return IntPtr.Zero;
}
}
return start;
}
} }
} }

View File

@ -32,11 +32,13 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DalamudPackager" Version="2.1.2"/> <PackageReference Include="DalamudLinter" Version="1.0.3"/>
<PackageReference Include="MessagePack" Version="2.3.75"/> <PackageReference Include="DalamudPackager" Version="2.1.5"/>
<PackageReference Include="MessagePack" Version="2.3.85"/>
<PackageReference Include="Sodium.Core" Version="1.2.3"/> <PackageReference Include="Sodium.Core" Version="1.2.3"/>
<PackageReference Include="System.Threading.Channels" Version="5.0.0"/> <PackageReference Include="System.Threading.Channels" Version="6.0.0"/>
<PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1"/> <PackageReference Include="WebSocketSharp-netstandard" Version="1.0.1"/>
<PackageReference Include="XivCommon" Version="4.0.0-alpha.1"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\XIVChatCommon\XIVChatCommon.csproj"/> <ProjectReference Include="..\XIVChatCommon\XIVChatCommon.csproj"/>