Better chat injection handling

This commit is contained in:
goat 2019-11-11 23:55:32 +09:00
parent e75841deb9
commit ba5fa1f4d0
11 changed files with 207 additions and 72 deletions

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Target"> <PropertyGroup Label="Target">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
<TargetFramework>net471</TargetFramework> <TargetFramework>net471</TargetFramework>
@ -14,10 +14,10 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Feature"> <PropertyGroup Label="Feature">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>2.4.0.0</AssemblyVersion> <AssemblyVersion>2.9.0.0</AssemblyVersion>
<FileVersion>2.4.0.0</FileVersion> <FileVersion>2.9.0.0</FileVersion>
<Description>XIVLauncher addon injection</Description> <Description>XIVLauncher addon injection</Description>
<Version>2.4.0</Version> <Version>2.9.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="EasyHook" Version="2.7.6270" /> <PackageReference Include="EasyHook" Version="2.7.6270" />

View file

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Dalamud.DiscordBot; using Dalamud.DiscordBot;
using Dalamud.Game; using Dalamud.Game;
using Dalamud.Game.Chat; using Dalamud.Game.Chat;
@ -172,6 +174,16 @@ namespace Dalamud {
CommandManager.AddHandler("/xlbotjoin", new CommandInfo(OnBotJoinCommand) { CommandManager.AddHandler("/xlbotjoin", new CommandInfo(OnBotJoinCommand) {
HelpMessage = "Add the XIVLauncher discord bot you set up to your server." HelpMessage = "Add the XIVLauncher discord bot you set up to your server."
}); });
CommandManager.AddHandler("/xlbgmset", new CommandInfo(OnBgmSetCommand)
{
HelpMessage = "Set the Game background music. Usage: /bgmset <BGM ID>"
});
CommandManager.AddHandler("/xlitem", new CommandInfo(OnItemLinkCommand)
{
HelpMessage = "Link an item by name. Usage: /item <Item name>"
});
} }
private void OnUnloadCommand(string command, string arguments) { private void OnUnloadCommand(string command, string arguments) {
@ -198,7 +210,7 @@ namespace Dalamud {
var msg = string.Join(" ", parts.Take(1).ToArray()); var msg = string.Join(" ", parts.Take(1).ToArray());
Framework.Gui.Chat.PrintChat(new XivChatEntry { Framework.Gui.Chat.PrintChat(new XivChatEntry {
Message = msg, MessageBytes = Encoding.UTF8.GetBytes(msg),
Name = "Xiv Launcher", Name = "Xiv Launcher",
Type = chatType Type = chatType
}); });
@ -419,5 +431,63 @@ namespace Dalamud {
Framework.Gui.Chat.Print( Framework.Gui.Chat.Print(
"The XIVLauncher discord bot was not set up correctly or could not connect to discord. Please check the settings and the FAQ."); "The XIVLauncher discord bot was not set up correctly or could not connect to discord. Please check the settings and the FAQ.");
} }
private void OnBgmSetCommand(string command, string arguments)
{
Framework.Network.InjectBgmTest(int.Parse(arguments));
}
private void OnItemLinkCommand(string command, string arguments) {
Task.Run(async () => {
try {
dynamic results = await XivApi.Search(arguments, "Item", 1);
var itemId = (short) results.Results[0].ID;
var itemName = (string)results.Results[0].Name;
var hexData = new byte[] {
0x02, 0x13, 0x06, 0xFE, 0xFF, 0xF3, 0xF3, 0xF3, 0x03, 0x02, 0x27, 0x07, 0x03, 0xF2, 0x3A, 0x2F,
0x02, 0x01, 0x03, 0x02, 0x13, 0x06, 0xFE, 0xFF, 0xFF, 0x7B, 0x1A, 0x03, 0xEE, 0x82, 0xBB, 0x02,
0x13, 0x02, 0xEC, 0x03
};
var endTag = new byte[] {
0x02, 0x27, 0x07, 0xCF, 0x01, 0x01, 0x01, 0xFF, 0x01, 0x03, 0x02, 0x13, 0x02, 0xEC, 0x03
};
BitConverter.GetBytes(itemId).Reverse().ToArray().CopyTo(hexData, 14);
hexData = hexData.Concat(Encoding.UTF8.GetBytes(itemName)).Concat(endTag).ToArray();
Framework.Gui.Chat.PrintChat(new XivChatEntry {
MessageBytes = hexData
});
}
catch {
Framework.Gui.Chat.PrintError("Could not find item.");
}
});
}
public static byte[] StringToByteArray(String value)
{
byte[] bytes = new byte[value.Length * sizeof(char)];
Buffer.BlockCopy(value.ToCharArray(), 0, bytes, 0, bytes.Length);
return bytes;
}
public static String ByteArrayToString(byte[] value)
{
var chars = new char[value.Length / sizeof(char)];
var atValue = 0;
for (var i = 0; i < chars.Length; i++) {
chars[i] = BitConverter.ToChar(value, atValue);
atValue += 2;
}
return new string(chars);
}
} }
} }

View file

@ -14,9 +14,9 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Feature"> <PropertyGroup Label="Feature">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyVersion>2.5.0.0</AssemblyVersion> <AssemblyVersion>2.7.0.0</AssemblyVersion>
<Version>2.5.0</Version> <Version>2.7.0</Version>
<FileVersion>2.5.0.0</FileVersion> <FileVersion>2.7.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" /> <None Include="$(SolutionDir)/Resources/**/*" CopyToOutputDirectory="PreserveNewest" Visible="false" />

View file

@ -231,13 +231,13 @@ namespace Dalamud.DiscordBot {
wasOutgoingTell = true; wasOutgoingTell = true;
} }
var chatTypeConfig = var chatTypeConfigs =
this.config.ChatTypeConfigurations.FirstOrDefault(typeConfig => typeConfig.ChatType == type); this.config.ChatTypeConfigurations.Where(typeConfig => typeConfig.ChatType == type);
if (chatTypeConfig == null) if (!chatTypeConfigs.Any())
return; return;
var channel = await GetChannel(chatTypeConfig.Channel); var channels = chatTypeConfigs.Select(c => GetChannel(c.Channel).GetAwaiter().GetResult());
var senderSplit = sender.Split(new[] {this.worldIcon}, StringSplitOptions.None); var senderSplit = sender.Split(new[] {this.worldIcon}, StringSplitOptions.None);
@ -271,47 +271,52 @@ namespace Dalamud.DiscordBot {
Log.Error(ex, "Could not get XIVAPI character search result."); Log.Error(ex, "Could not get XIVAPI character search result.");
} }
var embedBuilder = new EmbedBuilder { Thread.Sleep(this.config.ChatDelayMs);
Author = new EmbedAuthorBuilder {
IconUrl = avatarUrl, for (var chatTypeIndex = 0; chatTypeIndex < chatTypeConfigs.Count(); chatTypeIndex++) {
Name = wasOutgoingTell var embedBuilder = new EmbedBuilder
{
Author = new EmbedAuthorBuilder
{
IconUrl = avatarUrl,
Name = wasOutgoingTell
? "You" ? "You"
: sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender) : sender + (string.IsNullOrEmpty(world) || string.IsNullOrEmpty(sender)
? "" ? ""
: $" on {world}"), : $" on {world}"),
Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null Url = lodestoneId != 0 ? "https://eu.finalfantasyxiv.com/lodestone/character/" + lodestoneId : null
}, },
Description = message, Description = message,
Timestamp = DateTimeOffset.Now, Timestamp = DateTimeOffset.Now,
Footer = new EmbedFooterBuilder {Text = type.GetDetails().FancyName}, Footer = new EmbedFooterBuilder { Text = type.GetDetails().FancyName },
Color = new Color((uint) (chatTypeConfig.Color & 0xFFFFFF)) Color = new Color((uint)(chatTypeConfigs.ElementAt(chatTypeIndex).Color & 0xFFFFFF))
}; };
if (this.config.CheckForDuplicateMessages) { if (this.config.CheckForDuplicateMessages)
var recentMsg = this.recentMessages.FirstOrDefault( {
msg => msg.Embeds.FirstOrDefault( var recentMsg = this.recentMessages.FirstOrDefault(
embed => embed.Description == embedBuilder.Description && msg => msg.Embeds.FirstOrDefault(
embed.Author.HasValue && embed => embed.Description == embedBuilder.Description &&
embed.Author.Value.Name == embedBuilder.Author.Name && embed.Author.HasValue &&
embed.Timestamp.HasValue && embed.Author.Value.Name == embedBuilder.Author.Name &&
Math.Abs( embed.Timestamp.HasValue &&
(embed.Timestamp.Value.ToUniversalTime().Date - Math.Abs(
embedBuilder (embed.Timestamp.Value.ToUniversalTime().Date -
.Timestamp.Value.ToUniversalTime().Date) embedBuilder
.Milliseconds) < 15000) .Timestamp.Value.ToUniversalTime().Date)
!= null); .Milliseconds) < 15000)
!= null);
if (recentMsg != null) { if (recentMsg != null)
Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description); {
this.recentMessages.Remove(recentMsg); Log.Verbose("Duplicate message: [{0}] {1}", embedBuilder.Author.Name, embedBuilder.Description);
return; this.recentMessages.Remove(recentMsg);
return;
}
} }
await channels.ElementAt(chatTypeIndex).SendMessageAsync(embed: embedBuilder.Build());
} }
Thread.Sleep(this.config.ChatDelayMs);
await channel.SendMessageAsync(embed: embedBuilder.Build());
} }
private async Task<IMessageChannel> GetChannel(ChannelConfiguration channelConfig) { private async Task<IMessageChannel> GetChannel(ChannelConfiguration channelConfig) {
@ -321,7 +326,7 @@ namespace Dalamud.DiscordBot {
} }
private string RemoveAllNonLanguageCharacters(string input) { private string RemoveAllNonLanguageCharacters(string input) {
return Regex.Replace(input, @"[^\p{L} ]", ""); return Regex.Replace(input, @"[^\p{L} ']", "");
} }
public void Dispose() { public void Dispose() {

View file

@ -8,7 +8,7 @@ namespace Dalamud.Game.Chat {
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty; public byte[] MessageBytes { get; set; }
public IntPtr Parameters { get; set; } public IntPtr Parameters { get; set; }
} }

View file

@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -67,7 +69,7 @@ namespace Dalamud.Game {
ref bool isHandled) { ref bool isHandled) {
if (type == XivChatType.Notice && !this.hasSeenLoadingMsg) { if (type == XivChatType.Notice && !this.hasSeenLoadingMsg) {
this.dalamud.Framework.Gui.Chat.Print("XIVLauncher in-game addon loaded."); this.dalamud.Framework.Gui.Chat.Print($"XIVLauncher in-game addon v{Assembly.GetAssembly(typeof(ChatHandlers)).GetName().Version} loaded.");
this.hasSeenLoadingMsg = true; this.hasSeenLoadingMsg = true;
} }

View file

@ -42,15 +42,20 @@ namespace Dalamud.Game.ClientState.Actors {
if (offset == IntPtr.Zero) if (offset == IntPtr.Zero)
return null; return null;
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(offset); try {
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(offset);
//Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"), //Log.Debug("ActorTable[{0}]: {1} - {2} - {3}", index, tblIndex.ToString("X"), offset.ToString("X"),
// actorStruct.ObjectKind.ToString()); // actorStruct.ObjectKind.ToString());
switch (actorStruct.ObjectKind) { switch (actorStruct.ObjectKind)
case ObjectKind.Player: return new PlayerCharacter(actorStruct); {
case ObjectKind.BattleNpc: return new BattleNpc(actorStruct); case ObjectKind.Player: return new PlayerCharacter(actorStruct);
default: return new Actor(actorStruct); case ObjectKind.BattleNpc: return new BattleNpc(actorStruct);
default: return new Actor(actorStruct);
}
} catch (AccessViolationException) {
return null;
} }
} }
} }

View file

@ -90,6 +90,7 @@ namespace Dalamud.Game.Internal {
private bool HandleFrameworkUpdate(IntPtr framework) { private bool HandleFrameworkUpdate(IntPtr framework) {
try { try {
Gui.Chat.UpdateQueue(this); Gui.Chat.UpdateQueue(this);
Network.UpdateQueue(this);
} catch (Exception ex) { } catch (Exception ex) {
Log.Error(ex, "Exception while handling Framework::Update hook."); Log.Error(ex, "Exception while handling Framework::Update hook.");
} }

View file

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game.Chat; using Dalamud.Game.Chat;
using Dalamud.Game.Internal.Libc; using Dalamud.Game.Internal.Libc;
using Dalamud.Hooking; using Dalamud.Hooking;
@ -11,7 +12,7 @@ namespace Dalamud.Game.Internal.Gui {
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate void PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName, private delegate void PrintMessageDelegate(IntPtr manager, XivChatType chatType, IntPtr senderName,
IntPtr message, IntPtr message,
uint senderId, byte isLocal); uint senderId, IntPtr parameter);
public delegate void OnMessageDelegate(XivChatType type, uint senderId, string sender, ref string message, public delegate void OnMessageDelegate(XivChatType type, uint senderId, string sender, ref string message,
ref bool isHandled); ref bool isHandled);
@ -79,12 +80,12 @@ namespace Dalamud.Game.Internal.Gui {
} }
private void HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage, private void HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage,
uint senderid, byte isLocal) { uint senderid, IntPtr parameter) {
try { try {
var senderName = StdString.ReadFromPointer(pSenderName); var senderName = StdString.ReadFromPointer(pSenderName);
var message = StdString.ReadFromPointer(pMessage); var message = StdString.ReadFromPointer(pMessage);
//Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message))}] {message} from {senderName}"); Log.Debug($"HandlePrintMessageDetour {manager} - [{chattype}] [{BitConverter.ToString(Encoding.UTF8.GetBytes(message)).Replace("-", " ")}] {message} from {senderName}");
var originalMessage = string.Copy(message); var originalMessage = string.Copy(message);
@ -93,10 +94,10 @@ namespace Dalamud.Game.Internal.Gui {
OnChatMessage?.Invoke(chattype, senderid, senderName, ref message, ref isHandled); OnChatMessage?.Invoke(chattype, senderid, senderName, ref message, ref isHandled);
var messagePtr = pMessage; var messagePtr = pMessage;
OwnedStdString allocatedString = null; OwnedStdString allocatedString = null;
if (originalMessage != message) { if (originalMessage != message) {
allocatedString = this.dalamud.Framework.Libc.NewString(message); allocatedString = this.dalamud.Framework.Libc.NewString(Encoding.UTF8.GetBytes(message));
Log.Debug( Log.Debug(
$"HandlePrintMessageDetour String modified: {originalMessage}({messagePtr}) -> {message}({allocatedString.Address})"); $"HandlePrintMessageDetour String modified: {originalMessage}({messagePtr}) -> {message}({allocatedString.Address})");
messagePtr = allocatedString.Address; messagePtr = allocatedString.Address;
@ -104,7 +105,7 @@ namespace Dalamud.Game.Internal.Gui {
// Print the original chat if it's handled. // Print the original chat if it's handled.
if (!isHandled) if (!isHandled)
this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, isLocal); this.printMessageHook.Original(manager, chattype, pSenderName, messagePtr, senderid, parameter);
if (this.baseAddress == IntPtr.Zero) if (this.baseAddress == IntPtr.Zero)
this.baseAddress = manager; this.baseAddress = manager;
@ -112,7 +113,7 @@ namespace Dalamud.Game.Internal.Gui {
allocatedString?.Dispose(); allocatedString?.Dispose();
} catch (Exception ex) { } catch (Exception ex) {
Log.Error(ex, "Exception on OnChatMessage hook."); Log.Error(ex, "Exception on OnChatMessage hook.");
this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, isLocal); this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter);
} }
} }
@ -127,13 +128,13 @@ namespace Dalamud.Game.Internal.Gui {
public void Print(string message) { public void Print(string message) {
PrintChat(new XivChatEntry { PrintChat(new XivChatEntry {
Message = message MessageBytes = Encoding.UTF8.GetBytes(message)
}); });
} }
public void PrintError(string message) { public void PrintError(string message) {
PrintChat(new XivChatEntry { PrintChat(new XivChatEntry {
Message = message, MessageBytes = Encoding.UTF8.GetBytes(message),
Type = XivChatType.Urgent Type = XivChatType.Urgent
}); });
} }
@ -146,13 +147,15 @@ namespace Dalamud.Game.Internal.Gui {
var chat = this.chatQueue.Dequeue(); var chat = this.chatQueue.Dequeue();
var sender = chat.Name ?? ""; var sender = chat.Name ?? "";
var message = chat.Message ?? ""; var message = chat.MessageBytes ?? new byte[0];
if (this.baseAddress != IntPtr.Zero) if (this.baseAddress != IntPtr.Zero)
using (var senderVec = framework.Libc.NewString(sender)) using (var senderVec = framework.Libc.NewString(Encoding.UTF8.GetBytes(sender)))
using (var messageVec = framework.Libc.NewString(message)) { using (var messageVec = framework.Libc.NewString(message))
{
Log.Verbose($"String allocated to {messageVec.Address.ToInt64():X}");
this.printMessageHook.Original(this.baseAddress, chat.Type, senderVec.Address, this.printMessageHook.Original(this.baseAddress, chat.Type, senderVec.Address,
messageVec.Address, chat.SenderId, 0); messageVec.Address, chat.SenderId, chat.Parameters);
} }
} }
} }

View file

@ -1,4 +1,4 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Serilog; using Serilog;
@ -6,7 +6,7 @@ namespace Dalamud.Game.Internal.Libc {
public sealed class LibcFunction { public sealed class LibcFunction {
// TODO: prolly callconv is not okay in x86 // TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPUTF8Str)]string content, IntPtr size); private delegate IntPtr StdStringFromCStringDelegate(IntPtr pStdString, [MarshalAs(UnmanagedType.LPArray)]byte[] content, IntPtr size);
// TODO: prolly callconv is not okay in x86 // TODO: prolly callconv is not okay in x86
[UnmanagedFunctionPointer(CallingConvention.ThisCall)] [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
@ -25,7 +25,7 @@ namespace Dalamud.Game.Internal.Libc {
this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer<StdStringDeallocateDelegate>(Address.StdStringDeallocate); this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer<StdStringDeallocateDelegate>(Address.StdStringDeallocate);
} }
public OwnedStdString NewString(string content) { public OwnedStdString NewString(byte[] content) {
Log.Verbose("Allocating"); Log.Verbose("Allocating");
// While 0x70 bytes in the memory should be enough in DX11 version, // While 0x70 bytes in the memory should be enough in DX11 version,
@ -36,7 +36,7 @@ namespace Dalamud.Game.Internal.Libc {
var npos = new IntPtr(0xFFFFFFFF); // assumed to be -1 (0xFFFFFFFF in x86, 0xFFFFFFFF_FFFFFFFF in amd64) var npos = new IntPtr(0xFFFFFFFF); // assumed to be -1 (0xFFFFFFFF in x86, 0xFFFFFFFF_FFFFFFFF in amd64)
var pReallocString = this.stdStringCtorCString(pString, content, npos); var pReallocString = this.stdStringCtorCString(pString, content, npos);
Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString); //Log.Verbose("Prev: {Prev} Now: {Now}", pString, pReallocString);
return new OwnedStdString(pReallocString, DeallocateStdString); return new OwnedStdString(pReallocString, DeallocateStdString);
} }

View file

@ -1,4 +1,6 @@
using System; using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Dalamud.Hooking; using Dalamud.Hooking;
using Serilog; using Serilog;
@ -11,6 +13,7 @@ namespace Dalamud.Game.Internal.Network {
private readonly Hook<ProcessZonePacketDelegate> processZonePacketHook; private readonly Hook<ProcessZonePacketDelegate> processZonePacketHook;
private GameNetworkAddressResolver Address { get; } private GameNetworkAddressResolver Address { get; }
private IntPtr baseAddress;
public delegate void OnZonePacketDelegate(IntPtr dataPtr); public delegate void OnZonePacketDelegate(IntPtr dataPtr);
@ -18,6 +21,8 @@ namespace Dalamud.Game.Internal.Network {
private readonly Dalamud dalamud; private readonly Dalamud dalamud;
private readonly Queue<byte[]> zoneInjectQueue = new Queue<byte[]>();
public GameNetwork(Dalamud dalamud, SigScanner scanner) { public GameNetwork(Dalamud dalamud, SigScanner scanner) {
this.dalamud = dalamud; this.dalamud = dalamud;
Address = new GameNetworkAddressResolver(); Address = new GameNetworkAddressResolver();
@ -40,6 +45,8 @@ namespace Dalamud.Game.Internal.Network {
} }
private void ProcessZonePacketDetour(IntPtr a, IntPtr b, IntPtr dataPtr) { private void ProcessZonePacketDetour(IntPtr a, IntPtr b, IntPtr dataPtr) {
this.baseAddress = a;
// Call events // Call events
this.OnZonePacket?.Invoke(dataPtr); this.OnZonePacket?.Invoke(dataPtr);
@ -60,5 +67,47 @@ namespace Dalamud.Game.Internal.Network {
this.processZonePacketHook.Original(a, b, dataPtr); this.processZonePacketHook.Original(a, b, dataPtr);
} }
} }
private void InjectZoneProtoPacket(byte[] data) {
this.zoneInjectQueue.Enqueue(data);
}
private void InjectActorControl(short cat, int param1) {
var packetData = new byte[] {
0x14, 0x00, 0x64, 0x01, 0x00, 0x00, 0x0E, 0x00, 0x17, 0x7C, 0xC5, 0x5D, 0x00, 0x00, 0x00, 0x00,
0x05, 0x00, 0x48, 0xB2, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x43, 0x7F, 0x00, 0x00
};
BitConverter.GetBytes((short) cat).CopyTo(packetData, 0x10);
BitConverter.GetBytes((UInt32) param1).CopyTo(packetData, 0x14);
InjectZoneProtoPacket(packetData);
}
public void InjectBgmTest(int key) {
InjectActorControl(0xa1, key);
}
/// <summary>
/// Process a chat queue.
/// </summary>
public void UpdateQueue(Framework framework)
{
while (this.zoneInjectQueue.Count > 0)
{
var packetData = this.zoneInjectQueue.Dequeue();
var unmanagedPacketData = Marshal.AllocHGlobal(packetData.Length);
Marshal.Copy(packetData, 0, unmanagedPacketData, packetData.Length);
if (this.baseAddress != IntPtr.Zero) {
this.processZonePacketHook.Original(this.baseAddress, IntPtr.Zero, unmanagedPacketData);
}
Marshal.FreeHGlobal(unmanagedPacketData);
}
}
} }
} }